peek_proc_reader/
environ.rs1use crate::error::{io_to_error, Result};
8use std::path::PathBuf;
9
10#[derive(Debug, Clone)]
12pub struct EnvironEntry {
13 pub key: String,
14 pub value: String,
15}
16
17#[cfg(target_os = "linux")]
19fn parse_environ_bytes(raw: &[u8]) -> Vec<EnvironEntry> {
20 let mut vars = Vec::new();
21
22 for entry in raw.split(|&b| b == 0) {
23 if entry.is_empty() {
24 continue;
25 }
26 let s = String::from_utf8_lossy(entry);
27 if let Some(eq) = s.find('=') {
28 let key = s[..eq].to_string();
29 let value = s[eq + 1..].to_string();
30 vars.push(EnvironEntry { key, value });
31 }
32 }
33
34 vars.sort_by(|a, b| a.key.cmp(&b.key));
35 vars
36}
37
38#[cfg(target_os = "linux")]
40pub fn read_environ(pid: i32) -> Result<Vec<EnvironEntry>> {
41 let path = PathBuf::from(format!("/proc/{}/environ", pid));
42 let raw = std::fs::read(&path).map_err(|e| io_to_error(path, e, pid))?;
43 Ok(parse_environ_bytes(&raw))
44}
45
46#[cfg(not(target_os = "linux"))]
48pub fn read_environ(_pid: i32) -> Result<Vec<EnvironEntry>> {
49 Ok(Vec::new())
50}
51
52#[cfg(test)]
53mod tests {
54 use super::{parse_environ_bytes, EnvironEntry};
55 use proptest::prelude::*;
56
57 #[test]
58 fn environ_entry_debug_clone_works() {
59 let e = EnvironEntry {
60 key: "KEY".to_string(),
61 value: "VALUE".to_string(),
62 };
63 let cloned = e.clone();
64 assert_eq!(cloned.key, "KEY");
65 assert_eq!(cloned.value, "VALUE");
66 let debug = format!("{:?}", e);
67 assert!(debug.contains("KEY"));
68 }
69
70 proptest! {
71 #[test]
72 fn parse_environ_bytes_round_trips_basic_pairs(
73 kvs in prop::collection::vec(
74 (r"[A-Z_][A-Z0-9_]{0,4}", r"[a-zA-Z0-9_]{0,8}"),
76 0..16
77 )
78 ) {
79 let mut raw = Vec::new();
81 for (k, v) in &kvs {
82 if k.is_empty() {
83 continue;
84 }
85 raw.extend_from_slice(k.as_bytes());
86 raw.push(b'=');
87 raw.extend_from_slice(v.as_bytes());
88 raw.push(0);
89 }
90
91 let parsed = parse_environ_bytes(&raw);
92
93 let original_keys: std::collections::HashSet<&str> =
95 kvs.iter().map(|(k, _)| k.as_str()).collect();
96 for EnvironEntry { key, .. } in &parsed {
97 assert!(original_keys.contains(key.as_str()));
98 }
99 }
100 }
101}