running_process_platform_internal/
env_vars.rs1use crate::env::{EnvKind, Owner};
31
32crate::declare_env_vars! {
33 pub const DECLARED_PLATFORM;
36 COMPUTERNAME => "COMPUTERNAME",
37 EnvKind::Text, Owner::Foreign, "hostname is unknown",
38 "Windows machine name, reported as the host name.";
39 DISPLAY => "DISPLAY",
40 EnvKind::Text, Owner::Foreign, "no X11 display; window icons unsupported",
41 "X11 display; its presence is what makes a Linux window icon possible.";
42 HOME => "HOME",
43 EnvKind::Path, Owner::Foreign, "autostart paths cannot be resolved",
44 "Home directory; roots the autostart entries and the fallback APE loader cache directory.";
45 LOCALAPPDATA => "LOCALAPPDATA",
46 EnvKind::Path, Owner::Foreign, "the platform default is derived",
47 "Windows per-user application data root.";
48 PATH => "PATH",
49 EnvKind::Text, Owner::Foreign, "the child inherits no explicit PATH",
50 "Executable search path, forwarded to the symbolization worker and searched for an APE image or loader.";
51 APE_CACHE_DIR => "RUNNING_PROCESS_APE_CACHE_DIR",
52 EnvKind::Path, Owner::Crate, "the XDG cache, runtime and temporary directories",
53 "Preferred directory for loaders extracted from APE images; used only when private and exec-capable.";
54 APE_LOADER => "RUNNING_PROCESS_APE_LOADER",
55 EnvKind::Path, Owner::Crate, "the image's embedded loader, then `ape`, then `/bin/sh`",
56 "Explicit loader (an `ape` binary or a POSIX shell) for APE images.";
57 CONPTY_CACHE => "RUNNING_PROCESS_CONPTY_CACHE",
58 EnvKind::Path, Owner::Crate, "the platform cache directory",
59 "Root under which the ConPTY sidecar is cached on Windows.";
60 CONPTY_DIAGNOSTICS => "RUNNING_PROCESS_CONPTY_DIAGNOSTICS",
61 EnvKind::Text, Owner::Crate, "ConPTY resolution is silent",
62 "Print how the ConPTY implementation was chosen and fetched, to stderr.";
63 CONPTY_OFFLINE => "RUNNING_PROCESS_CONPTY_OFFLINE",
64 EnvKind::Text, Owner::Crate, "the ConPTY sidecar may be fetched",
65 "Forbid fetching the ConPTY sidecar over the network.";
66 CONPTY_SIDECAR_FETCH_TIMEOUT_MS => "RUNNING_PROCESS_CONPTY_SIDECAR_FETCH_TIMEOUT_MS",
67 EnvKind::Number { zero_selects_default: true }, Owner::Crate, "the built-in fetch timeout",
68 "Upper bound on the ConPTY sidecar download, in milliseconds.";
69 KILL_DRAIN_TIMEOUT_MS => "RUNNING_PROCESS_KILL_DRAIN_TIMEOUT_MS",
70 EnvKind::Number { zero_selects_default: false }, Owner::Crate, "two seconds",
71 "How long `kill()` waits for output capture to drain, in milliseconds.";
72 NATIVE_TERMINAL_INPUT_TRACE_PATH => "RUNNING_PROCESS_NATIVE_TERMINAL_INPUT_TRACE_PATH",
73 EnvKind::Text, Owner::Crate, "terminal input is not traced",
74 "Where Windows native terminal input events are traced.";
75 USE_SYSTEM_CONPTY => "RUNNING_PROCESS_USE_SYSTEM_CONPTY",
76 EnvKind::Text, Owner::Crate, "the bundled ConPTY sidecar is preferred",
77 "Use the system ConPTY instead of the bundled sidecar on Windows.";
78 TMPDIR => "TMPDIR",
79 EnvKind::Path, Owner::Foreign, "the platform temporary directory",
80 "macOS per-session temporary directory; a broker endpoint root.";
81 WAYLAND_DISPLAY => "WAYLAND_DISPLAY",
82 EnvKind::Text, Owner::Foreign, "not a Wayland session",
83 "Wayland session marker; window icons are unsupported under Wayland.";
84 WINDOWID => "WINDOWID",
85 EnvKind::Text, Owner::Foreign, "the X11 window is unknown",
86 "X11 window id exported by the terminal emulator.";
87 WT_SESSION => "WT_SESSION",
88 EnvKind::Text, Owner::Foreign, "not inside Windows Terminal",
89 "Windows Terminal session marker; runtime window icons are degraded there.";
90 XDG_CACHE_HOME => "XDG_CACHE_HOME",
91 EnvKind::Path, Owner::Foreign, "`~/.cache` is used",
92 "XDG per-user cache root; where extracted APE loaders are installed.";
93 XDG_CONFIG_HOME => "XDG_CONFIG_HOME",
94 EnvKind::Path, Owner::Foreign, "`~/.config` is used",
95 "XDG per-user configuration root; where service definitions are read.";
96 XDG_DATA_HOME => "XDG_DATA_HOME",
97 EnvKind::Path, Owner::Foreign, "the platform default is derived",
98 "XDG per-user data root, used by the daemon runtime collector.";
99 XDG_RUNTIME_DIR => "XDG_RUNTIME_DIR",
100 EnvKind::Path, Owner::Foreign, "a per-user directory under /tmp",
101 "XDG per-user runtime root; where broker sockets and fallback APE loaders are placed.";
102 XDG_STATE_HOME => "XDG_STATE_HOME",
103 EnvKind::Path, Owner::Foreign, "~/.local/state",
104 "XDG state root.";
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 use crate::env::Owner;
111
112 #[test]
113 fn declarations_are_sorted_unique_and_documented() {
114 let names: Vec<&str> = DECLARED_PLATFORM.iter().map(|var| var.name).collect();
115 let mut sorted = names.clone();
116 sorted.sort_unstable();
117 sorted.dedup();
118 assert_eq!(names, sorted, "DECLARED_PLATFORM must be sorted and unique");
119 for var in DECLARED_PLATFORM {
120 assert!(
121 !var.summary.trim().is_empty(),
122 "{} has no summary",
123 var.name
124 );
125 assert!(
126 !var.default.trim().is_empty(),
127 "{} has no default",
128 var.name
129 );
130 }
131 }
132
133 #[test]
134 fn owner_follows_the_name() {
135 for var in DECLARED_PLATFORM {
136 let ours = var.name.starts_with("RUNNING_PROCESS_");
137 assert_eq!(
138 var.owner == Owner::Crate,
139 ours,
140 "{} is declared {:?}",
141 var.name,
142 var.owner
143 );
144 }
145 }
146
147 const DYNAMIC_KEYS: &[&str] = &[];
151
152 #[test]
161 fn production_code_calls_std_env_only_for_dynamic_keys() {
162 let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
163 let mut offenders = Vec::new();
164 visit(&root, &mut offenders);
165 assert!(
166 offenders.is_empty(),
167 "undeclared environment reads: {offenders:#?}"
168 );
169 }
170
171 fn visit(dir: &std::path::Path, offenders: &mut Vec<String>) {
172 for entry in std::fs::read_dir(dir).expect("read src") {
173 let path = entry.expect("dir entry").path();
174 if path.is_dir() {
175 if path.file_name().is_some_and(|name| name == "tests") {
176 continue;
177 }
178 visit(&path, offenders);
179 continue;
180 }
181 if path.extension().is_none_or(|ext| ext != "rs")
182 || path.file_name().is_some_and(|name| {
183 let name = name.to_string_lossy();
184 name == "env.rs" || name == "env_vars.rs" || name.ends_with("_tests.rs")
185 })
186 {
187 continue;
188 }
189 let text = std::fs::read_to_string(&path).expect("read source");
190 let lines: Vec<&str> = text.lines().collect();
191 let cut = lines
192 .iter()
193 .enumerate()
194 .position(|(index, line)| {
195 let line = line.trim_start();
196 (line.starts_with("#[cfg(test)]") || line.starts_with("#[cfg(all(test"))
197 && lines[index + 1..]
198 .iter()
199 .map(|next| next.trim())
200 .find(|next| !next.is_empty() && !next.starts_with("#["))
201 .is_some_and(|next| next.starts_with("mod ") && next.ends_with('{'))
202 })
203 .unwrap_or(lines.len());
204 for (index, line) in lines[..cut].iter().enumerate() {
205 let direct = [
206 "env::var(",
207 "env::var_os(",
208 "env::set_var(",
209 "env::remove_var(",
210 ]
211 .iter()
212 .find_map(|call| line.find(call).map(|at| &line[at + call.len()..]));
213 if let Some(arg) = direct {
214 if !DYNAMIC_KEYS.iter().any(|key| arg.starts_with(key)) {
215 offenders.push(format!("{}:{}", path.display(), index + 1));
216 }
217 }
218 }
219 }
220 }
221}