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#
ProcessErrorif the child cannot start, the wait fails, or the output cannot be captured.OutOfMemoryErrorif theAllocatorcannot 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#
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#
pub struct ProcessOperationThe step that failed, as recorded in a ProcessError: spawn, wait, or capture.
Field code#
pub code: i32The stable numeric code for the provider-reported execution stage.
Implementation Debug for ProcessOperation#
impl Debug for ProcessOperationOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the code, such as Spawn, or the code when it has no name.
ProcessReason#
pub struct ProcessReasonThe portable reason for a ProcessError. Compare its code with the constructors such as
ChildProcess.notFound.
Field code#
pub code: i32The stable numeric code for the portable recovery category.
Implementation Debug for ProcessReason#
impl Debug for ProcessReasonOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the code, such as NotFound, or the code when it has no name.
ProcessError#
pub struct ProcessErrorThe 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#
pub operation: ProcessOperationThe execution stage reported by the provider.
Field reason#
pub reason: ProcessReasonThe portable category that callers can use for recovery.
Implementation Debug for ProcessError#
impl Debug for ProcessErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the fields, as ProcessError { operation: Spawn, reason: NotFound, providerCode: 2 }.
It leaves out providerCode when there is none.
ProcessRequest#
pub struct ProcessRequestA 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#
pub struct ExitedA child that ended with an exit code.
Field code#
pub code: i32The code the child returned. Any value, including a nonzero one, is ordinary data.
Field output#
pub output: BytesThe complete captured standard output, owned by this outcome.
Field errors#
pub errors: BytesThe complete captured standard error, owned by this outcome.
Signaled#
pub struct SignaledA child that a signal ended.
Field signal#
pub signal: i32The platform signal number that terminated the child.
Field output#
pub output: BytesThe complete captured standard output, owned by this outcome.
Field errors#
pub errors: BytesThe complete captured standard error, owned by this outcome.
ProcessOutcome#
pub struct ProcessOutcomeHow 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#
pub value: Exited | SignaledThe completed outcome.
ChildProcess#
pub service ChildProcessThe 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#
effect fn execute(
request: &ProcessRequest
) -> ProcessOutcome ! ProcessError | OutOfMemoryError ? &mut ChildProcess | &mut AllocatorRuns 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#
ProcessErrorif the child cannot start, the wait fails, or the output cannot be captured.OutOfMemoryErrorif theAllocatorcannot supply the captured output.
Implementation ChildProcess#
impl ChildProcessAssociated function spawnOperation#
pub fn spawnOperation() -> ProcessOperationReturns the stage for preparing and starting the child process.
Associated function waitOperation#
pub fn waitOperation() -> ProcessOperationReturns the stage for waiting until the child process terminates.
Associated function captureOperation#
pub fn captureOperation() -> ProcessOperationReturns the stage for copying the completed output and error streams.
Associated function operationCode#
pub fn operationCode(operation: ProcessOperation) -> i32Returns the stable numeric code for an execution stage.
Associated function notFound#
pub fn notFound() -> ProcessReasonReturns the reason used when no executable exists at the requested path.
Associated function permissionDenied#
pub fn permissionDenied() -> ProcessReasonReturns the reason used when the provider denies access to the requested executable.
Associated function invalidRequest#
pub fn invalidRequest() -> ProcessReasonReturns the reason used when the provider cannot present the request to its process boundary.
Associated function noSpace#
pub fn noSpace() -> ProcessReasonReturns the reason used when process setup or captured output exhausts provider storage.
Associated function unsupported#
pub fn unsupported() -> ProcessReasonReturns the reason used when the provider does not support the requested process operation.
Associated function other#
pub fn other() -> ProcessReasonReturns the reason used when no other portable recovery category applies.
Associated function reasonCode#
pub fn reasonCode(reason: ProcessReason) -> i32Returns the stable numeric code for a portable recovery category.
Associated function failure#
pub fn failure(operation: ProcessOperation, reason: ProcessReason) -> ProcessErrorCreates a process failure without a provider-defined numeric code.
Associated function failureWithCode#
pub fn failureWithCode(
operation: ProcessOperation,
reason: ProcessReason,
code: i32
) -> ProcessErrorCreates a process failure with a provider-defined numeric code for diagnostics.
Associated function providerCode#
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#
pub effect fn request(program: &Path) -> ProcessRequest ! OutOfMemoryError ? &mut AllocatorReturns a request to run program, with no arguments, an empty environment, and the working
directory of the caller.
Errors#
OutOfMemoryErrorif theAllocatorcannot supply the request.
Associated function requestWithin#
pub effect fn requestWithin(
program: &Path,
directory: &Path
) -> ProcessRequest ! OutOfMemoryError ? &mut AllocatorReturns a request to run program in directory, with no arguments and an empty environment.
Errors#
OutOfMemoryErrorif theAllocatorcannot supply the request.
Method addArgument#
pub effect fn addArgument(
self: &mut ProcessRequest,
value: string
) -> () ! OutOfMemoryError ? &mut AllocatorAdds a copy of the text value as the next argument.
Errors#
OutOfMemoryErrorif theAllocatorcannot 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#
pub effect fn addArgumentBytes(
self: &mut ProcessRequest,
value: &[u8]
) -> () ! OutOfMemoryError ? &mut AllocatorAdds 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#
OutOfMemoryErrorif theAllocatorcannot 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#
pub effect fn setVariable(
self: &mut ProcessRequest,
name: string,
value: string
) -> () ! OutOfMemoryError ? &mut AllocatorAdds 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#
OutOfMemoryErrorif theAllocatorcannot 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#
pub effect fn setVariableBytes(
self: &mut ProcessRequest,
name: &[u8],
value: &[u8]
) -> () ! OutOfMemoryError ? &mut AllocatorAdds 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#
OutOfMemoryErrorif theAllocatorcannot 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#
pub fn program(self: &ProcessRequest) -> &[u8]Borrows the executable path bytes for the lifetime of the request borrow.
Method arguments#
pub fn arguments(self: &ProcessRequest) -> &[u8]Borrows all ordered arguments as one block of NUL-terminated entries.
Method argumentCount#
pub fn argumentCount(self: &ProcessRequest) -> usizeReturns the number of calls to addArgument that completed successfully.
Method environment#
pub fn environment(self: &ProcessRequest) -> &[u8]Borrows the explicit environment as one block of NUL-terminated name=value entries.
Method environmentCount#
pub fn environmentCount(self: &ProcessRequest) -> usizeReturns the number of calls to setVariable that completed successfully.
Method workingDirectory#
pub fn workingDirectory(self: &ProcessRequest) -> &[u8]Borrows the selected working-directory bytes, or an empty view when the child inherits one.
Method hasWorkingDirectory#
pub fn hasWorkingDirectory(self: &ProcessRequest) -> boolReports whether the request selects a working directory instead of inheriting one.
Associated function exited#
pub fn exited(code: i32, output: Bytes, errors: Bytes) -> ProcessOutcomeCreates an exited outcome that owns output and errors.
Associated function signaled#
pub fn signaled(signal: i32, output: Bytes, errors: Bytes) -> ProcessOutcomeCreates a signaled outcome that owns output and errors.
Associated function isSignaled#
pub fn isSignaled(outcome: &ProcessOutcome) -> boolReports whether a signal terminated the child instead of an exit code.
Associated function exitCode#
pub fn exitCode(outcome: &ProcessOutcome) -> Option<i32>Returns the exit code, or None when a signal terminated the child.
Associated function terminatingSignal#
pub fn terminatingSignal(outcome: &ProcessOutcome) -> Option<i32>Returns the terminating signal number, or None when the child returned an exit code.
Associated function outputBytes#
pub fn outputBytes(outcome: &ProcessOutcome) -> &[u8]Borrows the complete captured standard output without consuming the outcome.
Associated function errorBytes#
pub fn errorBytes(outcome: &ProcessOutcome) -> &[u8]Borrows the complete captured standard error without consuming the outcome.
Associated function submit#
pub effect fn submit(
request: &ProcessRequest
) -> ProcessOutcome ! ProcessError | OutOfMemoryError ? &mut ChildProcess | &mut AllocatorRuns 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#
ProcessErrorif the child cannot start, the wait fails, or the output cannot be captured.OutOfMemoryErrorif theAllocatorcannot 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.