process-1.2.0.0: Process libraries

Copyright | (c) The University of Glasgow 2004-2008
License | BSD-style (see the file libraries/base/LICENSE)
Maintainer | libraries@haskell.org
Stability | experimental
Portability | non-portable (requires concurrency)
Safe Haskell | Trustworthy
Language | Haskell2010

System.Process

Contents

- Running sub-processes
  - Simpler functions for common tasks
  - Related utilities
  - Control-C handling on Unix
- Process completion
- Old deprecated functions

Description

Operations for creating and interacting with sub-processes.

Synopsis

- createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
- shell :: String -> CreateProcess
- proc :: FilePath -> [String] -> CreateProcess
- data CreateProcess = CreateProcess {
  - cmdspec :: CmdSpec
  - cwd :: Maybe FilePath
  - env :: Maybe [(String, String)]
  - std_in :: StdStream
  - std_out :: StdStream
  - std_err :: StdStream
  - close_fds :: Bool
  - create_group :: Bool
  - delegate_ctlc :: Bool
  }
- data CmdSpec
  - = ShellCommand String
  - | RawCommand FilePath [String]
- data StdStream
  - = Inherit
  - | UseHandle Handle
  - | CreatePipe
- data ProcessHandle
- callProcess :: FilePath -> [String] -> IO ()
- callCommand :: String -> IO ()
- spawnProcess :: FilePath -> [String] -> IO ProcessHandle
- spawnCommand :: String -> IO ProcessHandle
- readProcess :: FilePath -> [String] -> String -> IO String
- readProcessWithExitCode :: FilePath -> [String] -> String -> IO (ExitCode, String, String)
- showCommandForUser :: FilePath -> [String] -> String
- waitForProcess :: ProcessHandle -> IO ExitCode
- getProcessExitCode :: ProcessHandle -> IO (Maybe ExitCode)
- terminateProcess :: ProcessHandle -> IO ()
- interruptProcessGroupOf :: ProcessHandle -> IO ()
- runProcess :: FilePath -> [String] -> Maybe FilePath -> Maybe [(String, String)] -> Maybe Handle -> Maybe Handle -> Maybe Handle -> IO ProcessHandle
- runCommand :: String -> IO ProcessHandle
- runInteractiveProcess :: FilePath -> [String] -> Maybe FilePath -> Maybe [(String, String)] -> IO (Handle, Handle, Handle, ProcessHandle)
- runInteractiveCommand :: String -> IO (Handle, Handle, Handle, ProcessHandle)
- system :: String -> IO ExitCode
- rawSystem :: String -> [String] -> IO ExitCode

# Running sub-processes

createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)

This is the most general way to spawn an external process. The process can be a command line to be executed by a shell or a raw command with a list of arguments. The stdin, stdout, and stderr streams of the new process may individually be attached to new pipes, to existing `Handle`s, or just inherited from the parent (the default.)

The details of how to create the process are passed in the `CreateProcess` record. To make it easier to construct a `CreateProcess`, the functions `proc` and `shell` are supplied that fill in the fields with default values which can be overriden as needed.

`createProcess` returns `(mb_stdin_hdl, mb_stdout_hdl, mb_stderr_hdl, ph)`, where

- if ``std_in` == `CreatePipe``, then `mb_stdin_hdl` will be `Just h`, where `h` is the write end of the pipe connected to the child process's `stdin`.
- otherwise, `mb_stdin_hdl == Nothing`

Similarly for `mb_stdout_hdl` and `mb_stderr_hdl`.

For example, to execute a simple `ls` command:

      r <- createProcess (proc "ls" [])

To create a pipe from which to read the output of `ls`:

      (_, Just hout, _, _) <-
          createProcess (proc "ls" []){ std_out = CreatePipe }

To also set the directory in which to run `ls`:

      (_, Just hout, _, _) <-
          createProcess (proc "ls" []){ cwd = Just "\home\bob",
                                        std_out = CreatePipe }

shell :: String -> CreateProcess

Construct a `CreateProcess` record for passing to `createProcess`, representing a command to be passed to the shell.

proc :: FilePath -> [String] -> CreateProcess

Construct a `CreateProcess` record for passing to `createProcess`, representing a raw command with arguments.

The `FilePath` argument names the executable, and is interpreted according to the platform's standard policy for searching for executables. Specifically:

- on Unix systems the execvp(3) semantics is used, where if the executable filename does not contain a slash (`/`) then the `PATH` environment variable is searched for the executable.
- on Windows systems the Win32 `CreateProcess` semantics is used. Briefly: if the filename does not contain a path, then the directory containing the parent executable is searched, followed by the current directory, then some standard locations, and finally the current `PATH`. An `.exe` extension is added if the filename does not already have an extension. For full details see the documentation for the Windows `SearchPath` API.

data CreateProcess

Constructors

CreateProcess |

Fields

