running_process/
environment.rs1use std::ffi::OsString;
11use std::io;
12
13pub fn user_baseline_environment() -> io::Result<Vec<(OsString, OsString)>> {
20 crate::platform::host::login_environment()
21}
22
23#[cfg(windows)]
29pub fn user_baseline_environment_block() -> io::Result<Vec<u16>> {
30 crate::platform::host::login_environment_block()
31}
32
33#[cfg(any(feature = "daemon", test))]
38pub(crate) fn materialize_environment(
39 policy: crate::EnvironmentPolicy,
40 explicit: &[(String, String)],
41) -> io::Result<Option<Vec<(String, String)>>> {
42 if policy == crate::EnvironmentPolicy::Inherit && explicit.is_empty() {
43 return Ok(None);
44 }
45
46 let mut output: Vec<(String, String)> = match policy {
47 crate::EnvironmentPolicy::Inherit => std::env::vars().collect(),
48 crate::EnvironmentPolicy::UserBaseline => user_baseline_environment()?
49 .into_iter()
50 .map(|(key, value)| {
51 (
52 key.to_string_lossy().into_owned(),
53 value.to_string_lossy().into_owned(),
54 )
55 })
56 .collect(),
57 crate::EnvironmentPolicy::Clear => Vec::new(),
58 crate::EnvironmentPolicy::Auto => {
59 return Err(io::Error::new(
60 io::ErrorKind::InvalidInput,
61 "Auto environment policy must be resolved before materialization",
62 ));
63 }
64 };
65
66 for (key, value) in explicit {
67 let existing = output
72 .iter_mut()
73 .find(|(candidate, _)| environment_keys_match(candidate, key));
74 if let Some((existing_key, existing_value)) = existing {
75 *existing_key = key.clone();
76 *existing_value = value.clone();
77 } else {
78 output.push((key.clone(), value.clone()));
79 }
80 }
81 Ok(Some(output))
82}
83
84#[cfg(any(feature = "daemon", test))]
86fn environment_keys_match(left: &str, right: &str) -> bool {
87 if crate::platform::host::environment_keys_are_case_insensitive() {
88 left.eq_ignore_ascii_case(right)
89 } else {
90 left == right
91 }
92}
93
94#[cfg(test)]
95mod materialize_tests {
96 use super::*;
97
98 #[test]
99 fn clear_uses_only_explicit_entries() {
100 let env = materialize_environment(
101 crate::EnvironmentPolicy::Clear,
102 &[("CLIENT_ONLY".into(), "forwarded".into())],
103 )
104 .unwrap()
105 .unwrap();
106 assert_eq!(env, vec![("CLIENT_ONLY".into(), "forwarded".into())]);
107 }
108
109 #[test]
110 fn empty_inherit_uses_native_inheritance() {
111 assert_eq!(
112 materialize_environment(crate::EnvironmentPolicy::Inherit, &[]).unwrap(),
113 None
114 );
115 }
116
117 #[test]
118 fn unresolved_auto_is_rejected() {
119 assert!(materialize_environment(crate::EnvironmentPolicy::Auto, &[]).is_err());
120 }
121
122 #[test]
126 fn explicit_entries_replace_by_this_hosts_name_comparison() {
127 let env = materialize_environment(
128 crate::EnvironmentPolicy::Clear,
129 &[
130 ("ExampleVar".into(), "first".into()),
131 ("EXAMPLEVAR".into(), "second".into()),
132 ],
133 )
134 .unwrap()
135 .unwrap();
136
137 if crate::platform::host::environment_keys_are_case_insensitive() {
138 assert_eq!(
139 env,
140 vec![("EXAMPLEVAR".to_string(), "second".to_string())],
141 "one variable, last spelling and value win"
142 );
143 } else {
144 assert_eq!(
145 env,
146 vec![
147 ("ExampleVar".to_string(), "first".to_string()),
148 ("EXAMPLEVAR".to_string(), "second".to_string()),
149 ],
150 "two distinct variables"
151 );
152 }
153 }
154
155 #[test]
157 fn user_baseline_excludes_process_only_variables() {
158 std::env::set_var("RUNNING_PROCESS_MATERIALIZE_CANARY", "1");
159 let env = materialize_environment(crate::EnvironmentPolicy::UserBaseline, &[]).unwrap();
160 std::env::remove_var("RUNNING_PROCESS_MATERIALIZE_CANARY");
161 let env = env.expect("UserBaseline always materializes a block");
162 assert!(
163 !env.iter()
164 .any(|(key, _)| key == "RUNNING_PROCESS_MATERIALIZE_CANARY"),
165 "a process-local variable must not reach the user baseline"
166 );
167 }
168}