(* Copyright © 1991, 1992 Digital Equipment Corporation. *) (* Distributed only by permission. *) (* Created on Tue Nov 26 18:30:56 PST 1991 by meehan *) (* Last modified on Wed Jul 29 18:49:34 PDT 1992 by muller *) (* modified on Tue Mar 3 11:20:22 PST 1992 by meehan *) INTERFACE UProcess; (* An interface for creating Unix processes and communicating with them via Unix pipes. *) IMPORT Rd, Thread, Usignal, Wr; TYPE Handle = MUTEX OBJECT pid : CARDINAL; (* the process ID (pid) *) stdin : Wr.T; stdout, stderr: Rd.T; stdin_t, stdout_t, stderr_t: Thread.T; condition: Thread.Condition; (* Broadcast when child dies. *) childDied: BOOLEAN; (* Guarded by the mutex *) status: WaitStatus; (* Valid only when childDied = TRUE *) END; WaitStatus = RECORD exitCode : [0 .. 255]; terminationSignal: [0 .. 127]; dumpedCore : BOOLEAN END; EXCEPTION Error(TEXT); (* Unix errors are reported this way. *) PROCEDURE Fork ( program : TEXT; READONLY args : ARRAY OF TEXT; mergeOutput := FALSE; ignoreOutput := FALSE): Handle RAISES {Error, Rd.Failure, Wr.Failure}; (* This forks a Unix subprocess and opens up to 3 streams to communicate with it. "program" should be a pathname, e.g., "/usr/local/bin/foo". Fork uses execvp(2), which "duplicates the shell's actions in searching for an executable file in a list of directories. The directory list is obtained from the environment." "args" should be the complete list of arguments, including argument 0, the name of the program, e.g., "foo". Upon return, the 'stdin' field of the Handle is a *writer* to the caller of Fork. The characters sent to write appear on Stdio.stdin of the forked process. Likewise, 'stdout' and 'stderr' are *readers* to the caller of Fork, and they get their characters from Stdio.stdout and Stdio.stderr of the forked process. If "mergeOutput" is true, then the 'stderr' field of the the result is NIL and characters written to Stdio.stderr by the forked process appear on the 'stdout' field of the result handler. If "ignoreOutput" is true, then output from the child's stdout and stderr will be read and discarded. The 'stderr' and 'stdout' fields of the result handler are NIL. Fork first disables the virtual interval timer; see setitimer(2); This prevents other threads from running while we're cloning the image. Fork then calls vfork(2). The child process closes the "writing" side of its stdin pipe, and the "reading" side of its stdout and stderr pipes. It calls dup2(2) to reset its stdin, stdout, and stderr. Finally, it calls execvp(2) to run the program. The parent process then re-enables the virtual timer, even if an exception was raised. All errors in the child process, up to and including the "execvp", produce error messages on Stdio.stderr, and the child process calls _exit(2), not exit(2). The returned value, "handle", is a subtype of MUTEX. The mutex guards the "childDied" field, which is FALSE when the fork is created. When the child-process terminates, a background thread will set "handle.childDied" to true and will call Thread.Broadcast on "handle.condition". After that point, the value of the "status" field is valid. *) PROCEDURE Wait (h: Handle); (* This is a simple mechanism for blocking the current thread (not the entire process) until the child process ("h.pid") and the threads forked by AttachStreams have terminated. After this procedure returns, the value of "h.status" is defined. *) PROCEDURE Signal (h: Handle; signal := Usignal.SIGTERM) RAISES {Error}; (* This signals the subprocess. It takes no action with respect to the I/O streams. *) PROCEDURE AttachStreams (h : Handle; stdinReader : Rd.T := NIL; stdoutWriter, stderrWriter: Wr.T := NIL ); (* This will fork a separate thread to read characters from "stdinReader" and write them to "h.stdin", unless "stdinReader" h.stdin is NIL. Likewise, it will fork a thread to reader characters from "h.stdout" and write them to the stdoutWriter, unless stdoutWriter or h.stdout is NIL. Likewise for the stderr pipe and stderrWriter. These threads terminate on Rd.EndOfFile, Rd.Failure, Wr.Failure, or Thread.Alerted. The effect of attaching more than one stream to the same pipe is undefined. When a newline is read from any reader ("stdinReader", "h.stdout", or "h.stderr"), it will be sent to the corresponding writer, and then Wr.Flush will be called on that writer. Threads that are created are remembered in the handle, and Wait will join those threads. *) END UProcess.