+++ /dev/null
-{-# 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.
---
------------------------------------------------------------------------------
-
--- 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 ( openFd )
-
--- ----------------------------------------------------------------------------
--- 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
- openFd fd (Just Stream)
- 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