tur/process
process management: pid, spawn, wait, exec, cwd, chdir.
Wrappers around unistd.h and sys/wait.h primitives.
Since: Phase B1
Pid
(defopaque Pid :int)
ChildHandle
(defopaque ChildHandle :int :linear)
process/pid
(process/pid)
return the current process ID.
The PID of the calling process as a Pid.
(process/pid) ; => 12345
Since: Phase B2
process/ppid
(process/ppid)
return the parent process ID.
The PPID of the calling process as a Pid.
(process/ppid) ; => 1
Since: Phase B2
process/exit
(process/exit [code :int])
terminate the process with the given exit code.
| code | exit status (0 = success, non-zero = failure) |
(process/exit 0)
Since: Phase B2
Signal
(defdata Signal :copy (SigTerm) (SigKill) (SigInt) (SigHup) (SigOther int))
a signal to deliver to (or that terminated) a process.
SigTerm / SigKill / SigInt / SigHup name the common ones; SigOther carries any other platform signal number as an escape hatch. A tagged union rather than a raw signal number, so call sites say what they send.
Since: stdlib-os-surface P0
signal->number
(signal->number [s :Signal] :int)
the platform signal number for a Signal (POSIX values).
number->signal
(number->signal [n :int] :Signal)
the Signal for a platform signal number.
ExitStatus
(defdata ExitStatus :copy (Exited int) (Signaled Signal))
how a reaped child process ended.
(Exited code) for a normal exit with that status code; (Signaled sig) when a signal terminated it. Two variants, so a caller cannot mistake a killed child for exit code 9. Windows has no signals, so there every child is Exited.
Since: stdlib-os-surface P1
exit-status/success?
(exit-status/success? [st :ExitStatus] :bool)
true for (Exited 0).
| st | an ExitStatus |
true when the child exited normally with status 0.
(exit-status/success? (Exited 0)) ; => true
Since: stdlib-os-surface P1
process/exec-raw
(process/exec-raw [path :cstr args :int])
execvp with a cons-carrier arg list (internal).
process/exec
(process/exec [path :cstr & args :cstr])
replace the current process image with a new program.
Calls execvp(3) with argv = { path, args... }; PATH is searched when path has no slash. Does not return on success.
| path | the program to run (also passed as argv[0]) | |
| args | the remaining arguments, one cstr each |
Only on failure: the IoError execvp reported (e.g. not-found).
(process/exec "/bin/ls" "-l")
Since: Phase B2
process/spawn-raw
(process/spawn-raw [path :cstr args :int])
start a child; its pid, or -errno on failure (internal).
process/child-of-raw
(process/child-of-raw [pid :int] :ChildHandle)
wrap a live child's pid as its ChildHandle (internal).
process/spawn
(process/spawn [path :cstr & args :cstr])
start a new child process running path.
| path | the program to run (PATH is searched when it has no slash; also | |
| passed as argv[0]) | ||
| args | the remaining arguments, one cstr each |
(Ok child) -- a linear ChildHandle to reap exactly once with process/wait -- or (Err e) when the child could not be started (e.g. io-error/not-found? for a missing program).
(match (process/spawn "/bin/ls" "-l") (Ok child) (process/wait child) (Err e) (Err e))
Since: Phase B2
process/wait-raw
(process/wait-raw [child :ChildHandle])
reap a child; an encoded status (internal).
process/decode-wait
(process/decode-wait [code :int] :(Result ExitStatus IoError))
turn a process/wait-raw code into a Result (internal).
process/wait
(process/wait [child :ChildHandle])
wait for a child process to end, consuming its handle.
| child | ChildHandle from process/spawn (consumed) |
(Ok status) -- (Exited code) or (Signaled sig) -- or (Err e) when waiting failed.
(process/wait child) ; => (Ok (Exited 0))
Since: Phase B2
process/run
(process/run [path :cstr & args :cstr])
run a program to completion.
process/spawn followed by process/wait. The child inherits this process's stdin, stdout and stderr; use process/capture or process/output to collect its output instead.
| path | the program to run (PATH searched when it has no slash) | |
| args | the remaining arguments, one cstr each |
(Ok status) -- how the child ended -- or (Err e) when it could not be started or waited for.
(process/run "/bin/ls" "-l" "/tmp") ; => (Ok (Exited 0))
Since: Phase B2
process/capture-raw
(process/capture-raw [path :cstr args :int])
popen-based stdout capture (internal).
process/capture
(process/capture [path :cstr & args :cstr])
run a program and capture its stdout as a string.
Runs the command through popen(3) with every word single-quoted (quotes inside an argument are escaped), so arguments reach the program verbatim. The child's stderr is inherited and its exit status is not reported; use process/output for both.
| path | the program to run | |
| args | the remaining arguments, one cstr each |
(Ok out) -- a fresh heap cstr of everything the child wrote to stdout, which the caller owns (release with free) -- or (Err e) when the command could not be started.
(process/capture "echo" "hi") ; => (Ok "hi\n")
Since: Phase B2
process/cwd
(process/cwd)
return the current working directory.
(Ok dir) -- a fresh heap cstr the caller owns (release with free) -- or (Err e).
(process/cwd) ; => (Ok "/home/user/project")
Since: Phase B2
process/chdir
(process/chdir [path :cstr])
change the current working directory.
| path | the target directory path cstr |
(Ok nil), or (Err e) carrying the errno.
(process/chdir "/tmp") ; => (Ok nil)
Since: Phase B2
process/child-pid
(process/child-pid [^borrow child :ChildHandle] :Pid)
the process id of a spawned child, without reaping it.
| child | ChildHandle from process/spawn (borrowed; still must be waited) |
The child's Pid. On Windows the handle is a process HANDLE, and this answers its process id via GetProcessId.
(process/kill (process/child-pid child) (SigTerm))
Since: stdlib-os-surface P0
process/kill-raw
(process/kill-raw [pid :Pid signo :int])
kill(2) with a signal number (internal).
process/kill
(process/kill [pid :Pid sig :Signal])
send a signal to a process.
| pid | target process (process/child-pid gives a spawned child's) | |
| sig | the Signal to send |
(Ok nil), or (Err e). On Windows only SigKill and SigTerm map (both to TerminateProcess); any other Signal is an EINVAL error.
(process/kill (process/child-pid child) (SigKill)) ; => (Ok nil)
Since: stdlib-os-surface P0
signal/catch-raw
(signal/catch-raw [signo :int])
catch signo with the flag-setting handler (internal).
signal/default-raw
(signal/default-raw [signo :int])
give signo its default action (internal).
signal/ignore-raw
(signal/ignore-raw [signo :int])
ignore signo (internal).
signal/flag-raw
(signal/flag-raw [signo :int take :bool])
read the flag for signo, clearing it when take (internal).
signal/handle
(signal/handle [sig :Signal])
catch a signal so the program can poll for it.
From now on a delivery of sig no longer runs its default action (usually: terminate); it records that sig arrived, which signal/pending? and signal/take! report. A blocking call it interrupts -- net/accept, net/recv -- returns an io-error/interrupted? error, so a waiting loop wakes up to check. Do not combine with reactor-add-signal for the same signal: the reactor masks it and reads it itself.
| sig | the Signal to catch |
(Ok nil), or (Err e): EINVAL for a signal that cannot be caught (SigKill). On Windows only SigInt (Ctrl+C) and SigTerm can be.
(signal/handle (SigTerm)) ; => (Ok nil)
Since: stdlib-os-surface P3
signal/on-interrupt
(signal/on-interrupt)
catch Ctrl+C (SIGINT) so the program can poll for it.
Shorthand for (signal/handle (SigInt)); poll with signal/interrupted?.
(Ok nil), or (Err e).
(signal/on-interrupt) (while (not (signal/interrupted?)) (step))
Since: stdlib-os-surface P3
signal/pending?
(signal/pending? [sig :Signal])
has a caught signal arrived since it was last taken?
Does not clear it; signal/take! does.
| sig | a Signal installed with signal/handle |
true when sig has been delivered since the last signal/take! of it.
(signal/pending? (SigTerm)) ; => false
Since: stdlib-os-surface P3
signal/take!
(signal/take! [sig :Signal])
report and clear a caught signal.
| sig | a Signal installed with signal/handle |
true when sig had arrived (one or more times) since the last take; the next call answers false until it arrives again.
(when (signal/take! (SigHup)) (reload-config))
Since: stdlib-os-surface P3
signal/interrupted?
(signal/interrupted? :bool)
has Ctrl+C (SIGINT) arrived?
(signal/pending? (SigInt)): true once SIGINT has been caught, after signal/on-interrupt, until a signal/take! of it.
true when SIGINT is pending.
(signal/interrupted?) ; => false
Since: stdlib-os-surface P3
signal/ignore
(signal/ignore [sig :Signal])
make a signal do nothing at all.
Unlike signal/handle nothing is recorded, and blocking calls are not interrupted. (signal/ignore (SigOther 13)) -- SIGPIPE -- is the classic use; stdlib/net.tur already keeps SIGPIPE from its own sends.
| sig | the Signal |
(Ok nil), or (Err e) -- EINVAL for SigKill, and on Windows for anything but SigInt and SigTerm.
(signal/ignore (SigHup)) ; => (Ok nil)
Since: stdlib-os-surface P3
signal/reset
(signal/reset [sig :Signal])
give a signal back its default action.
Undoes signal/handle or signal/ignore. A flag already recorded stays until taken.
| sig | the Signal |
(Ok nil), or (Err e) -- EINVAL as for signal/ignore.
(signal/reset (SigInt)) ; => (Ok nil)
Since: stdlib-os-surface P3
ProcOutput
(defstruct ProcOutput [out :cstr err :cstr status :ExitStatus])
everything process/output collects from a finished child.
out and err are the child's complete stdout and stderr, as fresh heap strings the caller owns (release each with free); status is how it ended. (The fields are not spelled stdout / stderr: those are macros in some C libraries, and a struct field takes its C name from the field.)
Since: stdlib-os-surface P2
ProcOpts
(defstruct ProcOpts [])
how process/output starts the child.
input -- (Some text) is written to the child's stdin, which is then
closed; None gives it an empty stdin (never this process's own).
cwd -- (Some dir) runs the child in dir; None inherits the cwd. env -- (Some vars) replaces the environment with these "KEY=VALUE"
strings; None inherits it.
process/default-opts is all three None.
Since: stdlib-os-surface P2
process/default-opts
(process/default-opts :ProcOpts)
ProcOpts with empty stdin, inherited cwd and env.
(ProcOpts (None) (None) (None)).
(process/output (process/default-opts) "ls" "-l")
Since: stdlib-os-surface P2
ProcRun
(defopaque ProcRun :ptr<void>)
the raw outcome of process/output-raw (internal).
process/output-raw
(process/output-raw [path :cstr args :int has-input :bool input :cstr has-cwd :bool cwd :cstr has-env :bool env :(Vec cstr)])
spawn with three pipes and collect (internal).
process/proc-run-out
(process/proc-run-out [r :ProcRun] :cstr)
take the captured stdout (internal).
process/proc-run-err
(process/proc-run-err [r :ProcRun] :cstr)
take the captured stderr (internal).
process/proc-run-code
(process/proc-run-code [r :ProcRun] :int)
the encoded exit status (internal).
process/proc-run-free
(process/proc-run-free [r :ProcRun] :void)
free the ProcRun record, not its strings (internal).
process/finish-output
(process/finish-output [r :ProcRun] :(Result ProcOutput IoError))
move a ProcRun into a ProcOutput (internal).
process/output-with
(process/output-with [opts :ProcOpts path :cstr args :int has-input :bool input :cstr])
run the raw spawn with opts' cwd and env (internal).
process/output
(process/output [opts :ProcOpts path :cstr & args :cstr])
run a program to completion and collect everything.
The child's stdout and stderr are each captured in full, read together so a chatty child cannot deadlock against a full pipe; its stdin is opts' input (or empty). argv[0] is path, as for process/run.
| opts | a ProcOpts (process/default-opts for the plain case) | |
| path | the program to run (PATH searched when it has no slash) | |
| args | the remaining arguments, one cstr each |
(Ok (ProcOutput out err status)), or (Err e) when the child could not be started (e.g. not-found, or a cwd that does not exist). On Windows this is not implemented yet and answers an ENOSYS IoError.
(match (process/output (process/default-opts) "sh" "-c" "echo hi; echo oops >&2") (Ok o) (println (.out o)) ; prints "hi" (Err e) (println (io-error/message e)))
Since: stdlib-os-surface P2