1#![doc(html_root_url = "https://docs.rs/get_env/0.1.0")]
30#![cfg_attr(
31 any(
32 all(target_os = "linux", target_env = "gnu"),
33 target_os = "macos"
34 ),
35 feature(used)
36)]
37#![warn(
38 missing_copy_implementations,
39 missing_debug_implementations,
40 missing_docs,
41 trivial_numeric_casts,
42 unused_extern_crates,
43 unused_import_braces,
44 unused_qualifications,
45 unused_results,
46)] #![cfg_attr(feature = "cargo-clippy", warn(clippy_pedantic))]
48#![cfg_attr(
49 feature = "cargo-clippy",
50 allow(type_complexity, option_option, indexing_slicing)
51)]
52
53#[macro_use]
54extern crate lazy_static;
55#[cfg(target_family = "unix")]
56extern crate libc;
57
58#[cfg(target_family = "unix")]
59use libc::c_char;
60#[cfg(
61 any(
62 target_family = "windows",
63 target_os = "macos",
64 target_os = "ios"
65 )
66)]
67use std::env;
68#[cfg(target_family = "unix")]
69use std::{ffi::CStr, os::unix::ffi::OsStringExt};
70use std::{ffi::OsString, io, sync};
71#[cfg(any(target_os = "android", target_os = "linux"))]
72use std::{fs, io::Read};
73
74lazy_static! {
75 static ref ARGV: sync::RwLock<Option<Option<Vec<OsString>>>> = sync::RwLock::new(None);
76 static ref ENVP: sync::RwLock<Option<Option<Vec<(OsString, OsString)>>>> =
77 sync::RwLock::new(None);
78}
79
80pub fn args() -> Option<Vec<String>> {
109 args_os().map(|x| x.into_iter().map(|a| a.into_string().unwrap()).collect())
110}
111
112pub fn vars() -> Option<Vec<(String, String)>> {
137 vars_os().map(|x| {
138 x.into_iter()
139 .map(|(a, b)| (a.into_string().unwrap(), b.into_string().unwrap()))
140 .collect()
141 })
142}
143
144pub fn args_os() -> Option<Vec<OsString>> {
166 if ARGV.read().unwrap().is_none() {
167 let mut write = ARGV.write().unwrap();
168 if write.is_none() {
169 *write = Some(argv_from_global().ok().or_else(|| argv_from_proc().ok()));
170 }
171 }
172 ARGV.read().unwrap().as_ref().unwrap().clone()
173}
174
175pub fn vars_os() -> Option<Vec<(OsString, OsString)>> {
193 if ENVP.read().unwrap().is_none() {
194 let mut write = ENVP.write().unwrap();
195 if write.is_none() {
196 *write = Some(envp_from_global().ok().or_else(|| envp_from_proc().ok()));
197 }
198 }
199 ENVP.read().unwrap().as_ref().unwrap().clone()
200}
201
202fn argv_from_global() -> Result<Vec<OsString>, ()> {
203 #[cfg(
204 any(
205 target_family = "windows",
206 target_os = "macos",
207 target_os = "ios"
208 )
209 )]
210 {
211 Ok(env::args_os().collect())
213 }
214 #[cfg(
215 not(
216 any(
217 target_family = "windows",
218 target_os = "macos",
219 target_os = "ios"
220 )
221 )
222 )]
223 {
224 Err(())
225 }
226}
227
228fn argv_from_proc() -> Result<Vec<OsString>, io::Error> {
229 #[cfg(any(target_os = "android", target_os = "linux"))]
230 {
231 let mut cmdline = Vec::new();
232 let _ = fs::File::open("/proc/self/cmdline")?
233 .read_to_end(&mut cmdline)
234 .unwrap(); if let Some(b'\0') = cmdline.last() {
236 let null = cmdline.pop().unwrap();
237 assert_eq!(null, b'\0');
238 }
239 Ok(cmdline
240 .split(|&x| x == b'\0')
241 .map(|x| OsStringExt::from_vec(x.to_vec()))
242 .collect::<Vec<_>>())
243 }
244 #[cfg(not(any(target_os = "android", target_os = "linux")))]
245 {
246 Err(io::Error::new(
247 io::ErrorKind::NotFound,
248 "no /proc/self/cmdline equivalent",
249 ))
250 }
251}
252
253fn envp_from_global() -> Result<Vec<(OsString, OsString)>, ()> {
254 #[cfg(target_family = "unix")]
255 {
256 unsafe fn environ() -> *mut *const *const c_char {
257 #[cfg(target_os = "macos")]
258 {
259 extern "C" {
260 fn _NSGetEnviron() -> *mut *const *const c_char;
261 }
262 _NSGetEnviron()
263 }
264 #[cfg(not(target_os = "macos"))]
265 {
266 extern "C" {
267 static mut environ: *const *const c_char;
269 }
270 &mut environ
271 }
272 }
273 unsafe {
274 let mut environ = *environ();
275 if environ.is_null() {
276 return Err(());
277 }
278 let mut result = Vec::new();
279 while !(*environ).is_null() {
280 if let Some(key_value) = parse_env(CStr::from_ptr(*environ).to_bytes()) {
281 result.push(key_value);
282 }
283 environ = environ.offset(1);
284 }
285 Ok(result)
286 }
287 }
288 #[cfg(target_family = "windows")]
289 {
290 Ok(env::vars_os().collect())
292 }
293}
294fn envp_from_proc() -> Result<Vec<(OsString, OsString)>, io::Error> {
295 #[cfg(any(target_os = "android", target_os = "linux"))]
296 {
297 let mut envp = Vec::new();
298 let _ = fs::File::open("/proc/self/environ")?
299 .read_to_end(&mut envp)
300 .unwrap(); if let Some(b'\0') = envp.last() {
302 let null = envp.pop().unwrap();
303 assert_eq!(null, b'\0');
304 }
305 Ok(envp
306 .split(|&x| x == b'\0')
307 .flat_map(|x| parse_env(x))
308 .collect::<Vec<_>>())
309 }
310 #[cfg(not(any(target_os = "android", target_os = "linux")))]
311 {
312 Err(io::Error::new(
313 io::ErrorKind::NotFound,
314 "no /proc/self/environ equivalent",
315 ))
316 }
317}
318
319#[cfg(target_family = "unix")]
320fn parse_env(input: &[u8]) -> Option<(OsString, OsString)> {
321 if input.is_empty() {
323 return None;
324 }
325 let pos = input[1..].iter().position(|&x| x == b'=').map(|p| p + 1);
326 pos.map(|p| {
327 (
328 OsStringExt::from_vec(input[..p].to_vec()),
329 OsStringExt::from_vec(input[p + 1..].to_vec()),
330 )
331 })
332}
333
334#[doc(hidden)]
335#[cfg(
337 any(
338 all(target_os = "linux", target_env = "gnu"),
339 target_os = "macos"
340 )
341)]
342#[cfg_attr(
343 all(target_os = "linux", target_env = "gnu"),
344 link_section = ".init_array"
345)]
346#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
347#[used] pub static GRAB_ARGV_ENVP: extern "C" fn(
350 argc: libc::c_int,
351 argv: *const *const c_char,
352 envp: *const *const c_char,
353) = {
354 extern "C" fn grab_argv_envp(
356 _argc: libc::c_int, argv: *const *const c_char, envp: *const *const c_char,
357 ) {
358 unsafe {
359 let mut collect_args: Vec<OsString> = Vec::new();
360 let mut argv = argv;
361 while !(*argv).is_null() {
362 collect_args.push(OsStringExt::from_vec(
363 CStr::from_ptr(*argv).to_bytes().to_vec(),
364 ));
365 argv = argv.offset(1);
366 }
367 let mut collect_vars: Vec<(OsString, OsString)> = Vec::new();
368 let mut envp = envp;
369 while !(*envp).is_null() {
370 let x = CStr::from_ptr(*envp).to_bytes();
371 if let Some(x) = parse_env(x) {
372 collect_vars.push(x);
373 }
374 envp = envp.offset(1);
375 }
376 *ARGV.write().unwrap() = Some(Some(collect_args));
377 *ENVP.write().unwrap() = Some(Some(collect_vars));
378 }
379 }
380 grab_argv_envp
381};
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386 use std::env;
387 #[test]
388 fn same() {
389 let args = vec![
390 ARGV.read().unwrap().clone().unwrap_or(None),
391 argv_from_global().ok(),
392 argv_from_proc().ok(),
393 Some(env::args_os().collect::<Vec<_>>()),
394 ];
395 let mut args2 = args.clone().into_iter().flat_map(|x| x).collect::<Vec<_>>();
396 args2.dedup();
397 assert!(args2.len() == 1, "{:?}", args);
398
399 let args = vec![
400 ENVP.read().unwrap().clone().unwrap_or(None),
401 envp_from_global().ok(),
402 envp_from_proc().ok(),
403 Some(env::vars_os().collect::<Vec<_>>()),
404 ];
405 let mut args2 = args.clone().into_iter().flat_map(|x| x).collect::<Vec<_>>();
406 args2.dedup();
407 assert!(args2.len() == 1, "{:?}", args);
408 }
409}