Browse the docs

silk.system.child_process.

Run another program, wait for it to end, and get its exit code and all of its output.

When to use#

Make a ProcessRequest with ChildProcess.request, add arguments and environment variables, and run it with ChildProcess.submit. This is not a shell. Spaces, quotes, and other special characters in an argument are passed to the program as they are.

Details#

Arguments and environment entries are bytes, kept in the order you add them. The environment of the child starts empty: the child gets only the variables that you set. The child uses the working directory of the caller, unless you use ChildProcess.requestWithin. Standard input of the child is closed.

ChildProcess.submit blocks until the child ends. The ProcessOutcome owns all standard output and standard error. An exit code that is not 0 is a normal outcome, not an error.

Errors#

  • ProcessError if the child cannot start, the wait fails, or the output cannot be captured.

  • OutOfMemoryError if the Allocator cannot supply the request or the captured output.

Gotchas#

An argument, an environment name, or an environment value must not contain NUL. The request uses NUL to separate entries.

Examples#

Run a program with a test provider and read its exit code#

silk
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.collections.bytes { Bytes }
import silk.system.child_process { ChildProcess, ProcessError, ProcessOutcome, ProcessRequest }
import silk.core.effect { Effect }
import silk.system.filesystem { FileError, Path }
import silk.core.option { Option }
import silk.num.usize

struct Completed {}

effect fn execute(self: &mut Completed, request: &ProcessRequest) -> ProcessOutcome
  ! ProcessError | OutOfMemoryError
  ? &mut Allocator {
  return ChildProcess.exited(
    usize.toI32(ChildProcess.argumentCount(request)),
    Bytes.make(),
    Bytes.make()
  )
}

impl ChildProcess for Completed {
  execute: Completed.execute
}

pub effect fn main() -> i32
  ! FileError | OutOfMemoryError | ProcessError {
  let mut allocator = Allocator.systemAllocatorProvider()
  let mut provider = Completed {}
  let path = run Path.make("/usr/bin/git") |> Effect.provideMut<Allocator>(&mut allocator)
  let mut request = run ChildProcess.request(&path) |> Effect.provideMut<Allocator>(&mut allocator)
  run ChildProcess.addArgument(&mut request, "status")
    |> Effect.provideMut<Allocator>(&mut allocator)
  let outcome = run ChildProcess.submit(&request) |> Effect.provideMut<ChildProcess>(&mut provider)
    |> Effect.provideMut<Allocator>(&mut allocator)
  return match move ChildProcess.exitCode(&outcome) {
    Option<i32>.Some { value } => value - 1
    Option<i32>.None => 1
  }
}

Import as ChildProcess with import silk.system.child_process { ChildProcess }.

Public declarations: 8.

ProcessOperation#

silk
pub struct ProcessOperation

The step that failed, as recorded in a ProcessError: spawn, wait, or capture.

Field code#

silk
pub code: i32

The stable numeric code for the provider-reported execution stage.

Implementation Debug for ProcessOperation#

silk
impl Debug for ProcessOperation

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the code, such as Spawn, or the code when it has no name.

ProcessReason#

silk
pub struct ProcessReason

The portable reason for a ProcessError. Compare its code with the constructors such as ChildProcess.notFound.

Field code#

silk
pub code: i32

The stable numeric code for the portable recovery category.

Implementation Debug for ProcessReason#

silk
impl Debug for ProcessReason

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the code, such as NotFound, or the code when it has no name.

ProcessError#

silk
pub struct ProcessError

The error when a child process cannot start, cannot be waited for, or its output cannot be captured.

Details#

Decide how to recover from reason. ChildProcess.providerCode gives a code from the provider, such as an errno value, for diagnostics. A child that exits with a code other than 0 is a ProcessOutcome, not this error.

Field operation#

silk
pub operation: ProcessOperation

The execution stage reported by the provider.

Field reason#

silk
pub reason: ProcessReason

The portable category that callers can use for recovery.

Implementation Debug for ProcessError#

silk
impl Debug for ProcessError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the fields, as ProcessError { operation: Spawn, reason: NotFound, providerCode: 2 }. It leaves out providerCode when there is none.

ProcessRequest#

silk
pub struct ProcessRequest

A request to run one program, with its arguments, environment, and working directory.

Details#

Arguments and environment entries are bytes, so a value from the host can go to the child without changes. They keep the order in which you add them. The environment of the child starts empty: the child gets only the variables that you set. The child uses the working directory of the caller, unless the request comes from ChildProcess.requestWithin. This is not a shell: spaces, quotes, and other special characters in an argument go to the program as they are.

Gotchas#

The bytes must not contain NUL, because NUL separates the entries.

Exited#

silk
pub struct Exited

A child that ended with an exit code.

Field code#

