procfs_core/process/
status.rs

1use crate::{FromStrRadix, ProcResult};
2use std::collections::HashMap;
3use std::io::BufRead;
4
5#[cfg(feature = "serde1")]
6use serde::{Deserialize, Serialize};
7
8/// Status information about the process, based on the `/proc/<pid>/status` file.
9///
10/// Not all fields are available in every kernel.  These fields have `Option<T>` types.
11/// In general, the current kernel version will tell you what fields you can expect, but this
12/// isn't totally reliable, since some kernels might backport certain fields, or fields might
13/// only be present if certain kernel configuration options are enabled.  Be prepared to
14/// handle `None` values.
15///
16/// New fields to this struct may be added at any time (even without a major or minor semver bump).
17#[derive(Debug, Clone)]
18#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
19#[non_exhaustive]
20pub struct Status {
21    /// Command run by this process.
22    pub name: String,
23    /// Process umask, expressed in octal with a leading zero; see umask(2).  (Since Linux 4.7.)
24    pub umask: Option<u32>,
25    /// Current state of the process.
26    pub state: String,
27    /// Thread group ID (i.e., Process ID).
28    pub tgid: i32,
29    /// NUMA group ID (0 if none; since Linux 3.13).
30    pub ngid: Option<i32>,
31    /// Thread ID (see gettid(2)).
32    pub pid: i32,
33    /// PID of parent process.
34    pub ppid: i32,
35    /// PID of process tracing this process (0 if not being traced).
36    pub tracerpid: i32,
37    /// Real UID.
38    pub ruid: u32,
39    /// Effective UID.
40    pub euid: u32,
41    /// Saved set UID.
42    pub suid: u32,
43    /// Filesystem UID.
44    pub fuid: u32,
45    /// Real GID.
46    pub rgid: u32,
47    /// Effective GID.
48    pub egid: u32,
49    /// Saved set GID.
50    pub sgid: u32,
51    /// Filesystem GID.
52    pub fgid: u32,
53    /// Number of file descriptor slots currently allocated.
54    pub fdsize: u32,
55    /// Supplementary group list.
56    pub groups: Vec<u32>,
57    /// Thread group ID (i.e., PID) in each of the PID
58    /// namespaces of which (pid)[struct.Status.html#structfield.pid] is a member.  The leftmost entry
59    /// shows the value with respect to the PID namespace of the
60    /// reading process, followed by the value in successively
61    /// nested inner namespaces.  (Since Linux 4.1.)
62    pub nstgid: Option<Vec<i32>>,
63    /// Thread ID in each of the PID namespaces of which
64    /// (pid)[struct.Status.html#structfield.pid] is a member.  The fields are ordered as for NStgid.
65    /// (Since Linux 4.1.)
66    pub nspid: Option<Vec<i32>>,
67    /// Process group ID in each of the PID namespaces of
68    /// which (pid)[struct.Status.html#structfield.pid] is a member.  The fields are ordered as for NStgid.  (Since Linux 4.1.)
69    pub nspgid: Option<Vec<i32>>,
70    /// NSsid: descendant namespace session ID hierarchy Session ID
71    /// in each of the PID namespaces of which (pid)[struct.Status.html#structfield.pid] is a member.
72    /// The fields are ordered as for NStgid.  (Since Linux 4.1.)
73    pub nssid: Option<Vec<i32>>,
74    /// Peak virtual memory size by kibibytes.
75    pub vmpeak: Option<u64>,
76    /// Virtual memory size by kibibytes.
77    pub vmsize: Option<u64>,
78    /// Locked memory size by kibibytes (see mlock(3)).
79    pub vmlck: Option<u64>,
80    /// Pinned memory size by kibibytes (since Linux 3.2).  These are
81    /// pages that can't be moved because something needs to
82    /// directly access physical memory.
83    pub vmpin: Option<u64>,
84    /// Peak resident set size by kibibytes ("high water mark").
85    pub vmhwm: Option<u64>,
86    /// Resident set size by kibibytes.  Note that the value here is the
87    /// sum of RssAnon, RssFile, and RssShmem.
88    pub vmrss: Option<u64>,
89    /// Size of resident anonymous memory by kibibytes.  (since Linux 4.5).
90    pub rssanon: Option<u64>,
91    /// Size of resident file mappings by kibibytes.  (since Linux 4.5).
92    pub rssfile: Option<u64>,
93    /// Size of resident shared memory by kibibytes (includes System V
94    /// shared memory, mappings from tmpfs(5), and shared anonymous
95    /// mappings).  (since Linux 4.5).
96    pub rssshmem: Option<u64>,
97    /// Size of data by kibibytes.
98    pub vmdata: Option<u64>,
99    /// Size of stack by kibibytes.
100    pub vmstk: Option<u64>,
101    /// Size of text seg‐ments by kibibytes.
102    pub vmexe: Option<u64>,
103    /// Shared library code size by kibibytes.
104    pub vmlib: Option<u64>,
105    /// Page table entries size by kibibytes (since Linux 2.6.10).
106    pub vmpte: Option<u64>,
107    /// Swapped-out virtual memory size by anonymous private
108    /// pages by kibibytes; shmem swap usage is not included (since Linux 2.6.34).
109    pub vmswap: Option<u64>,
110    /// Size of hugetlb memory portions by kB.  (since Linux 4.4).
111    pub hugetlbpages: Option<u64>,
112    /// Number of threads in process containing this thread.
113    pub threads: u64,
114    /// This field contains two slash-separated numbers that
115    /// relate to queued signals for the real user ID of this
116    /// process.  The first of these is the number of currently
117    /// queued signals for this real user ID, and the second is the
118    /// resource limit on the number of queued signals for this
119    /// process (see the description of RLIMIT_SIGPENDING in
120    /// getrlimit(2)).
121    pub sigq: (u64, u64),
122    /// Number of signals pending for thread (see pthreads(7) and signal(7)).
123    pub sigpnd: u64,
124    /// Number of signals pending for process as a whole (see pthreads(7) and signal(7)).
125    pub shdpnd: u64,
126    /// Masks indicating signals being blocked (see signal(7)).
127    pub sigblk: u64,
128    /// Masks indicating signals being ignored (see signal(7)).
129    pub sigign: u64,
130    /// Masks indicating signals being caught (see signal(7)).
131    pub sigcgt: u64,
132    /// Masks of capabilities enabled in inheritable sets (see capabilities(7)).
133    pub capinh: u64,
134    /// Masks of capabilities enabled in permitted sets (see capabilities(7)).
135    pub capprm: u64,
136    /// Masks of capabilities enabled in effective sets (see capabilities(7)).
137    pub capeff: u64,
138    /// Capability Bounding set (since Linux 2.6.26, see capabilities(7)).
139    pub capbnd: Option<u64>,
140    /// Ambient capability set (since Linux 4.3, see capabilities(7)).
141    pub capamb: Option<u64>,
142    /// Value of the no_new_privs bit (since Linux 4.10, see prctl(2)).
143    pub nonewprivs: Option<u64>,
144    /// Seccomp mode of the process (since Linux 3.8, see
145    /// seccomp(2)).  0 means SECCOMP_MODE_DISABLED; 1 means SEC‐
146    /// COMP_MODE_STRICT; 2 means SECCOMP_MODE_FILTER.  This field
147    /// is provided only if the kernel was built with the CON‐
148    /// FIG_SECCOMP kernel configuration option enabled.
149    pub seccomp: Option<u32>,
150    /// Speculative store bypass mitigation status.
151    pub speculation_store_bypass: Option<String>,
152    /// Mask of CPUs on which this process may run (since Linux 2.6.24, see cpuset(7)).
153    pub cpus_allowed: Option<Vec<u32>>,
154    /// Same as previous, but in "list format" (since Linux 2.6.26, see cpuset(7)).
155    pub cpus_allowed_list: Option<Vec<(u32, u32)>>,
156    /// Mask of memory nodes allowed to this process (since Linux 2.6.24, see cpuset(7)).
157    pub mems_allowed: Option<Vec<u32>>,
158    /// Same as previous, but in "list format" (since Linux 2.6.26, see cpuset(7)).
159    pub mems_allowed_list: Option<Vec<(u32, u32)>>,
160    /// Number of voluntary context switches (since Linux 2.6.23).
161    pub voluntary_ctxt_switches: Option<u64>,
162    /// Number of involuntary context switches (since Linux 2.6.23).
163    pub nonvoluntary_ctxt_switches: Option<u64>,
164
165    /// Contains true if the process is currently dumping core.
166    ///
167    /// This information can be used by a monitoring process to avoid killing a processing that is
168    /// currently dumping core, which could result in a corrupted core dump file.
169    ///
170    /// (Since Linux 4.15)
171    pub core_dumping: Option<bool>,
172
173    /// Contains true if the process is allowed to use THP
174    ///
175    /// (Since Linux 5.0)
176    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            // This isn't an error because different kernels may put different data here, and distros
277            // may backport these changes into older kernels.  Too hard to keep track of
278            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}