{-# OPTIONS_GHC -cpp -fffi #-} ----------------------------------------------------------------------------- -- | -- 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. -- -- For a simpler, but less powerful, interface, see the "System.Cmd" module. -- ----------------------------------------------------------------------------- -- ToDo: -- * Flag to control whether exiting the parent also kills the child. -- * Windows impl of runProcess should close the Handles. -- * Add system/rawSystem replacements {- NOTES on createPipe: createPipe is no longer exported, because of the following problems: - it wasn't used to implement runInteractiveProcess on Unix, because the file descriptors for the unused ends of the pipe need to be closed in the child process. - on Windows, a special version of createPipe is needed that sets the inheritance flags correctly on the ends of the pipe (see mkAnonPipe below). -} module System.Process ( -- * Running sub-processes ProcessHandle, runCommand, runProcess, runInteractiveCommand, runInteractiveProcess, -- * Process completion waitForProcess, getProcessExitCode, terminateProcess, ) where import Prelude import System.Process.Internals import Foreign import Foreign.C import System.IO ( IOMode(..), Handle, hClose ) import System.Exit ( ExitCode(..) ) import System.Posix.Internals import GHC.IOBase ( FD ) import GHC.Handle ( fdToHandle' ) -- ---------------------------------------------------------------------------- -- runCommand {- | Runs a command using the shell. -} runCommand :: String -> IO ProcessHandle runCommand string = do (cmd,args) <- commandToProcess string #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) runProcessPosix "runCommand" cmd args Nothing Nothing Nothing Nothing Nothing Nothing Nothing #else runProcessWin32 "runCommand" cmd [] Nothing Nothing Nothing Nothing Nothing args #endif -- ---------------------------------------------------------------------------- -- runProcess {- | Runs a raw command, optionally specifying 'Handle's from which to take the @stdin@, @stdout@ and @stderr@ channels for the new process (otherwise these handles are inherited from the current process). Any 'Handle's passed to 'runProcess' are placed immediately in the closed state. -} runProcess :: FilePath -- ^ Filename of the executable -> [String] -- ^ Arguments to pass to the executable -> Maybe FilePath -- ^ Optional path to the working directory -> 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 runProcess cmd args mb_cwd mb_env mb_stdin mb_stdout mb_stderr = do #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) h <- runProcessPosix "runProcess" cmd args mb_cwd mb_env mb_stdin mb_stdout mb_stderr Nothing Nothing #else h <- runProcessWin32 "runProcess" cmd args mb_cwd mb_env mb_stdin mb_stdout mb_stderr "" #endif maybe (return ()) hClose mb_stdin maybe (return ()) hClose mb_stdout maybe (return ()) hClose mb_stderr return h -- ---------------------------------------------------------------------------- -- 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 = do (cmd,args) <- commandToProcess string #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) runInteractiveProcess1 "runInteractiveCommand" cmd args Nothing Nothing #else runInteractiveProcess1 "runInteractiveCommand" cmd [] Nothing Nothing args #endif -- ---------------------------------------------------------------------------- -- 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. For example, to start a process and feed a string to its stdin: > (inp,out,err,pid) <- runInteractiveProcess "..." > forkIO (hPutStr inp str) -} runInteractiveProcess :: FilePath -- ^ Filename of the executable -> [String] -- ^ Arguments to pass to the executable -> Maybe FilePath -- ^ Optional path to the working directory -> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit) -> IO (Handle,Handle,Handle,ProcessHandle) #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__) runInteractiveProcess cmd args mb_cwd mb_env = runInteractiveProcess1 "runInteractiveProcess" cmd args mb_cwd mb_env runInteractiveProcess1 fun cmd args mb_cwd mb_env = do withFilePathException cmd $ alloca $ \ pfdStdInput -> alloca $ \ pfdStdOutput -> alloca $ \ pfdStdError -> maybeWith withCEnvironment mb_env $ \pEnv -> maybeWith withCString mb_cwd $ \pWorkDir -> withMany withCString (cmd:args) $ \cstrs -> withArray0 nullPtr cstrs $ \pargs -> do proc_handle <- throwErrnoIfMinus1 fun (c_runInteractiveProcess pargs pWorkDir pEnv pfdStdInput pfdStdOutput pfdStdError) hndStdInput <- fdToHandle pfdStdInput WriteMode hndStdOutput <- fdToHandle pfdStdOutput ReadMode hndStdError <- fdToHandle pfdStdError ReadMode ph <- mkProcessHandle proc_handle return (hndStdInput, hndStdOutput, hndStdError, ph) foreign import ccall unsafe "runInteractiveProcess" c_runInteractiveProcess :: Ptr CString -> CString -> Ptr CString -> Ptr FD -> Ptr FD -> Ptr FD -> IO PHANDLE #else runInteractiveProcess cmd args mb_cwd mb_env = runInteractiveProcess1 "runInteractiveProcess" cmd args mb_cwd mb_env "" runInteractiveProcess1 fun cmd args workDir env extra_cmdline = withFilePathException cmd $ do let cmdline = translate cmd ++ concat (map ((' ':) . translate) args) ++ (if null extra_cmdline then "" else ' ':extra_cmdline) withCString cmdline $ \pcmdline -> alloca $ \ pfdStdInput -> alloca $ \ pfdStdOutput -> alloca $ \ pfdStdError -> do maybeWith withCEnvironment env $ \pEnv -> do maybeWith withCString workDir $ \pWorkDir -> do proc_handle <- throwErrnoIfMinus1 fun $ c_runInteractiveProcess pcmdline pWorkDir pEnv pfdStdInput pfdStdOutput pfdStdError hndStdInput <- fdToHandle pfdStdInput WriteMode hndStdOutput <- fdToHandle pfdStdOutput ReadMode hndStdError <- fdToHandle pfdStdError ReadMode ph <- mkProcessHandle proc_handle return (hndStdInput, hndStdOutput, hndStdError, ph) foreign import ccall unsafe "runInteractiveProcess" c_runInteractiveProcess :: CString -> CString -> Ptr () -> Ptr FD -> Ptr FD -> Ptr FD -> IO PHANDLE #endif fdToHandle :: Ptr FD -> IOMode -> IO Handle fdToHandle pfd mode = do fd <- peek pfd fdToHandle' fd (Just (Stream,0,0)) False{-not a socket-} ("fd:" ++ show fd) mode True{-binary-} -- ---------------------------------------------------------------------------- -- waitForProcess {- | Waits for the specified process to terminate, and returns its exit code. GHC Note: in order to call @waitForProcess@ without blocking all the other threads in the system, you must compile the program with @-threaded@. -} waitForProcess :: ProcessHandle -> IO ExitCode waitForProcess ph = do p_ <- withProcessHandle ph $ \p_ -> return (p_,p_) case p_ of ClosedHandle e -> return e OpenHandle h -> do -- don't hold the MVar while we call c_waitForProcess... -- (XXX but there's a small race window here during which another -- thread could close the handle or call waitForProcess) code <- throwErrnoIfMinus1 "waitForProcess" (c_waitForProcess h) withProcessHandle ph $ \p_ -> case p_ of ClosedHandle e -> return (p_,e) OpenHandle ph -> do closePHANDLE ph let e = if (code == 0) then ExitSuccess else (ExitFailure (fromIntegral code)) return (ClosedHandle e, e) -- ---------------------------------------------------------------------------- -- terminateProcess -- | Attempts to terminate the specified process. This function should -- not be used under normal circumstances - no guarantees are given regarding -- how cleanly the process is terminated. To check whether the process -- has indeed terminated, use 'getProcessExitCode'. -- -- On Unix systems, 'terminateProcess' sends the process the SIGKILL signal. -- On Windows systems, the Win32 @TerminateProcess@ function is called, passing -- an exit code of 1. terminateProcess :: ProcessHandle -> IO () terminateProcess ph = do withProcessHandle_ ph $ \p_ -> case p_ of ClosedHandle _ -> return p_ OpenHandle h -> do throwErrnoIfMinus1_ "terminateProcess" $ c_terminateProcess h return p_ -- does not close the handle, we might want to try terminating it -- again, or get its exit code. -- ---------------------------------------------------------------------------- -- getProcessExitCode {- | This is a non-blocking version of 'waitForProcess'. If the process is still running, 'Nothing' is returned. If the process has exited, then @'Just' e@ is returned where @e@ is the exit code of the process. Subsequent calls to @getProcessExitStatus@ always return @'Just' 'ExitSuccess'@, regardless of what the original exit code was. -} getProcessExitCode :: ProcessHandle -> IO (Maybe ExitCode) getProcessExitCode ph = do withProcessHandle ph $ \p_ -> case p_ of ClosedHandle e -> return (p_, Just e) OpenHandle h -> alloca $ \pExitCode -> do res <- throwErrnoIfMinus1 "getProcessExitCode" $ c_getProcessExitCode h pExitCode code <- peek pExitCode if res == 0 then return (p_, Nothing) else do closePHANDLE h let e | code == 0 = ExitSuccess | otherwise = ExitFailure (fromIntegral code) return (ClosedHandle e, Just e) -- ---------------------------------------------------------------------------- -- Interface to C bits foreign import ccall unsafe "terminateProcess" c_terminateProcess :: PHANDLE -> IO CInt foreign import ccall unsafe "getProcessExitCode" c_getProcessExitCode :: PHANDLE -> Ptr CInt -> IO CInt foreign import ccall safe "waitForProcess" -- NB. safe - can block c_waitForProcess :: PHANDLE -> IO CInt