Skip to main content

prick_exec/
lib.rs

1//! Child process launch for `prk run`.
2//!
3//! # Module layout
4//!
5//! | Module | Responsibility |
6//! |---|---|
7//! | [`guard`] | Refusing loader-controlling variables unless opted in |
8//! | [`launch`] | Resolving the program, building the environment, starting the child |
9//! | [`signal`] | Exit codes, signal dispositions and job control |
10//! | [`cmdline`] | Escaping arguments for a Windows batch shim |
11//! | [`error`] | Why a launch did not happen, and what a shell would have exited with |
12//! | `winjob` | Windows job objects and console control handling |
13//! | `winsec` | Restricting a file to the current user, for `prick-auth` |
14//!
15//! # Why this crate can be small
16//!
17//! Argv is carried as `Vec<OsString>` from `clap`'s `trailing_var_arg` all the
18//! way to `Command::args()`, which passes it to `execvp` as a vector. **There
19//! is never a command string**, so there is nothing to quote and nothing to
20//! escape -- the entire class of shell-quoting bugs is structurally absent
21//! rather than defended against, and non-UTF-8 arguments survive byte for byte.
22//!
23//! The single exception is a Windows `.cmd` shim, where `cmd.exe` genuinely
24//! does interpose a string. [`cmdline`] is that exception, it is confined to
25//! one module, and it has more tests than anything else here.
26//!
27//! # The `unsafe` in this crate
28//!
29//! `prick-core` is `#![forbid(unsafe_code)]` and is the miri target. This crate
30//! is the opposite: it is where every `unsafe` in the workspace lives, and it
31//! is exactly what miri **cannot** reach, because miri cannot execute a
32//! process, a signal handler, or an FFI call.
33//!
34//! That is stated plainly rather than papered over. The consequences are:
35//!
36//! - Every `unsafe` block carries a `// SAFETY:` comment naming the invariant
37//!   it relies on, not merely asserting that one exists.
38//! - The surface is kept as small as it can be: three places on Unix (the
39//!   `pre_exec` hook and the two calls inside it) and the Windows job, console
40//!   and security bindings.
41//! - Verification comes from **real integration tests that start real
42//!   processes**, in `tests/`. There is no substitute available.
43//!
44//! # Nothing is written to disk
45//!
46//! Secrets reach the child through its environment block and nowhere else. This
47//! crate creates no temporary file, no fifo and no dotenv, so there is no
48//! window in which a secret exists at a path something else could read.
49
50// This crate is the workspace's single home for `unsafe`; see the module docs
51// above. `unsafe_code` is `warn` workspace-wide precisely so that lifting it
52// has to be deliberate, visible, and in one place.
53#![allow(unsafe_code)]
54
55pub mod cmdline;
56pub mod error;
57pub mod guard;
58pub mod launch;
59pub mod signal;
60
61#[cfg(windows)]
62pub mod winjob;
63#[cfg(windows)]
64pub mod winsec;
65
66pub use error::LaunchError;
67pub use guard::{EnvGuard, GuardError};
68pub use launch::{LaunchSpec, run};