{-# OPTIONS -cpp #-} ----------------------------------------------------------------------------- -- | -- Module : System.Process -- Copyright : (c) The University of Glasgow 2004 -- License : BSD-style (see the file libraries/base/LICENSE) -- -- Maintainer : libraries@haskell.org -- Stability : experimental -- Portability : portable -- -- Operations for creating and interacting with sub-processes. -- ----------------------------------------------------------------------------- module System.Process ( -- * Running sub-processes ProcessHandle, runCommand, runProcess, runInteractiveCommand, runInteractiveProcess, commandToProcess, -- * Waiting for process termination waitForProcess, -- * Creating pipes createPipe, ) where #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) import qualified System.Posix as Posix #endif import System.IO ( Handle ) import System.Exit ( ExitCode(..) ) -- ----------------------------------------------------------------------------- -- ProcessHandle type {- | A handle to a process, which can be used to wait for termination of the process using 'waitForProcess'. None of the process-creation functions in this library wait for termination: they all return a 'ProcessHandle' which may be used to wait for the process later. -} #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) newtype ProcessHandle = ProcessHandle Posix.CPid #else newtype ProcessHandle = ProcessHandle CHANDLE{-???-} #endif -- ----------------------------------------------------------------------------- -- runCommand {- | Runs a command using the shell. -} runCommand :: String -> IO ProcessHandle runCommand string = do runProcess cmd args Nothing Nothing Nothing Nothing where (cmd,args) = commandToProcess string -- ----------------------------------------------------------------------------- -- runProcess {- | Runs a raw command, optionally specifying 'Handle's from which to take the @stdin@, @stdout@ and @stderr@ channels for the new process. These 'Handle's may be created by 'createPipe', in order to set up communication channels with the remote process (but see also 'runInteractiveProcess' if you want to do this). -} runProcess :: FilePath -- ^ Filename of the executable -> [String] -- ^ Arguments to pass to the executable -> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit) -> Maybe Handle -- ^ Handle to use for @stdin@ -> Maybe Handle -- ^ Handle to use for @stdout@ -> Maybe Handle -- ^ Handle to use for @stderr@ -> IO ProcessHandle #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) runProcess cmd args env mb_stdin mb_stdout mb_stderr = do pid <- Posix.forkProcess proc return (ProcessHandle pid) where proc = do new_fd 0 mb_stdin new_fd 1 mb_stdout new_fd 2 mb_stderr Posix.executeFile cmd False args env new_fd _ Nothing = return () new_fd fd (Just handle) = do old_fd <- Posix.handleToFd handle Posix.closeFd fd Posix.dupTo old_fd fd return () #else #error ToDo: Win32 version of runProcess #endif -- ----------------------------------------------------------------------------- -- runInteractiveCommand {- | Runs a command using the shell, and returns 'Handle's that may be used to communicate with the process via its @stdin@, @stdout@, and @stderr@ respectively. -} runInteractiveCommand :: String -> IO (Handle,Handle,Handle,ProcessHandle) runInteractiveCommand string = runInteractiveProcess cmd args Nothing where (cmd,args) = commandToProcess string -- ----------------------------------------------------------------------------- -- runInteractiveProcess {- | Runs a raw command, and returns 'Handle's that may be used to communicate with the process via its @stdin@, @stdout@ and @stderr@ respectively. 'runInteractiveProcess' may not be implementable in terms of 'runProcess' and 'createPipe', because of the need to ensure that the ends of the pipes that the child process does not use are closed correctly (otherwise the child process will not be able to receive EOF on stdin when the parent process closes the stdin 'Handle', for example). -} runInteractiveProcess :: FilePath -- ^ Filename of the executable -> [String] -- ^ Arguments to pass to the executable -> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit) -> IO (Handle,Handle,Handle,ProcessHandle) #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) runInteractiveProcess cmd args env = do (r_stdin, w_stdin) <- Posix.createPipe (r_stdout,w_stdout) <- Posix.createPipe (r_stderr,w_stderr) <- Posix.createPipe let -- the child process: proc = do -- It is important that the other ends of the pipes are closed -- in the child process, otherwise these will keep the pipes open. -- eg. if we fail to close w_stdin, then closing the stdin Handle in -- the parent will not result in the child process receiving EOF on -- stdin. Posix.closeFd w_stdin Posix.closeFd r_stdout Posix.closeFd r_stderr Posix.closeFd 0 Posix.dupTo r_stdin 0 Posix.closeFd 1 Posix.dupTo w_stdout 1 Posix.closeFd 2 Posix.dupTo w_stderr 2 Posix.executeFile cmd False args env -- in pid <- Posix.forkProcess proc -- Close the ends of the pipes that the parent process doens't need. Posix.closeFd r_stdin Posix.closeFd w_stdout Posix.closeFd w_stderr h0 <- Posix.fdToHandle w_stdin h1 <- Posix.fdToHandle r_stdout h2 <- Posix.fdToHandle r_stderr return (h0, h1, h2, ProcessHandle pid) #else #error ToDo: Win32 waitForProcess #endif -- ----------------------------------------------------------------------------- -- waitForProcess {- | Waits for the specified process to terminate, and returns its exit code. -} waitForProcess :: ProcessHandle -> IO ExitCode #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) waitForProcess (ProcessHandle pid) = do mb_stat <- Posix.getProcessStatus True{-block-} False{-stopped-} pid case mb_stat of Nothing -> error "waitForProcess: internal error" Just (Posix.Exited code) -> return code Just (Posix.Terminated sig) -> return (ExitFailure (fromIntegral sig)) #else #error ToDo: Win32 waitForProcess #endif -- ----------------------------------------------------------------------------- -- commandToProcess {- | Turns a shell command into a raw command. Usually this involves wrapping it in an invocation of the shell. -} commandToProcess :: String -> (FilePath,[String]) #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) commandToProcess string = ("/bin/sh", ["-c", string]) #else #error ToDo: Win32 commandToProcess #endif -- ----------------------------------------------------------------------------- -- createPipe {- | Creates an anonymous /pipe/ and returns a pair of handles, the first for reading and the second for writing. Both pipe ends can be inherited by a child process. -} createPipe :: IO (Handle,Handle) #if !defined(mingw32_TARGET_OS) && !defined(__MINGW32__) createPipe = do (read,write) <- Posix.createPipe rh <- Posix.fdToHandle read wh <- Posix.fdToHandle write return (rh,wh) #else #error ToDo: Win32 createPipe #endif {- - To change working directory: use runCommand with "cd ; ..." - To send a string to stdin: (in,out,err,pid) <- runInteractiveProcess "..." forkIO (hPutStr in str) -}