Skip to main content

running_process/
env_vars.rs

1//! Every environment variable this crate reads, declared in one place.
2//!
3//! An environment variable is an interface. Other repositories embed this
4//! crate -- soldr vendors it -- and have to reason about what it reads, which
5//! until now meant grepping every call site. [`DECLARED`] is that list, and
6//! `declaration_table_covers_every_variable` keeps it honest: a new
7//! `RUNNING_PROCESS_*` literal anywhere in the crate fails the build unless it
8//! is declared here.
9//!
10//! # Why booleans get two accessors rather than one
11//!
12//! "Is this switch on?" has two defensible answers when the value is neither
13//! clearly on nor clearly off, and which one is right depends on who owns the
14//! variable -- not on the call site, which is how a codebase ends up with five
15//! parsers that disagree.
16//!
17//! - [`flag_owned`] is for switches this crate defines. Unknown means **off**.
18//!   The value space is ours, so anything outside it is a typo, and a typo in
19//!   `SOMETHING_DISABLE` must not disable something.
20//! - [`flag_foreign`] is for values written by someone else, where absence of a
21//!   recognised falsy spelling is better read as "set". Unknown means **on**.
22//!   The daemon marker is this kind: a process that says it is a daemon in a
23//!   spelling we did not anticipate is still a daemon, and a stray `=0` must
24//!   never exempt it from reaping.
25//! - [`flag_opt_out`] is for an escape hatch that is on until someone turns it
26//!   off. Unset means **on**, which is the whole difference from the other two.
27//!
28//! Both trim and lowercase before comparing, so `" True "` and `"TRUE"` agree.
29//!
30//! # The table and the parser must agree
31//!
32//! Writing the table turned up a switch whose declared default and whose
33//! parser disagreed -- `BROKER_OWNED_BIND` is documented as on by default but
34//! was first declared with semantics that read unset as off. That is the class
35//! of bug this module exists to end, so
36//! `an_unset_flag_matches_its_declared_default` now checks the two against
37//! each other for every declared flag.
38
39/// Declare a crate's own variables with the shared mechanism; see
40/// `running_process_platform_internal::declare_env_vars`. Re-exported so a
41/// crate built on `running-process` can keep its own table without depending
42/// on the platform layer directly.
43pub use running_process_platform_internal::declare_env_vars;
44pub use running_process_platform_internal::env::{
45    flag_foreign, flag_opt_out, flag_owned, os_named, string_named, value_is_affirmative_foreign,
46    EnvKind, EnvVar, Owner,
47};
48/// The variables `running-process-platform-internal` declares and reads,
49/// including the ones this crate reads too. See [`all_declared`] for the
50/// combined inventory.
51pub use running_process_platform_internal::env_vars as platform;
52
53/// Every environment variable read by this crate or by the platform layer it
54/// builds on, once each, sorted by name.
55///
56/// With the `probe` feature this also lists the probe crate's own reads (its
57/// crash spool and report directories, its crash-handler opt-out), because
58/// such a build links that crate. The probe daemon and the symbolization
59/// worker are separate processes that declare their own reads.
60///
61/// [`DECLARED`] lists what `running-process` itself reads. A process that
62/// links `running-process` also runs `running-process-platform-internal`,
63/// whose reads ([`platform::DECLARED_PLATFORM`]) include variables this crate
64/// never touches directly -- `HOME`, `DISPLAY`, the ConPTY switches. An
65/// embedder scrubbing a child's environment needs both, so this is the one
66/// list to check.
67///
68/// A name read by both crates has one declaration, owned by the lower crate
69/// and referred to from [`DECLARED`], so it appears here once;
70/// `the_combined_inventory_is_sorted_unique_and_documented` holds that.
71pub fn all_declared() -> Vec<EnvVar> {
72    let mut all: Vec<EnvVar> = DECLARED
73        .iter()
74        .chain(platform::DECLARED_PLATFORM)
75        .copied()
76        .collect();
77    // The probe crate sits below this one only behind its feature; its table
78    // joins the inventory exactly when its code does.
79    #[cfg(feature = "probe")]
80    all.extend_from_slice(running_process_probe::env_vars::DECLARED_PROBE);
81    all.sort_by(|left, right| left.name.cmp(right.name));
82    all.dedup_by(|left, right| left.name == right.name);
83    all
84}
85
86/// Declares this crate's variables and builds [`DECLARED`] from them.
87///
88/// An entry is either a full declaration, or `IDENT => use PATH;` for a
89/// variable owned by a crate below this one: the constant is re-exported
90/// under the same name and listed in [`DECLARED`] where it sorts, so each name
91/// has exactly one declaration however many crates read it.
92macro_rules! declare {
93    (@collect [$($all:ident)*]) => {
94        /// Every environment variable this crate reads.
95        ///
96        /// Kept in the same order as the declarations above, which
97        /// `declarations_are_sorted_and_unique` holds to alphabetical so a
98        /// reader can find a name without searching. [`all_declared`] adds the
99        /// variables only the platform layer reads.
100        pub const DECLARED: &[EnvVar] = &[$($all),*];
101    };
102    (@collect [$($all:ident)*] $ident:ident => use $($path:ident)::+; $($rest:tt)*) => {
103        #[doc = concat!(
104            "Declared by the platform layer, which reads it too: [`",
105            stringify!($($path)::+),
106            "`]."
107        )]
108        pub const $ident: EnvVar = $($path)::+;
109        declare!(@collect [$($all)* $ident] $($rest)*);
110    };
111    (@collect [$($all:ident)*]
112        $ident:ident => $name:literal, $kind:expr, $owner:expr, $default:literal, $summary:literal;
113        $($rest:tt)*
114    ) => {
115        #[doc = $summary]
116        ///
117        #[doc = concat!("Environment variable `", $name, "`. Unset: ", $default, ".")]
118        pub const $ident: EnvVar = EnvVar {
119            name: $name,
120            kind: $kind,
121            owner: $owner,
122            default: $default,
123            summary: $summary,
124        };
125        declare!(@collect [$($all)* $ident] $($rest)*);
126    };
127    ($($rest:tt)*) => {
128        declare!(@collect [] $($rest)*);
129    };
130}
131
132declare! {
133    GITHUB_ACTIONS => "GITHUB_ACTIONS",
134        EnvKind::ForeignFlag, Owner::Foreign, "not running under GitHub Actions",
135        "Set by GitHub Actions; tests wait longer for a shared runner.";
136    INVOCATION_ID => "INVOCATION_ID",
137        EnvKind::Text, Owner::Foreign, "not started by systemd",
138        "Set by systemd for a unit invocation; identifies the launching unit.";
139    LOCALAPPDATA => use running_process_platform_internal::env_vars::LOCALAPPDATA;
140    PATH => use running_process_platform_internal::env_vars::PATH;
141    BROKER_ALLOW_PRIVILEGED => "RUNNING_PROCESS_BROKER_ALLOW_PRIVILEGED",
142        EnvKind::ExactValue("1"), Owner::Crate, "privileged startup is refused",
143        "Opt out of the broker's refusal to start as root or LocalSystem.";
144    BROKER_CLIENT_TIMEOUT_MS => "RUNNING_PROCESS_BROKER_CLIENT_TIMEOUT_MS",
145        EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in client timeout",
146        "Broker client request timeout, in milliseconds.";
147    BROKER_CRASH_DUMP_DIR => "RUNNING_PROCESS_BROKER_CRASH_DUMP_DIR",
148        EnvKind::Path, Owner::Crate, "the standard diagnostic-artifact location",
149        "Where broker crash dumps are written.";
150    BROKER_HELLO_PERF_GUARD => "RUNNING_PROCESS_BROKER_HELLO_PERF_GUARD",
151        EnvKind::OwnedFlag, Owner::Crate, "the guard does not run",
152        "Run the broker Hello latency guard.";
153    BROKER_HELLO_TIMEOUT_MS => "RUNNING_PROCESS_BROKER_HELLO_TIMEOUT_MS",
154        EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in Hello timeout",
155        "Broker Hello handshake timeout, in milliseconds.";
156    BROKER_HTTP_BIND => "RUNNING_PROCESS_BROKER_HTTP_BIND",
157        EnvKind::Text, Owner::Crate, "the loopback bind address",
158        "Bind address for the broker HTTP aggregator.";
159    BROKER_HTTP_PORT => "RUNNING_PROCESS_BROKER_HTTP_PORT",
160        EnvKind::Number { zero_selects_default: false }, Owner::Crate, "an ephemeral port",
161        "Port for the broker HTTP aggregator.";
162    BROKER_LISTENER_FD => "RUNNING_PROCESS_BROKER_LISTENER_FD",
163        EnvKind::Number { zero_selects_default: false }, Owner::Foreign, "the daemon binds its own endpoint",
164        "Descriptor of a listening socket the broker already bound and passed.";
165    BROKER_MAX_INFLIGHT_HANDLERS => "RUNNING_PROCESS_BROKER_MAX_INFLIGHT_HANDLERS",
166        EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in concurrency cap",
167        "Maximum broker request handlers running at once.";
168    BROKER_OWNED_BIND => "RUNNING_PROCESS_BROKER_OWNED_BIND",
169        EnvKind::OptOutFlag, Owner::Crate, "broker-owned bind is used",
170        "Escape hatch: set falsy to fall back to spawn-then-probe.";
171    BROKER_V1_BACKEND_NAMESPACE => "RUNNING_PROCESS_BROKER_V1_BACKEND_NAMESPACE",
172        EnvKind::Text, Owner::Foreign, "no namespace is applied",
173        "Backend namespace handed to a v1 broker backend.";
174    BROKER_V1_BACKEND_PIPE => "RUNNING_PROCESS_BROKER_V1_BACKEND_PIPE",
175        EnvKind::Text, Owner::Foreign, "the backend derives its own endpoint",
176        "Endpoint a v1 broker backend should serve on.";
177    BROKER_V1_INSTANCE => "RUNNING_PROCESS_BROKER_V1_INSTANCE",
178        EnvKind::Text, Owner::Foreign, "the default instance",
179        "Instance identifier for a v1 broker backend.";
180    BROKER_V1_SERVICE_NAME => "RUNNING_PROCESS_BROKER_V1_SERVICE_NAME",
181        EnvKind::Text, Owner::Foreign, "the backend supplies its own name",
182        "Service name a v1 broker backend registers under.";
183    BROKER_V1_SERVICE_VERSION => "RUNNING_PROCESS_BROKER_V1_SERVICE_VERSION",
184        EnvKind::Text, Owner::Foreign, "the backend supplies its own version",
185        "Service version a v1 broker backend reports.";
186    BROKER_V1_SESSION_TOKEN => "RUNNING_PROCESS_BROKER_V1_SESSION_TOKEN",
187        EnvKind::Text, Owner::Foreign, "no session token is presented",
188        "Session token a v1 broker backend presents to the broker.";
189    BROKER_V1_SOCKET => "RUNNING_PROCESS_BROKER_V1_SOCKET",
190        EnvKind::Text, Owner::Foreign, "the standard broker endpoint",
191        "Broker endpoint a v1 backend dials.";
192    BROKER_V1_TRACEPARENT => "RUNNING_PROCESS_BROKER_V1_TRACEPARENT",
193        EnvKind::Text, Owner::Foreign, "no trace context is propagated",
194        "W3C traceparent propagated into a v1 broker backend.";
195    BROKER_V1_TRACESTATE => "RUNNING_PROCESS_BROKER_V1_TRACESTATE",
196        EnvKind::Text, Owner::Foreign, "no trace state is propagated",
197        "W3C tracestate propagated into a v1 broker backend.";
198    CHILD_PID_LOG_PATH => "RUNNING_PROCESS_CHILD_PID_LOG_PATH",
199        EnvKind::Path, Owner::Foreign, "spawned child PIDs are not logged",
200        "Append each spawned child PID to this file (test harness seam).";
201    CLIENT_CONNECT_TIMEOUT_MS => "RUNNING_PROCESS_CLIENT_CONNECT_TIMEOUT_MS",
202        EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in connect timeout",
203        "Daemon client connect timeout, in milliseconds.";
204    CLIENT_RPC_TIMEOUT_MS => "RUNNING_PROCESS_CLIENT_RPC_TIMEOUT_MS",
205        EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in RPC timeout",
206        "Daemon client RPC timeout, in milliseconds.";
207    DAEMON_IDENTITY_STAMP => "RUNNING_PROCESS_DAEMON_IDENTITY_STAMP",
208        EnvKind::Text, Owner::Crate, "a dev-scope daemon computes it from its own executable",
209        "Dev-scope daemon identity stamp, `<version>-<16 hex of the executable's blake3>`; ignored outside dev scope.";
210    DAEMON_SCOPE => "RUNNING_PROCESS_DAEMON_SCOPE",
211        EnvKind::Text, Owner::Crate, "the user-wide scope",
212        "Daemon scope selector; `dev` gives a CWD-scoped daemon for tests.";
213    DAEMON_SHADOWED => "RUNNING_PROCESS_DAEMON_SHADOWED",
214        EnvKind::OwnedFlag, Owner::Crate, "a dev-build daemon relocates itself",
215        "Marks a daemon already running from its shadow copy.";
216    DAEMON_START_TIMEOUT_MS => "RUNNING_PROCESS_DAEMON_START_TIMEOUT_MS",
217        EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in 750ms budget",
218        "How long a client waits for a freshly spawned daemon to bind its socket, in milliseconds.";
219    DISABLE => "RUNNING_PROCESS_DISABLE",
220        EnvKind::ExactValue("1"), Owner::Crate, "the broker is used",
221        "Canonical escape hatch: bypass the broker entirely.";
222    FAKE_BACKEND => "RUNNING_PROCESS_FAKE_BACKEND",
223        EnvKind::Path, Owner::Foreign, "backends are reached through the broker",
224        "TEST-ONLY: dial this endpoint directly, skipping broker negotiation.";
225    IS_DAEMON => "RUNNING_PROCESS_IS_DAEMON",
226        EnvKind::ForeignFlag, Owner::Crate, "the process is not a daemon",
227        "Marks a process spawned as a daemon, for originator reaping.";
228    KILL_DRAIN_TIMEOUT_MS => use running_process_platform_internal::env_vars::KILL_DRAIN_TIMEOUT_MS;
229    MANIFEST_DIR => "RUNNING_PROCESS_MANIFEST_DIR",
230        EnvKind::Path, Owner::Foreign, "the standard manifest location",
231        "Where broker cache manifests are read and written.";
232    NO_TRACKING => "RUNNING_PROCESS_NO_TRACKING",
233        EnvKind::OwnedFlag, Owner::Crate, "processes are tracked",
234        "Disable daemon IPC and process tracking.";
235    ORIGINATOR => "RUNNING_PROCESS_ORIGINATOR",
236        EnvKind::Text, Owner::Foreign, "the originator is inferred",
237        "Identifies the process that originated a spawn tree.";
238    SERVICE_DEF_DIR => "RUNNING_PROCESS_SERVICE_DEF_DIR",
239        EnvKind::Path, Owner::Foreign, "the standard service-definition location",
240        "Where service definitions are read from.";
241    TMPDIR => use running_process_platform_internal::env_vars::TMPDIR;
242    USERNAME => "USERNAME",
243        EnvKind::Text, Owner::Foreign, "the endpoint is named `unknown`",
244        "Windows account name, mixed into the daemon pipe name.";
245    XDG_CONFIG_HOME => use running_process_platform_internal::env_vars::XDG_CONFIG_HOME;
246    XDG_DATA_HOME => use running_process_platform_internal::env_vars::XDG_DATA_HOME;
247    XDG_RUNTIME_DIR => use running_process_platform_internal::env_vars::XDG_RUNTIME_DIR;
248}
249
250#[cfg(test)]
251#[path = "tests/env_vars.rs"]
252mod tests;