Guile Library

(os process)

Overview

This is a library for execution of other programs from Guile. It also allows communication using pipes (or a pseudo terminal device, but that's not currently implemented). This code originates in the (goosh) modules, which itself was part of goonix in one of Guile's past lives.

The following will hold when starting programs:

  1. If the name of the program does not contain a / then the directories listed in the current PATH environment variable are searched to locate the program.

  2. Unlike for the corresponding primitive exec procedures, e.g., execlp, the name of the program can not be set independently of the path to execute: the zeroth and first members of the argument vector are combined into one.

All symbols exported with the prefix os:process: are there in support of macros that use them. They should be ignored by users of this module.

Usage

os:process:pipe-fork-child
[Special Form]
run+
[Special Form]

Evaluate an expression in a new foreground process and wait for its completion. If no connection terms are specified, then all ports except current-input-port, current-output-port and current-error-port will be closed in the new process. The file descriptors underlying these ports will not be changed.

The value returned is the exit status from the new process as returned by the waitpid procedure.

The keywords and connections arguments are optional: see run-concurrently+, which is documented below. The #:foreground keyword is implied.

 (run+ (begin (write (+ 2 2)) (newline) (quit 0)))
 (run+ (tail-call-program "cat" "/etc/passwd"))
run-concurrently+
[Special Form]

Evaluate an expression in a new background process. If no connection terms are specified, then all ports except current-input-port, current-output-port and current-error-port will be closed in the new process. The file descriptors underlying these ports will not be changed.

The value returned in the parent is the pid of the new process.

When the process terminates its exit status can be collected using the waitpid procedure.

Keywords can be specified before the connection list:

#:slave causes the new process to be put into a new session. If current-input-port (after redirections) is a tty it will be assigned as the controlling terminal. This option is used when controlling a process via a pty.

#:no-auto-close prevents the usual closing of ports which occurs by default.

#:foreground makes the new process the foreground job of the controlling terminal, if the current process is using job control. (not currently implemented). The default is to place it into the background

The optional connection list can take several forms:

(port) usually specifies that a given port not be closed. However if #:no-auto-close is present it specifies instead a port which should be closed.

(port 0) specifies that a port be moved to a given file descriptor (e.g., 0) in the new process. The order of the two components is not significant, but one must be a number and the other must evaluate to a port. If the file descriptor is one of the standard set (0, 1, 2) then the corresponding standard port (e.g., current-input-port) will be set to the specified port.

Example:

 (let ((p (open-input-file "/etc/passwd")))
   (run-concurrently+ (tail-call-program "cat") (p 0)))
tail-call-pipeline
[Special Form]

Replace the current process image with a pipeline of connected processes.

The expressions in the pipeline are run in new background processes. The foreground process waits for them all to terminate. The exit status is derived from the status of the process at the tail of the pipeline: its exit status if it terminates normally, otherwise 128 plus the number of the signal that caused it to terminate.

The signal handlers will be reset and file descriptors set up as for tail-call-program. Like tail-call-program it does not close open ports or flush buffers.

Example:

 (tail-call-pipeline ("ls" "/etc") ("grep" "passwd"))
tail-call-pipeline+
[Special Form]

Replace the current process image with a pipeline of connected processes.

Each process is specified by an expression and each pair of processes has a connection list with pairs of file descriptors. E.g., ((1 0) (2 0)) specifies that file descriptors 1 and 2 are to be connected to file descriptor 0. This may also be written as ((1 2 0)).

The expressions in the pipeline are run in new background processes. The foreground process waits for them all to terminate. The exit status is derived from the status of the process at the tail of the pipeline: its exit status if it terminates normally, otherwise 128 plus the number of the signal that caused it to terminate.

The signal handlers will be reset and file descriptors set up as for tail-call-program. Like tail-call-program it does not close open ports or flush buffers.

Example:

 (tail-call-pipeline+ (tail-call-program "ls" "/etc") ((1 0))
                      (tail-call-program "grep" "passwd"))
os:process:new-comm-pipes
[Function]
os:process:pipe-make-commands
[Function]
os:process:pipe-make-redir-commands
[Function]
os:process:setup-redirected-port
[Function]
run
[Function]

Execute prog in a new foreground process and wait for its completion. The value returned is the exit status of the new process as returned by the waitpid procedure.

Example:

 (run "cat" "/etc/passwd")
run-concurrently
[Function]

Start a program running in a new background process. The value returned is the pid of the new process.

When the process terminates its exit status can be collected using the waitpid procedure.

Example:

 (run-concurrently "cat" "/etc/passwd")
run-with-pipe
[Function]

Start prog running in a new background process. The value returned is a pair: the CAR is the pid of the new process and the CDR is either a port or a pair of ports (with the CAR containing the input port and the CDR the output port). The port(s) can be used to read from the standard output of the process and/or write to its standard input, depending on the mode setting. The value of mode should be one of "r", "w" or "r+".

When the process terminates its exit status can be collected using the waitpid procedure.

Example:

 (use-modules (ice-9 rdelim)) ; needed by read-line
 (define catport (cdr (run-with-pipe "r" "cat" "/etc/passwd")))
 (read-line catport)
tail-call-program
[Function]

Replace the current process image by executing prog with the supplied list of arguments, args.

This procedure will reset the signal handlers and attempt to set up file descriptors as follows:

  1. File descriptor 0 is set from (current-input-port).

  2. File descriptor 1 is set from (current-output-port).

  3. File descriptor 2 is set from (current-error-port).

If a port can not be used (e.g., because it's closed or it's a string port) then the file descriptor is opened on the file specified by *null-device* instead.

Note that this procedure does not close any ports or flush output buffers. Successfully executing prog will prevent the normal flushing of buffers that occurs when Guile terminates. Doing otherwise would be incorrect after forking a child process, since the buffers would be flushed in both parent and child.

Examples:

 (tail-call-program "cat" "/etc/passwd")
 (with-input-from-file "/etc/passwd"
  (lambda ()
    (tail-call-program "cat")))