silk
pub code: i32

The code the child returned. Any value, including a nonzero one, is ordinary data.

Field output#

silk
pub output: Bytes

The complete captured standard output, owned by this outcome.

Field errors#

silk
pub errors: Bytes

The complete captured standard error, owned by this outcome.

Signaled#

silk
pub struct Signaled

A child that a signal ended.

Field signal#

silk
pub signal: i32

The platform signal number that terminated the child.

Field output#

silk
pub output: Bytes

The complete captured standard output, owned by this outcome.

Field errors#

silk
pub errors: Bytes

The complete captured standard error, owned by this outcome.

ProcessOutcome#

silk
pub struct ProcessOutcome

How a child ended: with an exit code or by a signal, and its captured output.

Details#

Use ChildProcess.exitCode and ChildProcess.terminatingSignal. The exit code and the signal are separate, so a signal number cannot be read as an exit code.

Field value#

silk
pub value: Exited | Signaled

The completed outcome.

ChildProcess#

silk
pub service ChildProcess

The effect that runs child processes: run another program, wait for it to end, and get its exit code and all of its output.

When to use#

Make a ProcessRequest with ChildProcess.request, add arguments and environment variables, and run it with ChildProcess.submit. This is not a shell. Spaces, quotes, and other special characters in an argument are passed to the program as they are.

Details#

A provider implements execute. Callers use ChildProcess.submit. Native programs use the provider in silk.system.child_process_native.

Operation execute#

silk
effect fn execute(
    request: &ProcessRequest
  ) -> ProcessOutcome ! ProcessError | OutOfMemoryError ? &mut ChildProcess | &mut Allocator

Runs request, waits until the child ends, and returns the outcome with all captured output.

Details#

Standard input of the child is closed. An exit code other than 0 is a normal outcome.

Errors#

  • ProcessError if the child cannot start, the wait fails, or the output cannot be captured.

  • OutOfMemoryError if the Allocator cannot supply the captured output.

Implementation ChildProcess#

silk
impl ChildProcess

Associated function spawnOperation#

silk
pub fn spawnOperation() -> ProcessOperation

Returns the stage for preparing and starting the child process.

Associated function waitOperation#

silk
pub fn waitOperation() -> ProcessOperation

Returns the stage for waiting until the child process terminates.

Associated function captureOperation#

silk
pub fn captureOperation() -> ProcessOperation

Returns the stage for copying the completed output and error streams.

Associated function operationCode#

silk
pub fn operationCode(operation: ProcessOperation) -> i32

Returns the stable numeric code for an execution stage.

Associated function notFound#

silk
pub fn notFound() -> ProcessReason

Returns the reason used when no executable exists at the requested path.

Associated function permissionDenied#

silk
pub fn permissionDenied() -> ProcessReason

Returns the reason used when the provider denies access to the requested executable.

Associated function invalidRequest#

silk
pub fn invalidRequest() -> ProcessReason

Returns the reason used when the provider cannot present the request to its process boundary.

Associated function noSpace#

silk
pub fn noSpace() -> ProcessReason

Returns the reason used when process setup or captured output exhausts provider storage.

Associated function unsupported#

silk
pub fn unsupported() -> ProcessReason

Returns the reason used when the provider does not support the requested process operation.

Associated function other#

silk
pub fn other() -> ProcessReason

Returns the reason used when no other portable recovery category applies.

Associated function reasonCode#

silk
pub fn reasonCode(reason: ProcessReason) -> i32

Returns the stable numeric code for a portable recovery category.

Associated function failure#

silk
pub fn failure(operation: ProcessOperation, reason: ProcessReason) -> ProcessError

Creates a process failure without a provider-defined numeric code.

Associated function failureWithCode#

silk
pub fn failureWithCode(
    operation: ProcessOperation,
    reason: ProcessReason,
    code: i32
  ) -> ProcessError

Creates a process failure with a provider-defined numeric code for diagnostics.

Associated function providerCode#

silk
pub fn providerCode(error: &ProcessError) -> Option<i32>

Returns the provider-defined numeric code, or None when the failure has no such code.

Associated function request#

silk
pub effect fn request(program: &Path) -> ProcessRequest ! OutOfMemoryError ? &mut Allocator

Returns a request to run program, with no arguments, an empty environment, and the working directory of the caller.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the request.

Associated function requestWithin#

silk
pub effect fn requestWithin(
    program: &Path,
    directory: &Path
  ) -> ProcessRequest ! OutOfMemoryError ? &mut Allocator

Returns a request to run program in directory, with no arguments and an empty environment.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the request.

Method addArgument#

silk
pub effect fn addArgument(
    self: &mut ProcessRequest,
    value: string
  ) -> () ! OutOfMemoryError ? &mut Allocator

