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 syscall;
37pub use syscall::*;
38
39mod pagemap;
40pub use pagemap::*;
41
42mod clear_refs;
43pub use clear_refs::*;
44
45bitflags! {
46 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
50 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
51 pub struct StatFlags: u32 {
52 const PF_IDLE = 0x0000_0002;
54 const PF_EXITING = 0x0000_0004;
56 const PF_EXITPIDONE = 0x0000_0008;
58 const PF_VCPU = 0x0000_0010;
60 const PF_WQ_WORKER = 0x0000_0020;
62 const PF_FORKNOEXEC = 0x0000_0040;
64 const PF_MCE_PROCESS = 0x0000_0080;
66 const PF_SUPERPRIV = 0x0000_0100;
68 const PF_DUMPCORE = 0x0000_0200;
70 const PF_SIGNALED = 0x0000_0400;
72 const PF_MEMALLOC = 0x0000_0800;
74 const PF_NPROC_EXCEEDED = 0x0000_1000;
76 const PF_USED_MATH = 0x0000_2000;
78 const PF_USED_ASYNC = 0x0000_4000;
80 const PF_NOFREEZE = 0x0000_8000;
82 const PF_FROZEN = 0x0001_0000;
84 const PF_KSWAPD = 0x0002_0000;
86 const PF_MEMALLOC_NOFS = 0x0004_0000;
88 const PF_MEMALLOC_NOIO = 0x0008_0000;
90 const PF_LESS_THROTTLE = 0x0010_0000;
92 const PF_KTHREAD = 0x0020_0000;
94 const PF_RANDOMIZE = 0x0040_0000;
96 const PF_SWAPWRITE = 0x0080_0000;
98 const PF_MEMSTALL = 0x0100_0000;
100 const PF_UMH = 0x0200_0000;
102 const PF_NO_SETAFFINITY = 0x0400_0000;
104 const PF_MCE_EARLY = 0x0800_0000;
106 const PF_MEMALLOC_NOCMA = 0x1000_0000;
108 const PF_MUTEX_TESTER = 0x2000_0000;
110 const PF_FREEZER_SKIP = 0x4000_0000;
112 const PF_SUSPEND_TASK = 0x8000_0000;
114
115 }
116}
117
118bitflags! {
119 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
121 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
122 pub struct CoredumpFlags: u32 {
123 const ANONYMOUS_PRIVATE_MAPPINGS = 0x01;
124 const ANONYMOUS_SHARED_MAPPINGS = 0x02;
125 const FILEBACKED_PRIVATE_MAPPINGS = 0x04;
126 const FILEBACKED_SHARED_MAPPINGS = 0x08;
127 const ELF_HEADERS = 0x10;
128 const PROVATE_HUGEPAGES = 0x20;
129 const SHARED_HUGEPAGES = 0x40;
130 const PRIVATE_DAX_PAGES = 0x80;
131 const SHARED_DAX_PAGES = 0x100;
132 }
133}
134
135bitflags! {
136 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
142 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
143 pub struct MMPermissions: u8 {
144 const NONE = 0;
146 const READ = 1 << 0;
148 const WRITE = 1 << 1;
150 const EXECUTE = 1 << 2;
152 const SHARED = 1 << 3;
156 const PRIVATE = 1 << 4;
160 }
161}
162
163impl MMPermissions {
164 fn from_ascii_char(b: u8) -> Self {
165 match b {
166 b'r' => Self::READ,
167 b'w' => Self::WRITE,
168 b'x' => Self::EXECUTE,
169 b's' => Self::SHARED,
170 b'p' => Self::PRIVATE,
171 _ => Self::NONE,
172 }
173 }
174 pub fn as_str(&self) -> String {
180 let mut s = String::with_capacity(4);
181 s.push(if self.contains(Self::READ) { 'r' } else { '-' });
182 s.push(if self.contains(Self::WRITE) { 'w' } else { '-' });
183 s.push(if self.contains(Self::EXECUTE) { 'x' } else { '-' });
184 s.push(if self.contains(Self::SHARED) {
185 's'
186 } else if self.contains(Self::PRIVATE) {
187 'p'
188 } else {
189 '-'
190 });
191
192 s
193 }
194}
195
196impl FromStr for MMPermissions {
197 type Err = std::convert::Infallible;
198
199 fn from_str(s: &str) -> Result<Self, Self::Err> {
200 Ok(s.bytes()
202 .map(Self::from_ascii_char)
203 .fold(Self::default(), std::ops::BitOr::bitor))
204 }
205}
206
207bitflags! {
208 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
211 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
212 pub struct VmFlags: u32 {
213 const NONE = 0;
215 const RD = 1 << 0;
217 const WR = 1 << 1;
219 const EX = 1 << 2;
221 const SH = 1 << 3;
223 const MR = 1 << 4;
225 const MW = 1 << 5;
227 const ME = 1 << 6;
229 const MS = 1 << 7;
231 const GD = 1 << 8;
233 const PF = 1 << 9;
235 const DW = 1 << 10;
237 const LO = 1 << 11;
239 const IO = 1 << 12;
241 const SR = 1 << 13;
243 const RR = 1 << 14;
245 const DC = 1 << 15;
247 const DE = 1 << 16;
249 const AC = 1 << 17;
251 const NR = 1 << 18;
253 const HT = 1 << 19;
255 const SF = 1 << 20;
257 const NL = 1 << 21;
259 const AR = 1 << 22;
261 const WF = 1 << 23;
263 const DD = 1 << 24;
265 const SD = 1 << 25;
267 const MM = 1 << 26;
269 const HG = 1 << 27;
271 const NH = 1 << 28;
273 const MG = 1 << 29;
275 const UM = 1 << 30;
277 const UW = 1 << 31;
279 }
280}
281
282impl VmFlags {
283 fn from_str(flag: &str) -> Self {
284 if flag.len() != 2 {
285 return VmFlags::NONE;
286 }
287
288 match flag {
289 "rd" => VmFlags::RD,
290 "wr" => VmFlags::WR,
291 "ex" => VmFlags::EX,
292 "sh" => VmFlags::SH,
293 "mr" => VmFlags::MR,
294 "mw" => VmFlags::MW,
295 "me" => VmFlags::ME,
296 "ms" => VmFlags::MS,
297 "gd" => VmFlags::GD,
298 "pf" => VmFlags::PF,
299 "dw" => VmFlags::DW,
300 "lo" => VmFlags::LO,
301 "io" => VmFlags::IO,
302 "sr" => VmFlags::SR,
303 "rr" => VmFlags::RR,
304 "dc" => VmFlags::DC,
305 "de" => VmFlags::DE,
306 "ac" => VmFlags::AC,
307 "nr" => VmFlags::NR,
308 "ht" => VmFlags::HT,
309 "sf" => VmFlags::SF,
310 "nl" => VmFlags::NL,
311 "ar" => VmFlags::AR,
312 "wf" => VmFlags::WF,
313 "dd" => VmFlags::DD,
314 "sd" => VmFlags::SD,
315 "mm" => VmFlags::MM,
316 "hg" => VmFlags::HG,
317 "nh" => VmFlags::NH,
318 "mg" => VmFlags::MG,
319 "um" => VmFlags::UM,
320 "uw" => VmFlags::UW,
321 _ => VmFlags::NONE,
322 }
323 }
324}
325
326#[derive(Debug, Clone, Copy, Eq, PartialEq)]
328pub enum ProcState {
329 Running,
331 Sleeping,
333 Waiting,
335 Zombie,
337 Stopped,
341 Tracing,
343 Dead,
345 Wakekill,
347 Waking,
349 Parked,
351 Idle,
353}
354
355impl ProcState {
356 pub fn from_char(c: char) -> Option<ProcState> {
357 match c {
358 'R' => Some(ProcState::Running),
359 'S' => Some(ProcState::Sleeping),
360 'D' => Some(ProcState::Waiting),
361 'Z' => Some(ProcState::Zombie),
362 'T' => Some(ProcState::Stopped),
363 't' => Some(ProcState::Tracing),
364 'X' | 'x' => Some(ProcState::Dead),
365 'K' => Some(ProcState::Wakekill),
366 'W' => Some(ProcState::Waking),
367 'P' => Some(ProcState::Parked),
368 'I' => Some(ProcState::Idle),
369 _ => None,
370 }
371 }
372}
373
374impl FromStr for ProcState {
375 type Err = ProcError;
376 fn from_str(s: &str) -> Result<ProcState, ProcError> {
377 ProcState::from_char(expect!(s.chars().next(), "empty string"))
378 .ok_or_else(|| build_internal_error!("failed to convert"))
379 }
380}
381
382#[derive(Debug, Copy, Clone)]
390#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
391pub struct Io {
392 pub rchar: u64,
399
400 pub wchar: u64,
405 pub syscr: u64,
410 pub syscw: u64,
415 pub read_bytes: u64,
420 pub write_bytes: u64,
424 pub cancelled_write_bytes: u64,
433}
434
435#[derive(Debug, PartialEq, Eq, Clone, Hash)]
436#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
437pub enum MMapPath {
438 Path(PathBuf),
440 Heap,
442 Stack,
444 TStack(u32),
449 Vdso,
451 Vvar,
453 Vsyscall,
455 Rollup,
457 Anonymous,
459 Vsys(i32),
461 Other(String),
463}
464
465impl MMapPath {
466 pub fn from(path: &str) -> ProcResult<MMapPath> {
467 Ok(match path.trim() {
468 "" => MMapPath::Anonymous,
469 "[heap]" => MMapPath::Heap,
470 "[stack]" => MMapPath::Stack,
471 "[vdso]" => MMapPath::Vdso,
472 "[vvar]" => MMapPath::Vvar,
473 "[vsyscall]" => MMapPath::Vsyscall,
474 "[rollup]" => MMapPath::Rollup,
475 x if x.starts_with("[stack:") => {
476 let mut s = x[1..x.len() - 1].split(':');
477 let tid = from_str!(u32, expect!(s.nth(1)));
478 MMapPath::TStack(tid)
479 }
480 x if x.starts_with('[') && x.ends_with(']') => MMapPath::Other(x[1..x.len() - 1].to_string()),
481 x if x.starts_with("/SYSV") => MMapPath::Vsys(u32::from_str_radix(&x[5..13], 16)? as i32), x => MMapPath::Path(PathBuf::from(x)),
483 })
484 }
485}
486
487#[derive(Debug, PartialEq, Eq, Clone)]
489#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
490#[non_exhaustive]
491pub struct MemoryMaps(pub Vec<MemoryMap>);
492
493impl crate::FromBufRead for MemoryMaps {
494 fn from_buf_read<R: BufRead>(mut reader: R) -> ProcResult<Self> {
496 let mut memory_maps = Vec::with_capacity(10);
497 let mut current_memory_map: Option<MemoryMap> = None;
498 let mut line = String::with_capacity(100);
499
500 loop {
501 line.clear();
502 match reader.read_line(&mut line) {
503 Ok(0) => break,
505 Ok(_) => {}
506 Err(_) => return Err(ProcError::Incomplete(None)),
507 }
508
509 if line.is_empty() {
510 break;
511 }
512
513 if line.starts_with(|c: char| c.is_ascii_uppercase()) {
516 match current_memory_map.as_mut() {
517 None => return Err(ProcError::Incomplete(None)),
518 Some(mm) => {
519 if line.starts_with("VmFlags") {
521 let flags = line.split_ascii_whitespace();
522 let flags = flags.skip(1); let flags = flags
525 .map(VmFlags::from_str)
526 .fold(VmFlags::NONE, std::ops::BitOr::bitor);
528
529 mm.extension.vm_flags = flags;
530 } else {
531 let mut parts = line.split_ascii_whitespace();
532
533 let key = parts.next();
534 let value = parts.next();
535
536 if let (Some(k), Some(v)) = (key, value) {
537 let size_suffix = parts.next();
539
540 let size_multiplier = if size_suffix.is_some() { 1024 } else { 1 };
545
546 let v = v.parse::<u64>().map_err(|_| {
547 ProcError::Other("Value in `Key: Value` pair was not actually a number".into())
548 })?;
549
550 let k = k.trim_end_matches(':');
552
553 mm.extension.map.insert(k.into(), v * size_multiplier);
554 }
555 }
556 }
557 }
558 } else {
559 if let Some(mm) = current_memory_map.take() {
560 memory_maps.push(mm);
561 }
562 current_memory_map = Some(MemoryMap::from_line(&line)?);
563 }
564 }
565 if let Some(mm) = current_memory_map.take() {
566 memory_maps.push(mm);
567 }
568
569 Ok(MemoryMaps(memory_maps))
570 }
571}
572
573impl MemoryMaps {
574 pub fn iter(&self) -> std::slice::Iter<MemoryMap> {
576 self.0.iter()
577 }
578
579 pub fn len(&self) -> usize {
580 self.0.len()
581 }
582}
583
584impl<'a> IntoIterator for &'a MemoryMaps {
585 type IntoIter = std::slice::Iter<'a, MemoryMap>;
586 type Item = &'a MemoryMap;
587
588 fn into_iter(self) -> Self::IntoIter {
589 self.iter()
590 }
591}
592
593impl IntoIterator for MemoryMaps {
594 type IntoIter = std::vec::IntoIter<MemoryMap>;
595 type Item = MemoryMap;
596
597 fn into_iter(self) -> Self::IntoIter {
598 self.0.into_iter()
599 }
600}
601
602#[derive(Debug, PartialEq, Eq, Clone)]
604#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
605pub struct MemoryMap {
606 pub address: (u64, u64),
608 pub perms: MMPermissions,
609 pub offset: u64,
611 pub dev: (i32, i32),
613 pub inode: u64,
618 pub pathname: MMapPath,
619 pub extension: MMapExtension,
623}
624
625impl MemoryMap {
626 fn from_line(line: &str) -> ProcResult<MemoryMap> {
627 let mut s = line.splitn(6, ' ');
628 let address = expect!(s.next());
629 let perms = expect!(s.next());
630 let offset = expect!(s.next());
631 let dev = expect!(s.next());
632 let inode = expect!(s.next());
633 let path = expect!(s.next());
634
635 Ok(MemoryMap {
636 address: split_into_num(address, '-', 16)?,
637 perms: perms.parse()?,
638 offset: from_str!(u64, offset, 16),
639 dev: split_into_num(dev, ':', 16)?,
640 inode: from_str!(u64, inode),
641 pathname: MMapPath::from(path)?,
642 extension: Default::default(),
643 })
644 }
645}
646
647#[derive(Default, Debug, PartialEq, Eq, Clone)]
649#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
650pub struct MMapExtension {
651 pub map: HashMap<String, u64>,
658 pub vm_flags: VmFlags,
662}
663
664impl MMapExtension {
665 pub fn is_empty(&self) -> bool {
667 self.map.is_empty() && self.vm_flags == VmFlags::NONE
668 }
669}
670
671impl crate::FromBufRead for Io {
672 fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> {
673 let mut map = HashMap::new();
674
675 for line in reader.lines() {
676 let line = line?;
677 if line.is_empty() || !line.contains(' ') {
678 continue;
679 }
680 let mut s = line.split_whitespace();
681 let field = expect!(s.next());
682 let value = expect!(s.next());
683
684 let value = from_str!(u64, value);
685
686 map.insert(field[..field.len() - 1].to_string(), value);
687 }
688 let io = Io {
689 rchar: expect!(map.remove("rchar")),
690 wchar: expect!(map.remove("wchar")),
691 syscr: expect!(map.remove("syscr")),
692 syscw: expect!(map.remove("syscw")),
693 read_bytes: expect!(map.remove("read_bytes")),
694 write_bytes: expect!(map.remove("write_bytes")),
695 cancelled_write_bytes: expect!(map.remove("cancelled_write_bytes")),
696 };
697
698 assert!(!cfg!(test) || map.is_empty(), "io map is not empty: {:#?}", map);
699
700 Ok(io)
701 }
702}
703
704#[derive(Clone, Debug, PartialEq, Eq)]
706#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
707pub enum FDTarget {
708 Path(PathBuf),
710 Socket(u64),
712 Net(u64),
713 Pipe(u64),
714 AnonInode(String),
716 MemFD(String),
718 Other(String, u64),
720 Unknown(String, String),
722}
723
724impl FromStr for FDTarget {
725 type Err = ProcError;
726 fn from_str(s: &str) -> Result<FDTarget, ProcError> {
727 fn strip_first_last(s: &str) -> ProcResult<&str> {
729 if s.len() > 2 {
730 let mut c = s.chars();
731 let _ = c.next();
733 let _ = c.next_back();
734 Ok(c.as_str())
735 } else {
736 Err(ProcError::Incomplete(None))
737 }
738 }
739
740 if !s.starts_with('/') && s.contains(':') {
741 let mut s = s.splitn(2, ':');
742 let fd_type = expect!(s.next());
743 match fd_type {
744 "socket" => {
745 let inode = expect!(s.next(), "socket inode");
746 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
747 Ok(FDTarget::Socket(inode))
748 }
749 "net" => {
750 let inode = expect!(s.next(), "net inode");
751 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
752 Ok(FDTarget::Net(inode))
753 }
754 "pipe" => {
755 let inode = expect!(s.next(), "pipe inode");
756 let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
757 Ok(FDTarget::Pipe(inode))
758 }
759 "anon_inode" => Ok(FDTarget::AnonInode(expect!(s.next(), "anon inode").to_string())),
760 "" => Err(ProcError::Incomplete(None)),
761 x => {
762 let inode = expect!(s.next(), "other inode");
763 if inode.starts_with('[') && inode.ends_with(']') {
766 if let Ok(inode) = u64::from_str_radix(strip_first_last(inode)?, 10) {
767 return Ok(FDTarget::Other(x.to_string(), inode));
768 }
769 }
770 Ok(FDTarget::Unknown(x.to_string(), inode.to_string()))
771 }
772 }
773 } else if let Some(s) = s.strip_prefix("/memfd:") {
774 Ok(FDTarget::MemFD(s.to_string()))
775 } else {
776 Ok(FDTarget::Path(PathBuf::from(s)))
777 }
778 }
779}
780
781#[derive(Debug, Clone, Copy)]
783#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
784pub struct StatM {
785 pub size: u64,
789 pub resident: u64,
793 pub shared: u64,
797 pub text: u64,
799 pub lib: u64,
801 pub data: u64,
803 pub dt: u64,
805}
806
807impl crate::FromRead for StatM {
808 fn from_read<R: Read>(mut r: R) -> ProcResult<Self> {
809 let mut line = String::new();
810 r.read_to_string(&mut line)?;
811 let mut s = line.split_whitespace();
812
813 let size = expect!(from_iter(&mut s));
814 let resident = expect!(from_iter(&mut s));
815 let shared = expect!(from_iter(&mut s));
816 let text = expect!(from_iter(&mut s));
817 let lib = expect!(from_iter(&mut s));
818 let data = expect!(from_iter(&mut s));
819 let dt = expect!(from_iter(&mut s));
820
821 if cfg!(test) {
822 assert!(s.next().is_none());
823 }
824
825 Ok(StatM {
826 size,
827 resident,
828 shared,
829 text,
830 lib,
831 data,
832 dt,
833 })
834 }
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840
841 #[test]
842 fn parse_memory_map_permissions() {
843 use MMPermissions as P;
844 assert_eq!("rw-p".parse(), Ok(P::READ | P::WRITE | P::PRIVATE));
845 assert_eq!("r-xs".parse(), Ok(P::READ | P::EXECUTE | P::SHARED));
846 assert_eq!("----".parse(), Ok(P::NONE));
847
848 assert_eq!((P::READ | P::WRITE | P::PRIVATE).as_str(), "rw-p");
849 assert_eq!((P::READ | P::EXECUTE | P::SHARED).as_str(), "r-xs");
850 assert_eq!(P::NONE.as_str(), "----");
851 }
852}