1use crate::{FromStrRadix, ProcResult};
2use std::collections::HashMap;
3use std::io::BufRead;
4
5#[cfg(feature = "serde1")]
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone)]
18#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
19#[non_exhaustive]
20pub struct Status {
21 pub name: String,
23 pub umask: Option<u32>,
25 pub state: String,
27 pub tgid: i32,
29 pub ngid: Option<i32>,
31 pub pid: i32,
33 pub ppid: i32,
35 pub tracerpid: i32,
37 pub ruid: u32,
39 pub euid: u32,
41 pub suid: u32,
43 pub fuid: u32,
45 pub rgid: u32,
47 pub egid: u32,
49 pub sgid: u32,
51 pub fgid: u32,
53 pub fdsize: u32,
55 pub groups: Vec<u32>,
57 pub nstgid: Option<Vec<i32>>,
63 pub nspid: Option<Vec<i32>>,
67 pub nspgid: Option<Vec<i32>>,
70 pub nssid: Option<Vec<i32>>,
74 pub vmpeak: Option<u64>,
76 pub vmsize: Option<u64>,
78 pub vmlck: Option<u64>,
80 pub vmpin: Option<u64>,
84 pub vmhwm: Option<u64>,
86 pub vmrss: Option<u64>,
89 pub rssanon: Option<u64>,
91 pub rssfile: Option<u64>,
93 pub rssshmem: Option<u64>,
97 pub vmdata: Option<u64>,
99 pub vmstk: Option<u64>,
101 pub vmexe: Option<u64>,
103 pub vmlib: Option<u64>,
105 pub vmpte: Option<u64>,
107 pub vmswap: Option<u64>,
110 pub hugetlbpages: Option<u64>,
112 pub threads: u64,
114 pub sigq: (u64, u64),
122 pub sigpnd: u64,
124 pub shdpnd: u64,
126 pub sigblk: u64,
128 pub sigign: u64,
130 pub sigcgt: u64,
132 pub capinh: u64,
134 pub capprm: u64,
136 pub capeff: u64,
138 pub capbnd: Option<u64>,
140 pub capamb: Option<u64>,
142 pub nonewprivs: Option<u64>,
144 pub seccomp: Option<u32>,
150 pub speculation_store_bypass: Option<String>,
152 pub cpus_allowed: Option<Vec<u32>>,
154 pub cpus_allowed_list: Option<Vec<(u32, u32)>>,
156 pub mems_allowed: Option<Vec<u32>>,
158 pub mems_allowed_list: Option<Vec<(u32, u32)>>,
160 pub voluntary_ctxt_switches: Option<u64>,
162 pub nonvoluntary_ctxt_switches: Option<u64>,
164
165 pub core_dumping: Option<bool>,
172
173 pub thp_enabled: Option<bool>,
177}
178
179impl crate::FromBufRead for Status {
180 fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> {
181 let mut map = HashMap::new();
182
183 for line in reader.lines() {
184 let line = line?;
185 if line.is_empty() {
186 continue;
187 }
188 let mut s = line.split(':');
189 let field = expect!(s.next());
190 let value = expect!(s.next()).trim();
191
192 map.insert(field.to_string(), value.to_string());
193 }
194
195 let status = Status {
196 name: expect!(map.remove("Name")),
197 umask: map.remove("Umask").map(|x| Ok(from_str!(u32, &x, 8))).transpose()?,
198 state: expect!(map.remove("State")),
199 tgid: from_str!(i32, &expect!(map.remove("Tgid"))),
200 ngid: map.remove("Ngid").map(|x| Ok(from_str!(i32, &x))).transpose()?,
201 pid: from_str!(i32, &expect!(map.remove("Pid"))),
202 ppid: from_str!(i32, &expect!(map.remove("PPid"))),
203 tracerpid: from_str!(i32, &expect!(map.remove("TracerPid"))),
204 ruid: expect!(Status::parse_uid_gid(expect!(map.get("Uid")), 0)),
205 euid: expect!(Status::parse_uid_gid(expect!(map.get("Uid")), 1)),
206 suid: expect!(Status::parse_uid_gid(expect!(map.get("Uid")), 2)),
207 fuid: expect!(Status::parse_uid_gid(&expect!(map.remove("Uid")), 3)),
208 rgid: expect!(Status::parse_uid_gid(expect!(map.get("Gid")), 0)),
209 egid: expect!(Status::parse_uid_gid(expect!(map.get("Gid")), 1)),
210 sgid: expect!(Status::parse_uid_gid(expect!(map.get("Gid")), 2)),
211 fgid: expect!(Status::parse_uid_gid(&expect!(map.remove("Gid")), 3)),
212 fdsize: from_str!(u32, &expect!(map.remove("FDSize"))),
213 groups: Status::parse_list(&expect!(map.remove("Groups")))?,
214 nstgid: map.remove("NStgid").map(|x| Status::parse_list(&x)).transpose()?,
215 nspid: map.remove("NSpid").map(|x| Status::parse_list(&x)).transpose()?,
216 nspgid: map.remove("NSpgid").map(|x| Status::parse_list(&x)).transpose()?,
217 nssid: map.remove("NSsid").map(|x| Status::parse_list(&x)).transpose()?,
218 vmpeak: Status::parse_with_kb(map.remove("VmPeak"))?,
219 vmsize: Status::parse_with_kb(map.remove("VmSize"))?,
220 vmlck: Status::parse_with_kb(map.remove("VmLck"))?,
221 vmpin: Status::parse_with_kb(map.remove("VmPin"))?,
222 vmhwm: Status::parse_with_kb(map.remove("VmHWM"))?,
223 vmrss: Status::parse_with_kb(map.remove("VmRSS"))?,
224 rssanon: Status::parse_with_kb(map.remove("RssAnon"))?,
225 rssfile: Status::parse_with_kb(map.remove("RssFile"))?,
226 rssshmem: Status::parse_with_kb(map.remove("RssShmem"))?,
227 vmdata: Status::parse_with_kb(map.remove("VmData"))?,
228 vmstk: Status::parse_with_kb(map.remove("VmStk"))?,
229 vmexe: Status::parse_with_kb(map.remove("VmExe"))?,
230 vmlib: Status::parse_with_kb(map.remove("VmLib"))?,
231 vmpte: Status::parse_with_kb(map.remove("VmPTE"))?,
232 vmswap: Status::parse_with_kb(map.remove("VmSwap"))?,
233 hugetlbpages: Status::parse_with_kb(map.remove("HugetlbPages"))?,
234 threads: from_str!(u64, &expect!(map.remove("Threads"))),
235 sigq: expect!(Status::parse_sigq(&expect!(map.remove("SigQ")))),
236 sigpnd: from_str!(u64, &expect!(map.remove("SigPnd")), 16),
237 shdpnd: from_str!(u64, &expect!(map.remove("ShdPnd")), 16),
238 sigblk: from_str!(u64, &expect!(map.remove("SigBlk")), 16),
239 sigign: from_str!(u64, &expect!(map.remove("SigIgn")), 16),
240 sigcgt: from_str!(u64, &expect!(map.remove("SigCgt")), 16),
241 capinh: from_str!(u64, &expect!(map.remove("CapInh")), 16),
242 capprm: from_str!(u64, &expect!(map.remove("CapPrm")), 16),
243 capeff: from_str!(u64, &expect!(map.remove("CapEff")), 16),
244 capbnd: map.remove("CapBnd").map(|x| Ok(from_str!(u64, &x, 16))).transpose()?,
245 capamb: map.remove("CapAmb").map(|x| Ok(from_str!(u64, &x, 16))).transpose()?,
246 nonewprivs: map.remove("NoNewPrivs").map(|x| Ok(from_str!(u64, &x))).transpose()?,
247 seccomp: map.remove("Seccomp").map(|x| Ok(from_str!(u32, &x))).transpose()?,
248 speculation_store_bypass: map.remove("Speculation_Store_Bypass"),
249 cpus_allowed: map
250 .remove("Cpus_allowed")
251 .map(|x| Status::parse_allowed(&x))
252 .transpose()?,
253 cpus_allowed_list: map
254 .remove("Cpus_allowed_list")
255 .and_then(|x| Status::parse_allowed_list(&x).ok()),
256 mems_allowed: map
257 .remove("Mems_allowed")
258 .map(|x| Status::parse_allowed(&x))
259 .transpose()?,
260 mems_allowed_list: map
261 .remove("Mems_allowed_list")
262 .and_then(|x| Status::parse_allowed_list(&x).ok()),
263 voluntary_ctxt_switches: map
264 .remove("voluntary_ctxt_switches")
265 .map(|x| Ok(from_str!(u64, &x)))
266 .transpose()?,
267 nonvoluntary_ctxt_switches: map
268 .remove("nonvoluntary_ctxt_switches")
269 .map(|x| Ok(from_str!(u64, &x)))
270 .transpose()?,
271 core_dumping: map.remove("CoreDumping").map(|x| x == "1"),
272 thp_enabled: map.remove("THP_enabled").map(|x| x == "1"),
273 };
274
275 if cfg!(test) && !map.is_empty() {
276 eprintln!("Warning: status map is not empty: {:#?}", map);
279 }
280
281 Ok(status)
282 }
283}
284
285impl Status {
286 fn parse_with_kb<T: FromStrRadix>(s: Option<String>) -> ProcResult<Option<T>> {
287 if let Some(s) = s {
288 Ok(Some(from_str!(T, &s.replace(" kB", ""))))
289 } else {
290 Ok(None)
291 }
292 }
293
294 #[doc(hidden)]
295 pub fn parse_uid_gid(s: &str, i: usize) -> ProcResult<u32> {
296 Ok(from_str!(u32, expect!(s.split_whitespace().nth(i))))
297 }
298
299 fn parse_sigq(s: &str) -> ProcResult<(u64, u64)> {
300 let mut iter = s.split('/');
301 let first = from_str!(u64, expect!(iter.next()));
302 let second = from_str!(u64, expect!(iter.next()));
303 Ok((first, second))
304 }
305
306 fn parse_list<T: FromStrRadix>(s: &str) -> ProcResult<Vec<T>> {
307 let mut ret = Vec::new();
308 for i in s.split_whitespace() {
309 ret.push(from_str!(T, i));
310 }
311 Ok(ret)
312 }
313
314 fn parse_allowed(s: &str) -> ProcResult<Vec<u32>> {
315 let mut ret = Vec::new();
316 for i in s.split(',') {
317 ret.push(from_str!(u32, i, 16));
318 }
319 Ok(ret)
320 }
321
322 fn parse_allowed_list(s: &str) -> ProcResult<Vec<(u32, u32)>> {
323 let mut ret = Vec::new();
324 for s in s.split(',') {
325 if s.contains('-') {
326 let mut s = s.split('-');
327 let beg = from_str!(u32, expect!(s.next()));
328 if let Some(x) = s.next() {
329 let end = from_str!(u32, x);
330 ret.push((beg, end));
331 }
332 } else {
333 let beg = from_str!(u32, s);
334 let end = from_str!(u32, s);
335 ret.push((beg, end));
336 }
337 }
338 Ok(ret)
339 }
340}