Skip to content
CryoCryo home
StdlibSystem

process

Subprocess spawning. Cross-platform: fork + execvp on POSIX, CreateProcess on Windows, selected by target.

ItemImport
Command, Stdio, Outputimport std::process::command;
Child, ExitStatus, ChildStdin, ...import std::process::child;
Signal, SigNoimport std::process::signal;

Command

The builder. Configure it, then finish with spawn, output, or status.

import std::process::command;

mut cmd: Command = Command::new(Str::new("git"));
cmd.arg(Str::new("rev-parse"));
cmd.arg(Str::new("HEAD"));
cmd.stdout(Stdio::Piped);

match (cmd.output()) {
    Result::Ok(out)  => { /* out.stdout() is a Slice<u8> */ }
    Result::Err(e)   => { /* IoError */ }
}
cmd.drop();
MethodNotes
static new(program: Str)The program is resolved against $PATH at spawn time; pass an absolute path to skip the lookup.
arg(value: Str)Append one argument.
env(key: Str, value: Str)Set a variable for the child.
env_clear()Do not inherit the parent's environment — only env() entries reach the child.
cwd(path: Str)Working directory for the child.
stdin / stdout / stderr(setting: Stdio)Wire up the standard streams.

Finishing

MethodReturnsNotes
spawn()Result<Child, IoError>Fork, configure, exec. You must eventually wait.
output()Result<Output, IoError>Spawn, wait, and collect stdout and stderr. The "run this and tell me what it printed" case.
status()Result<ExitStatus, IoError>Spawn and wait; streams go wherever you configured them.

Output exposes status() -> ExitStatus, stdout() -> Slice<u8>, and stderr() -> Slice<u8>. Both slices borrow the Output and are valid only until it drops — copy out anything you need to keep.

Stdio

type enum Stdio {
    Inherit;      // share the parent's fd (the default)
    Null;         // redirect to / from /dev/null
    Piped;        // create a pipe; the parent's end lands in Child::stdin / stdout / stderr
    Fd(i32);      // use a specific fd; ownership transfers
}

With Fd, the spawn dups the descriptor into the child and closes it in the parent.

If execvp fails in the child, it exits with 127 — the standard "command not found" status — so the parent sees that rather than a hang.

Child

A running subprocess, produced by spawn. It holds the pid and whichever pipe ends you asked for, exposed as stdin: Option<ChildStdin>, stdout: Option<ChildStdout>, and stderr: Option<ChildStderr>.

MethodReturnsNotes
id()i32
wait()Result<ExitStatus, IoError>Blocks. Closes stdin first so the child sees EOF.
try_wait()Result<Option<ExitStatus>, IoError>Ok(None) means still running.
kill()Result<(), IoError>SIGKILL on POSIX. Does not wait.
send_signal(sig: Signal)Result<(), IoError>

wait closing stdin first is deliberate: it is deadlock-avoidant for a child that reads all of stdin before writing anything to stdout.

ChildStdin implements Write; ChildStdout and ChildStderr implement Read. Dropping ChildStdin closes the pipe, which is what signals EOF to the child.

Reaping

Every Child should have wait — or kill then wait — called before it drops.

Child::drop closes any captured pipe ends and makes a non-blocking best-effort reap. If the child has already exited, its zombie is released there. If it is still running, a destructor cannot block, so the child stays detached and reaping it remains your responsibility. That closes the common "spawn, child finishes, drop without wait" leak without ever blocking.

ExitStatus

POSIX packs a normal exit and a signal death into one status word; ExitStatus is the decoded form.

MethodReturnsNotes
success()booleanExited with status 0.
exit_code()Option<i32>None if killed by a signal.
terminating_signal()Option<i32>Some if killed by a signal.

Signal

Signal wraps an i32 so the compiler catches passing a pid where a signal was expected. The common ones have named constructors — hangup, interrupt, quit, illegal, trap, abort, bus, floating_point, kill, segfault, pipe, alarm, terminate, child, cont, user1, user2 — and arbitrary numbers go through from_i32. The numbers match Linux x86-64.

Signal handling — installing a handler with sigaction — is not modelled here. The moment a program installs handlers it crosses into async-signal safety, reentrant allocation, and interaction with every other stdlib primitive; that is a separate design problem.