Adds a copy of the text value as the next argument.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the storage. Do not use the request again, because it can contain part of the argument.

Gotchas#

value must not contain NUL.

Method addArgumentBytes#

silk
pub effect fn addArgumentBytes(
    self: &mut ProcessRequest,
    value: &[u8]
  ) -> () ! OutOfMemoryError ? &mut Allocator

Adds a copy of value as the next argument. The bytes do not have to be valid UTF-8.

When to use#

Use it for an argument that you got as bytes, such as a host path from Path.rawBytes or a program argument, so you can pass it on without changes.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the storage. Do not use the request again, because it can contain part of the argument.

Gotchas#

value must not contain NUL.

Method setVariable#

silk
pub effect fn setVariable(
    self: &mut ProcessRequest,
    name: string,
    value: string
  ) -> () ! OutOfMemoryError ? &mut Allocator

Adds the environment entry name=value, where name and value are text.

Details#

The environment of the request starts empty. This function does not read the environment of the caller. It does not check that name is a valid variable name.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the storage. Do not use the request again, because it can contain part of the entry.

Gotchas#

name and value must not contain NUL.

Method setVariableBytes#

silk
pub effect fn setVariableBytes(
    self: &mut ProcessRequest,
    name: &[u8],
    value: &[u8]
  ) -> () ! OutOfMemoryError ? &mut Allocator

Adds the environment entry name=value. The bytes do not have to be valid UTF-8.

When to use#

Use it for a value that you got as bytes, such as a copy of a variable from HostInput, so you can pass it on without changes.

Details#

The environment of the request starts empty. This function does not read the environment of the caller. It does not check that name is a valid variable name.

Errors#

  • OutOfMemoryError if the Allocator cannot supply the storage. Do not use the request again, because it can contain part of the entry.

Gotchas#

name and value must not contain NUL.

Method program#

silk
pub fn program(self: &ProcessRequest) -> &[u8]

Borrows the executable path bytes for the lifetime of the request borrow.

Method arguments#

silk
pub fn arguments(self: &ProcessRequest) -> &[u8]

Borrows all ordered arguments as one block of NUL-terminated entries.

Method argumentCount#

silk
pub fn argumentCount(self: &ProcessRequest) -> usize

Returns the number of calls to addArgument that completed successfully.

Method environment#

silk
pub fn environment(self: &ProcessRequest) -> &[u8]

Borrows the explicit environment as one block of NUL-terminated name=value entries.

Method environmentCount#

silk
pub fn environmentCount(self: &ProcessRequest) -> usize

Returns the number of calls to setVariable that completed successfully.

Method workingDirectory#

silk
pub fn workingDirectory(self: &ProcessRequest) -> &[u8]

Borrows the selected working-directory bytes, or an empty view when the child inherits one.

Method hasWorkingDirectory#

silk
pub fn hasWorkingDirectory(self: &ProcessRequest) -> bool

Reports whether the request selects a working directory instead of inheriting one.

Associated function exited#

silk
pub fn exited(code: i32, output: Bytes, errors: Bytes) -> ProcessOutcome

Creates an exited outcome that owns output and errors.

Associated function signaled#

silk
pub fn signaled(signal: i32, output: Bytes, errors: Bytes) -> ProcessOutcome

Creates a signaled outcome that owns output and errors.

Associated function isSignaled#

silk
pub fn isSignaled(outcome: &ProcessOutcome) -> bool

Reports whether a signal terminated the child instead of an exit code.

Associated function exitCode#

silk
pub fn exitCode(outcome: &ProcessOutcome) -> Option<i32>

Returns the exit code, or None when a signal terminated the child.

Associated function terminatingSignal#

silk
pub fn terminatingSignal(outcome: &ProcessOutcome) -> Option<i32>

Returns the terminating signal number, or None when the child returned an exit code.

Associated function outputBytes#

silk
pub fn outputBytes(outcome: &ProcessOutcome) -> &[u8]

Borrows the complete captured standard output without consuming the outcome.

Associated function errorBytes#

silk
pub fn errorBytes(outcome: &ProcessOutcome) -> &[u8]

Borrows the complete captured standard error without consuming the outcome.

Associated function submit#

silk
pub effect fn submit(
    request: &ProcessRequest
  ) -> ProcessOutcome ! ProcessError | OutOfMemoryError ? &mut ChildProcess | &mut Allocator

Runs request with the active provider, waits until the child ends, and returns the outcome.

Details#

It blocks. Standard input of the child is closed. The outcome owns all standard output and standard error. An exit code other than 0 is a normal outcome.

Errors#

  • ProcessError if the child cannot start, the wait fails, or the output cannot be captured.

  • OutOfMemoryError if the Allocator cannot supply the captured output.

Category: System.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.

View the source of this page