cmdspec :: CmdSpec

  Executable & arguments, or shell command

cwd :: Maybe FilePath

  Optional path to the working directory for the new process

env :: Maybe [(String, String)]

  Optional environment (otherwise inherit from the current process)

std_in :: StdStream

  How to determine stdin

std_out :: StdStream

  How to determine stdout

std_err :: StdStream

  How to determine stderr

close_fds :: Bool

  Close all file descriptors except stdin, stdout and stderr in the new process (on Windows, only works if std_in, std_out, and std_err are all Inherit)

create_group :: Bool

  Create a new process group

delegate_ctlc :: Bool

  Delegate control-C handling. Use this for interactive console processes to let them handle control-C themselves (see below for details).

  On Windows this has no effect.

  Since: 1.2.0.0

data CmdSpec

Constructors

ShellCommand String | a command line to execute using the shell
RawCommand FilePath [String] | the filename of an executable with a list of arguments. see `proc` for the precise interpretation of the `FilePath` field.

data StdStream

Constructors

Inherit | Inherit Handle from parent
UseHandle Handle | Use the supplied Handle
CreatePipe | Create a new pipe. The returned `Handle` will use the default encoding and newline translation mode (just like `Handle`s created by `openFile`).

data ProcessHandle

## Simpler functions for common tasks

callProcess :: FilePath -> [String] -> IO ()

Creates a new process to run the specified command with the given arguments, and wait for it to finish. If the command returns a non-zero exit code, an exception is raised.

If an asynchronous exception is thrown to the thread executing `callProcess`. The forked process will be terminated and `callProcess` will wait (block) until the process has been terminated.

Since: 1.2.0.0

callCommand :: String -> IO ()

Creates a new process to run the specified shell command. If the command returns a non-zero exit code, an exception is raised.

If an asynchronous exception is thrown to the thread executing `callCommand`. The forked process will be terminated and `callCommand` will wait (block) until the process has been terminated.

Since: 1.2.0.0

spawnProcess :: FilePath -> [String] -> IO ProcessHandle

Creates a new process to run the specified raw command with the given arguments. It does not wait for the program to finish, but returns the `ProcessHandle`.

Since: 1.2.0.0

spawnCommand :: String -> IO ProcessHandle

Creates a new process to run the specified shell command. It does not wait for the program to finish, but returns the `ProcessHandle`.

Since: 1.2.0.0

readProcess

Arguments

:: FilePath | Filename of the executable (see `proc` for details)
-> [String] | any arguments
-> String | standard input
-> IO String | stdout

`readProcess` forks an external process, reads its standard output strictly, blocking until the process terminates, and returns the output string.

If an asynchronous exception is thrown to the thread executing `readProcess`. The forked process will be terminated and `readProcess` will wait (block) until the process has been terminated.

Output is returned strictly, so this is not suitable for interactive applications.

This function throws an `IOError` if the process `ExitCode` is anything other than `ExitSuccess`.

Users of this function should compile with `-threaded` if they want other Haskell threads to keep running while waiting on the result of readProcess.

     > readProcess "date" [] []
     "Thu Feb  7 10:03:39 PST 2008\n"

The arguments are:

- The command to run, which must be in the $PATH, or an absolute path
- A list of separate command line arguments to the program
- A string to pass on the standard input to the program.

readProcessWithExitCode

Arguments

:: FilePath | Filename of the executable (see `proc` for details)
-> [String] | any arguments
-> String | standard input
-> IO (ExitCode, String, String) | exitcode, stdout, stderr

`readProcessWithExitCode` creates an external process, reads its standard output and standard error strictly, waits until the process terminates, and then returns the `ExitCode` of the process, the standard output, and the standard error.

If an asynchronous exception is thrown to the thread executing `readProcessWithExitCode`. The forked process will be terminated and `readProcessWithExitCode` will wait (block) until the process has been terminated.

`readProcess` and `readProcessWithExitCode` are fairly simple wrappers around `createProcess`. Constructing variants of these functions is quite easy: follow the link to the source code to see how `readProcess` is implemented.

On Unix systems, see `waitForProcess` for the meaning of exit codes when the process died as the result of a signal.

## Related utilities

showCommandForUser :: FilePath -> [String] -> String

Given a program `p` and arguments `args`, `showCommandForUser p args` returns a string suitable for pasting into `/bin/sh` (on Unix systems) or `CMD.EXE` (on Windows).

## Control-C handling on Unix

When running an interactive console process (such as a shell, console-based text editor or ghci), we typically want that process to be allowed to handle Ctl-C keyboard interrupts how it sees fit. For example, while most programs simply quit on a Ctl-C, some handle it specially. To allow this to happen, use the ``delegate_ctlc` = True` option in the `CreateProcess` options.

The gory details:

By default Ctl-C will generate a `SIGINT` signal, causing a `UserInterrupt` exception to be sent to the main Haskell thread of your program, which if not specially handled will terminate the program. Normally, this is exactly what is wanted: an orderly shutdown of the program in response to Ctl-C.

Of course when running another interactive program in the console then we want to let that program handle Ctl-C. Under Unix however, Ctl-C sends `SIGINT` to every process using the console. The standard solution is that while running an interactive program, ignore `SIGINT` in the parent, and let it be handled in the child process. If that process then terminates due to the `SIGINT` signal, then at that point treat it as if we had recieved the `SIGINT` ourselves and begin an orderly shutdown.

This behaviour is implemented by `createProcess` (and `waitForProcess` / `getProcessExitCode`) when the ``delegate_ctlc` = True` option is set. In particular, the `SIGINT` signal will be ignored until `waitForProcess` returns (or `getProcessExitCode` returns a non-Nothing result), so it becomes especially important to use `waitForProcess` for every processes created.

In addition, in `delegate_ctlc` mode, `waitForProcess` and `getProcessExitCode` will throw a `UserInterrupt` exception if the process terminated with ``ExitFailure` (-SIGINT)`. Typically you will not want to catch this exception, but let it propagate, giving a normal orderly shutdown. One detail to be aware of is that the `UserInterrupt` exception is thrown synchronously in the thread that calls `waitForProcess`, whereas normally `SIGINT` causes the exception to be thrown asynchronously to the main thread.

For even more detail on this topic, see "Proper handling of SIGINT/SIGQUIT".

# Process completion

waitForProcess :: ProcessHandle -> IO ExitCode

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`.

(Since: 1.2.0.0) On Unix systems, a negative value ``ExitFailure` -signum` indicates that the child was terminated by signal `signum`. The signal numbers are platform-specific, so to test for a specific signal use the constants provided by System.Posix.Signals in the `unix` package. Note: core dumps are not reported, use System.Posix.Process if you need this detail.

getProcessExitCode :: ProcessHandle -> IO (Maybe ExitCode)

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.

On Unix systems, see `waitForProcess` for the meaning of exit codes when the process died as the result of a signal.

terminateProcess :: ProcessHandle -> IO ()

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 SIGTERM signal. On Windows systems, the Win32 `TerminateProcess` function is called, passing an exit code of 1.

Note: on Windows, if the process was a shell command created by `createProcess` with `shell`, or created by `runCommand` or `runInteractiveCommand`, then `terminateProcess` will only terminate the shell, not the command itself. On Unix systems, both processes are in a process group and will be terminated together.

interruptProcessGroupOf

Arguments

:: ProcessHandle | A process in the process group
-> IO () |

Sends an interrupt signal to the process group of the given process.

On Unix systems, it sends the group the SIGINT signal.

On Windows systems, it generates a CTRL_BREAK_EVENT and will only work for processes created using `createProcess` and setting the `create_group` flag

# Old deprecated functions

These functions pre-date `createProcess` which is much more flexible.

runProcess

Arguments

:: FilePath | Filename of the executable (see `proc` for details)
-> [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` (Nothing => use existing `stdin`)
-> Maybe Handle | Handle to use for `stdout` (Nothing => use existing `stdout`)
-> Maybe Handle | Handle to use for `stderr` (Nothing => use existing `stderr`)
-> IO ProcessHandle |

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.

Note: consider using the more general `createProcess` instead of `runProcess`.

runCommand :: String -> IO ProcessHandle

Runs a command using the shell.

runInteractiveProcess

Arguments

:: FilePath | Filename of the executable (see `proc` for details)
-> [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) |

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)

The `Handle`s are initially in binary mode; if you need them to be in text mode then use `hSetBinaryMode`.

runInteractiveCommand :: String -> IO (Handle, Handle, Handle, ProcessHandle)

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. The `Handle`s are initially in binary mode; if you need them to be in text mode then use `hSetBinaryMode`.

system :: String -> IO ExitCode

Computation `system cmd` returns the exit code produced when the operating system runs the shell command `cmd`.

This computation may fail with one of the following `IOErrorType` exceptions:

`PermissionDenied`
  The process has insufficient privileges to perform the operation.
`ResourceExhausted`
  Insufficient resources are available to perform the operation.
`UnsupportedOperation`
  The implementation does not support system calls.

On Windows, `system` passes the command to the Windows command interpreter (`CMD.EXE` or `COMMAND.COM`), hence Unixy shell tricks will not work.

On Unix systems, see `waitForProcess` for the meaning of exit codes when the process died as the result of a signal.

rawSystem :: String -> [String] -> IO ExitCode

The computation ``rawSystem` cmd args` runs the operating system command `cmd` in such a way that it receives as arguments the `args` strings exactly as given, with no funny escaping or shell meta-syntax expansion. It will therefore behave more portably between operating systems than `system`.

The return codes and possible failures are the same as for `system`.