1use super::*;
7use crate::from_iter;
8
9#[cfg(feature = "serde1")]
10use serde::{Deserialize, Serialize};
11use std::io::Read;
12use std::path::PathBuf;
13use std::str::FromStr;
14
15mod limit;
16pub use limit::*;
17
18mod stat;
19pub use stat::*;
20
21mod mount;
22pub use mount::*;
23
24mod namespaces;
25pub use namespaces::*;
26
27mod status;
28pub use status::*;
29
30mod schedstat;
31pub use schedstat::*;
32
33mod smaps_rollup;
34pub use smaps_rollup::*;
35
36mod pagemap;
37pub use pagemap::*;
38
39mod clear_refs;
40pub use clear_refs::*;
41
42bitflags! {
43 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
47 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
48 pub struct StatFlags: u32 {
49 const PF_IDLE = 0x0000_0002;
51 const PF_EXITING = 0x0000_0004;
53 const PF_EXITPIDONE = 0x0000_0008;
55 const PF_VCPU = 0x0000_0010;
57 const PF_WQ_WORKER = 0x0000_0020;
59 const PF_FORKNOEXEC = 0x0000_0040;
61 const PF_MCE_PROCESS = 0x0000_0080;
63 const PF_SUPERPRIV = 0x0000_0100;
65 const PF_DUMPCORE = 0x0000_0200;
67 const PF_SIGNALED = 0x0000_0400;
69 const PF_MEMALLOC = 0x0000_0800;
71 const PF_NPROC_EXCEEDED = 0x0000_1000;
73 const PF_USED_MATH = 0x0000_2000;
75 const PF_USED_ASYNC = 0x0000_4000;
77 const PF_NOFREEZE = 0x0000_8000;
79 const PF_FROZEN = 0x0001_0000;
81 const PF_KSWAPD = 0x0002_0000;
83 const PF_MEMALLOC_NOFS = 0x0004_0000;
85 const PF_MEMALLOC_NOIO = 0x0008_0000;
87 const PF_LESS_THROTTLE = 0x0010_0000;
89 const PF_KTHREAD = 0x0020_0000;
91 const PF_RANDOMIZE = 0x0040_0000;
93 const PF_SWAPWRITE = 0x0080_0000;
95 const PF_MEMSTALL = 0x0100_0000;
97 const PF_UMH = 0x0200_0000;
99 const PF_NO_SETAFFINITY = 0x0400_0000;
101 const PF_MCE_EARLY = 0x0800_0000;
103 const PF_MEMALLOC_NOCMA = 0x1000_0000;
105 const PF_MUTEX_TESTER = 0x2000_0000;
107 const PF_FREEZER_SKIP = 0x4000_0000;
109 const PF_SUSPEND_TASK = 0x8000_0000;
111
112 }
113}
114
115bitflags! {
116 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
118 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
119 pub struct CoredumpFlags: u32 {
120 const ANONYMOUS_PRIVATE_MAPPINGS = 0x01;
121 const ANONYMOUS_SHARED_MAPPINGS = 0x02;
122 const FILEBACKED_PRIVATE_MAPPINGS = 0x04;
123 const FILEBACKED_SHARED_MAPPINGS = 0x08;
124 const ELF_HEADERS = 0x10;
125 const PROVATE_HUGEPAGES = 0x20;
126 const SHARED_HUGEPAGES = 0x40;
127 const PRIVATE_DAX_PAGES = 0x80;
128 const SHARED_DAX_PAGES = 0x100;
129 }
130}
131
132bitflags! {
133 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
139 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
140 pub struct MMPermissions: u8 {
141 const NONE = 0;
143 const READ = 1 << 0;
145 const WRITE = 1 << 1;
147 const EXECUTE = 1 << 2;
149 const SHARED = 1 << 3;
153 const PRIVATE = 1 << 4;
157 }
158}
159
160impl MMPermissions {
161 fn from_ascii_char(b: u8) -> Self {
162 match b {
163 b'r' => Self::READ,
164 b'w' => Self::WRITE,
165 b'x' => Self::EXECUTE,
166 b's' => Self::SHARED,
167 b'p' => Self::PRIVATE,
168 _ => Self::NONE,
169 }
170 }
171 pub fn as_str(&self) -> String {
177 let mut s = String::with_capacity(4);
178 s.push(if self.contains(Self::READ) { 'r' } else { '-' });
179 s.push(if self.contains(Self::WRITE) { 'w' } else { '-' });
180 s.push(if self.contains(Self::EXECUTE) { 'x' } else { '-' });
181 s.push(if self.contains(Self::SHARED) {
182 's'
183 } else if self.contains(Self::PRIVATE) {
184 'p'
185 } else {
186 '-'
187 });
188
189 s
190 }
191}
192
193impl FromStr for MMPermissions {
194 type Err = std::convert::Infallible;
195
196 fn from_str(s: &str) -> Result<Self, Self::Err> {
197 Ok(s.bytes()
199 .map(Self::from_ascii_char)
200 .fold(Self::default(), std::ops::BitOr::bitor))
201 }
202}
203
204bitflags! {
205 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
208 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
209 pub struct VmFlags: u32 {
210 const NONE = 0;
212 const RD = 1 << 0;
214 const WR = 1 << 1;
216 const EX = 1 << 2;
218 const SH = 1 << 3;
220 const MR = 1 << 4;
222 const MW = 1 << 5;
224 const ME = 1 << 6;
226 const MS = 1 << 7;
228 const GD = 1 << 8;
230 const PF = 1 << 9;
232 const DW = 1 << 10;
234 const LO = 1 << 11;
236 const IO = 1 << 12;
238 const SR = 1 << 13;
240 const RR = 1 << 14;
242 const DC = 1 << 15;
244 const DE = 1 << 16;
246 const AC = 1 << 17;
248 const NR = 1 << 18;
250 const HT = 1 << 19;
252 const SF = 1 << 20;
254 const NL = 1 << 21;
256 const AR = 1 << 22;
258 const WF = 1 << 23;
260 const DD = 1 << 24;
262 const SD = 1 << 25;
264 const MM = 1 << 26;
266 const HG = 1 << 27;
268 const NH = 1 << 28;
270 const MG = 1 << 29;
272 const UM = 1 << 30;
274 const UW = 1 << 31;
276 }
277}
278
279impl VmFlags {
280 fn from_str(flag: &str) -> Self {
281 if flag.len() != 2 {
282 return VmFlags::NONE;
283 }
284
285 match flag {
286 "rd" => VmFlags::RD,
287 "wr" => VmFlags::WR,
288 "ex" => VmFlags::EX,
289 "sh" => VmFlags::SH,
290 "mr" => VmFlags::MR,
291 "mw" => VmFlags::MW,
292 "me" => VmFlags::ME,
293 "ms" => VmFlags::MS,
294 "gd" => VmFlags::GD,
295 "pf" => VmFlags::PF,
296 "dw" => VmFlags::DW,
297 "lo" => VmFlags::LO,
298 "io" => VmFlags::IO,
299 "sr" => VmFlags::SR,
300 "rr" => VmFlags::RR,
301 "dc" => VmFlags::DC,
302 "de" => VmFlags::DE,
303 "ac" => VmFlags::AC,
304 "nr" => VmFlags::NR,
305 "ht" => VmFlags::HT,
306 "sf" => VmFlags::SF,
307 "nl" => VmFlags::NL,
308 "ar" => VmFlags::AR,
309 "wf" => VmFlags::WF,
310 "dd" => VmFlags::DD,
311 "sd" => VmFlags::SD,
312 "mm" => VmFlags::MM,
313 "hg" => VmFlags::HG,
314 "nh" => VmFlags::NH,
315 "mg" => VmFlags::MG,
316 "um" => VmFlags::UM,
317 "uw" => VmFlags::UW,
318 _ => VmFlags::NONE,
319 }
320 }
321}
322
323#[derive(Debug, Clone, Copy, Eq, PartialEq)]
325pub enum ProcState {
326 Running,
328 Sleeping,
330 Waiting,
332 Zombie,
334 Stopped,
338 Tracing,
340 Dead,
342 Wakekill,
344 Waking,
346 Parked,
348 Idle,
350}
351
352impl ProcState {
353 pub fn from_char(c: char) -> Option<ProcState> {
354 match c {
355 'R' => Some(ProcState::Running),
356 'S' => Some(ProcState::Sleeping),
357 'D' => Some(ProcState::Waiting),
358 'Z' => Some(ProcState::Zombie),
359 'T' => Some(ProcState::Stopped),
360 't' => Some(ProcState::Tracing),
361 'X' | 'x' => Some(ProcState::Dead),
362 'K' => Some(ProcState::Wakekill),
363 'W' => Some(ProcState::Waking),
364 'P' => Some(ProcState::Parked),
365 'I' => Some(ProcState::Idle),
366 _ => None,
367 }
368 }
369}
370
371impl FromStr for ProcState {
372 type Err = ProcError;
373 fn from_str(s: &str) -> Result<ProcState, ProcError> {
374 ProcState::from_char(expect!(s.chars().next(), "empty string"))
375 .ok_or_else(|| build_internal_error!("failed to convert"))
376 }
377}
378
379#[derive(Debug, Copy, Clone)]
387#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
388pub struct Io {
389 pub rchar: u64,
396
397 pub wchar: u64,
402 pub syscr: u64,
407 pub syscw: u64,
412 pub read_bytes: u64,
417 pub write_bytes: u64,
421 pub cancelled_write_bytes: u64,
430}
431
432#[derive(Debug, PartialEq, Eq, Clone, Hash)]
433#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
434pub enum MMapPath {
435 Path(PathBuf),
437 Heap,
439 Stack,
441 TStack(u32),
446 Vdso,
448 Vvar,
450 Vsyscall,
452 Rollup,
454 Anonymous,
456 Vsys(i32),
458 Other(String),
460}
461
462impl MMapPath {
463 pub fn from(path: &str) -> ProcResult<MMapPath> {
464 Ok(match path.trim() {
465 "" => MMapPath::Anonymous,
466 "[heap]" => MMapPath::Heap,
467 "[stack]" => MMapPath::Stack,
468 "[vdso]" => MMapPath::Vdso,
469 "[vvar]" => MMapPath::Vvar,
470 "[vsyscall]" => MMapPath::Vsyscall,
471 "[rollup]" => MMapPath::Rollup,
472 x if x.starts_with("[stack:") => {
473 let mut s = x[1..x.len() - 1].split(':');
474 let tid = from_str!(u32, expect!(s.nth(1)));
475 MMapPath::TStack(tid)
476 }
477 x if x.starts_with('[') && x.ends_with(']') => MMapPath::Other(x[1..x.len() - 1].to_string()),
478 x if x.starts_with("/SYSV") => MMapPath::Vsys(u32::from_str_radix(&x[5..13], 16)? as i32), x => MMapPath::Path(PathBuf::from(x)),
480 })
481 }
482}
483
484#[derive(Debug, PartialEq, Eq, Clone)]
486#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
487#[non_exhaustive]
488pub struct MemoryMaps(pub Vec<MemoryMap>);
489
490impl crate::FromBufRead for MemoryMaps {
491 fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> {
493 let mut memory_maps = Vec::new();
494
495 let mut line_iter = reader.lines().map(|r| r.map_err(|_| ProcError::Incomplete(None)));
496 let mut current_memory_map: Option<MemoryMap> = None;
497 while let Some(line) = line_iter.next().transpose()? {
498 if line.starts_with(|c: char| c.is_ascii_uppercase()) {
501 match current_memory_map.as_mut() {
502 None => return Err(ProcError::Incomplete(None)),
503 Some(mm) => {
504 if line.starts_with("VmFlags") {
506 let flags = line.split_ascii_whitespace();
507 let flags = flags.skip(1); let flags = flags
510 .map(VmFlags::from_str)
511 .fold(VmFlags::NONE, std::ops::BitOr::bitor);
513
514 mm.extension.vm_flags = flags;
515 } else {
516 let mut parts = line.split_ascii_whitespace();
517
518 let key = parts.next();
519 let value = parts.next();
520
521 if let (Some(k), Some(v)) = (key, value) {
522 let size_suffix = parts.next();
524
525 let size_multiplier = if size_suffix.is_some() { 1024 } else { 1 };
530
531 let v = v.parse::<u64>().map_err(|_| {
532 ProcError::Other("Value in `Key: Value` pair was not actually a number".into())
533 })?;
534
535 let k = k.trim_end_matches(':');
537
538 mm.extension.map.insert(k.into(), v * size_multiplier);
539 }
540 }
541 }
542 }
543 } else {
544 if let Some(mm) = current_memory_map.take() {
545 memory_maps.push(mm);
546 }
547 current_memory_map = Some(MemoryMap::from_line(&line)?);
548 }
549 }
550 if let Some(mm) = current_memory_map.take() {
551 memory_maps.push(mm);
552 }
553
554 Ok(MemoryMaps(memory_maps))
555 }
556}
557
558impl MemoryMaps {
559 pub fn iter(&self) -> std::slice::Iter<MemoryMap> {
561 self.0.iter()
562 }
563
564 pub fn len(&self) -> usize {
565 self.0.len()
566 }
567}
568
569impl<'a> IntoIterator for &'a MemoryMaps {
570 type IntoIter = std::slice::Iter<'a, MemoryMap>;
571 type Item = &'a MemoryMap;
572
573 fn into_iter(self) -> Self::IntoIter {
574 self.iter()
575 }
576}
577
578impl IntoIterator for MemoryMaps {
579 type IntoIter = std::vec::IntoIter<MemoryMap>;
580 type Item = MemoryMap;
581
582 fn into_iter(self) -> Self::IntoIter {
583 self.0.into_iter()
584 }
585}
586
587#[derive(Debug, PartialEq, Eq, Clone)]
589#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
590pub struct MemoryMap {
591 pub address: (u64, u64),
593 pub perms: MMPermissions,
594 pub offset: u64,
596 pub dev: (i32, i32),
598 pub inode: u64,
603 pub pathname: MMapPath,
604 pub extension: MMapExtension,
608}
609
610impl MemoryMap {
611 fn from_line(line: &str) -> ProcResult<MemoryMap> {
612 let mut s = line.splitn(6, ' ');
613 let address = expect!(s.next());
614 let perms = expect!(s.next());
615 let offset = expect!(s.next());
616 let dev = expect!(s.next());
617 let inode = expect!(s.next());
618 let path = expect!(s.next());
619
620 Ok(MemoryMap {
621 address: split_into_num(address, '-', 16)?,
622 perms: perms.parse()?,
623 offset: from_str!(u64, offset, 16),
624 dev: split_into_num(dev, ':', 16)?,
625 inode: from_str!(u64, inode),
626 pathname: MMapPath::from(path)?,
627 extension: Default::default(),
628 })
629 }
630}
631
632#[derive(Default, Debug, PartialEq, Eq, Clone)]
634#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
635pub struct MMapExtension {
636 pub map: HashMap<String, u64>,
643 pub vm_flags: VmFlags,
647}
648
649impl MMapExtension {
650 pub fn is_empty(&self) -> bool {
652 self.map.is_empty() && self.vm_flags == VmFlags::NONE
653 }
654}
655
656impl crate::FromBufRead for Io {
657 fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> {
658 let mut map = HashMap::new();
659
660 for line in reader.lines() {
661 let line = line?;
662 if line.is_empty() || !line.contains(' ') {
663 continue;
664 }
665 let mut s = line.split_whitespace();
666 let field = expect!(s.next());
667 let value = expect!(s.next());
668
669 let value = from_str!(u64, value);
670
671 map.insert(field[..field.len() - 1].to_string(), value);
672 }
673 let io = Io {
674 rchar: expect!(map.remove("rchar")),
675 wchar: expect!(map.remove("wchar")),
676 syscr: expect!(map.remove("syscr")),
677 syscw: expect!(map.remove("syscw")),
678 read_bytes: expect!(map.remove("read_bytes")),
679 write_bytes: expect!(map.remove("write_bytes")),
680 cancelled_write_bytes: expect!(map.remove("cancelled_write_bytes")),
681 };
682
683 assert!(!cfg!(test) || map.is_empty(), "io map is not empty: {:#?}", map);
684
685 Ok(io)
686 }
687}
688
689#[derive(Clone, Debug)]
691#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
692pub enum FDTarget {
693 Path(PathBuf),
695 Socket(u64),
697 Net(u64),
698 Pipe(u64),
699 AnonInode(String),
701 MemFD(String),
703 Other(String, u64),
705}
706
707impl FromStr for FDTarget {
708 type Err = ProcError;
709 fn from_str(s: &str) -> Result<FDTarget, ProcError> {
710 fn strip_first_last(s: &str) -> ProcResult<&str> {
712 if s.len() > 2 {
713 let mut c = s.chars();
714 let _ = c.next();
716 let _ = c.next_back();
717 Ok(c.as_str())
718 } else {
719 Err(ProcError::Incomplete(None))
720 }
721 }
722
723 if !s.starts_with('/') && s.contains(':') {
724 let mut s = s.split(':');
725 let fd_type = expect!(s.next());
726 match fd_type {
727 "socket" => {
728 let inode = expect!(s.next(), "socket inode");
729 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
730 Ok(FDTarget::Socket(inode))
731 }
732 "net" => {
733 let inode = expect!(s.next(), "net inode");
734 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
735 Ok(FDTarget::Net(inode))
736 }
737 "pipe" => {
738 let inode = expect!(s.next(), "pipe inode");
739 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
740 Ok(FDTarget::Pipe(inode))
741 }
742 "anon_inode" => Ok(FDTarget::AnonInode(expect!(s.next(), "anon inode").to_string())),
743 "" => Err(ProcError::Incomplete(None)),
744 x => {
745 let inode = expect!(s.next(), "other inode");
746 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
747 Ok(FDTarget::Other(x.to_string(), inode))
748 }
749 }
750 } else if let Some(s) = s.strip_prefix("/memfd:") {
751 Ok(FDTarget::MemFD(s.to_string()))
752 } else {
753 Ok(FDTarget::Path(PathBuf::from(s)))
754 }
755 }
756}
757
758#[derive(Debug, Clone, Copy)]
760#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
761pub struct StatM {
762 pub size: u64,
766 pub resident: u64,
770 pub shared: u64,
774 pub text: u64,
776 pub lib: u64,
778 pub data: u64,
780 pub dt: u64,
782}
783
784impl crate::FromRead for StatM {
785 fn from_read<R: Read>(mut r: R) -> ProcResult<Self> {
786 let mut line = String::new();
787 r.read_to_string(&mut line)?;
788 let mut s = line.split_whitespace();
789
790 let size = expect!(from_iter(&mut s));
791 let resident = expect!(from_iter(&mut s));
792 let shared = expect!(from_iter(&mut s));
793 let text = expect!(from_iter(&mut s));
794 let lib = expect!(from_iter(&mut s));
795 let data = expect!(from_iter(&mut s));
796 let dt = expect!(from_iter(&mut s));
797
798 if cfg!(test) {
799 assert!(s.next().is_none());
800 }
801
802 Ok(StatM {
803 size,
804 resident,
805 shared,
806 text,
807 lib,
808 data,
809 dt,
810 })
811 }
812}
813
814#[cfg(test)]
815mod tests {
816 use super::*;
817
818 #[test]
819 fn parse_memory_map_permissions() {
820 use MMPermissions as P;
821 assert_eq!("rw-p".parse(), Ok(P::READ | P::WRITE | P::PRIVATE));
822 assert_eq!("r-xs".parse(), Ok(P::READ | P::EXECUTE | P::SHARED));
823 assert_eq!("----".parse(), Ok(P::NONE));
824
825 assert_eq!((P::READ | P::WRITE | P::PRIVATE).as_str(), "rw-p");
826 assert_eq!((P::READ | P::EXECUTE | P::SHARED).as_str(), "r-xs");
827 assert_eq!(P::NONE.as_str(), "----");
828 }
829}