diff options
Diffstat (limited to 'src/Script')
-rw-r--r-- | src/Script/Shell.hs | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/Script/Shell.hs b/src/Script/Shell.hs new file mode 100644 index 0000000..60ec929 --- /dev/null +++ b/src/Script/Shell.hs @@ -0,0 +1,89 @@ +module Script.Shell ( + ShellStatement(..), + ShellScript(..), + withShellProcess, +) where + +import Control.Concurrent +import Control.Concurrent.STM +import Control.Monad +import Control.Monad.Except +import Control.Monad.IO.Class +import Control.Monad.Reader + +import Data.Text (Text) +import Data.Text qualified as T +import Data.Text.IO qualified as T + +import System.Exit +import System.IO +import System.Process hiding (ShellCommand) + +import Network +import Output +import Process +import Run.Monad + + +data ShellStatement = ShellStatement + { shellCommand :: Text + , shellArguments :: [ Text ] + } + +newtype ShellScript = ShellScript [ ShellStatement ] + + +executeScript :: Node -> ProcName -> Handle -> Handle -> Handle -> ShellScript -> TestRun () +executeScript node pname pstdin pstdout pstderr (ShellScript statements) = do + forM_ statements $ \ShellStatement {..} -> case shellCommand of + "echo" -> liftIO $ do + T.hPutStrLn pstdout $ T.intercalate " " shellArguments + hFlush pstdout + cmd -> do + (_, _, _, phandle) <- liftIO $ createProcess_ "shell" + (proc (T.unpack cmd) (map T.unpack shellArguments)) + { std_in = UseHandle pstdin + , std_out = UseHandle pstdout + , std_err = UseHandle pstderr + , cwd = Just (nodeDir node) + , env = Just [] + } + liftIO (waitForProcess phandle) >>= \case + ExitSuccess -> return () + ExitFailure code -> do + outLine OutputChildFail (Just $ textProcName pname) $ T.pack $ "exit code: " ++ show code + throwError Failed + +spawnShell :: Node -> ProcName -> ShellScript -> TestRun Process +spawnShell procNode procName script = do + procOutput <- liftIO $ newTVarIO [] + statusVar <- liftIO $ newEmptyMVar + ( pstdin, procStdin ) <- liftIO $ createPipe + ( hout, pstdout ) <- liftIO $ createPipe + ( herr, pstderr ) <- liftIO $ createPipe + procHandle <- fmap (Right . (, statusVar)) $ forkTest $ do + executeScript procNode procName pstdin pstdout pstderr script + liftIO $ putMVar statusVar ExitSuccess + + let procKillWith = Nothing + let process = Process {..} + + void $ forkTest $ lineReadingLoop process hout $ \line -> do + outProc OutputChildStdout process line + liftIO $ atomically $ modifyTVar procOutput (++ [ line ]) + void $ forkTest $ lineReadingLoop process herr $ \line -> do + outProc OutputChildStderr process line + + return process + +withShellProcess :: Node -> ProcName -> ShellScript -> (Process -> TestRun a) -> TestRun a +withShellProcess node pname script inner = do + procVar <- asks $ teProcesses . fst + + process <- spawnShell node pname script + liftIO $ modifyMVar_ procVar $ return . (process:) + + inner process `finally` do + ps <- liftIO $ takeMVar procVar + closeProcess process `finally` do + liftIO $ putMVar procVar $ filter (/=process) ps |