heim_process/sys/linux/process/procfs/
stat.rs

1use std::convert::TryFrom;
2use std::io;
3use std::str::FromStr;
4
5use heim_common::prelude::*;
6use heim_common::sys::unix::CLOCK_TICKS;
7use heim_common::units::{time, Time};
8use heim_common::utils::iter::{ParseIterator, TryIterator};
9use heim_runtime::fs;
10
11use crate::{Pid, ProcessError, ProcessResult, Status};
12
13impl TryFrom<char> for Status {
14    type Error = Error;
15
16    fn try_from(value: char) -> Result<Status> {
17        match value {
18            'R' => Ok(Status::Running),
19            'S' => Ok(Status::Sleeping),
20            'D' => Ok(Status::Waiting),
21            'Z' => Ok(Status::Zombie),
22            'T' => Ok(Status::Stopped),
23            't' => Ok(Status::Tracing),
24            'X' | 'x' => Ok(Status::Dead),
25            'K' => Ok(Status::Wakekill),
26            'W' => Ok(Status::Waking),
27            'P' => Ok(Status::Parked),
28            'I' => Ok(Status::Idle),
29            other => Err(Error::incompatible(format!(
30                "Unknown process state {}",
31                other
32            ))),
33        }
34    }
35}
36
37impl FromStr for Status {
38    type Err = Error;
39
40    fn from_str(value: &str) -> Result<Status> {
41        match value.chars().next() {
42            Some(chr) => Status::try_from(chr),
43            // Can only mean a bug in implementation
44            None => unreachable!(),
45        }
46    }
47}
48
49#[derive(Debug)]
50pub struct Stat {
51    pub pid: Pid,
52    pub name: String,
53    pub state: Status,
54    pub ppid: Pid,
55    pub create_time: Time,
56    pub utime: Time,
57    pub stime: Time,
58    pub cutime: Time,
59    pub cstime: Time,
60}
61
62impl FromStr for Stat {
63    type Err = Error;
64
65    fn from_str(s: &str) -> Result<Self> {
66        let mut parts = s.splitn(2, ' ');
67        let pid: Pid = parts.try_parse_next()?;
68        let leftover = parts.try_next()?;
69        let comm_end = leftover
70            .rfind(')')
71            .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?;
72        let name = leftover[1..comm_end].to_string();
73        // `+ 2` is for the ") " at the start
74        let mut parts = leftover[comm_end + 2..].split_whitespace();
75        let state: Status = parts.try_parse_next()?;
76        let ppid: Pid = parts.try_parse_next()?;
77        let _pgrp: i32 = parts.try_parse_next()?;
78        let _session_id: i32 = parts.try_parse_next()?;
79        let _tty_nr: i32 = parts.try_parse_next()?;
80        let _tpgid: i32 = parts.try_parse_next()?;
81        let _flags: u32 = parts.try_parse_next()?;
82        let _minflt: u64 = parts.try_parse_next()?;
83        let _cminflt: u64 = parts.try_parse_next()?;
84        let _majflt: u64 = parts.try_parse_next()?;
85        let _cmajflt: u64 = parts.try_parse_next()?;
86        let utime: u64 = parts.try_parse_next()?;
87        let stime: u64 = parts.try_parse_next()?;
88        let cutime: i64 = parts.try_parse_next()?;
89        let cstime: i64 = parts.try_parse_next()?;
90        let _priority: i64 = parts.try_parse_next()?;
91        let _nice: i64 = parts.try_parse_next()?;
92        let _num_threads: i64 = parts.try_parse_next()?;
93        let _itrealvalue: i64 = parts.try_parse_next()?;
94        let start_time: i64 = parts.try_parse_next()?;
95        let _vsize: i64 = parts.try_parse_next()?;
96        let _rss: i64 = parts.try_parse_next()?;
97        let _rsslim: u64 = parts.try_parse_next()?;
98        // ...
99
100        let start_time = start_time as f64 / *CLOCK_TICKS;
101        debug_assert!(!start_time.is_nan());
102
103        Ok(Stat {
104            pid,
105            name,
106            state,
107            ppid,
108            create_time: Time::new::<time::second>(start_time),
109            // TODO: Possible values truncation during the `as f64` cast
110            utime: Time::new::<time::second>(utime as f64 / *CLOCK_TICKS),
111            stime: Time::new::<time::second>(stime as f64 / *CLOCK_TICKS),
112            cutime: Time::new::<time::second>(cutime as f64 / *CLOCK_TICKS),
113            cstime: Time::new::<time::second>(cstime as f64 / *CLOCK_TICKS),
114        })
115    }
116}
117
118pub fn stat(pid: Pid) -> impl Future<Output = ProcessResult<Stat>> {
119    fs::read_to_string(format!("/proc/{}/stat", pid))
120        .map_err(move |e| {
121            if e.kind() == io::ErrorKind::NotFound {
122                ProcessError::NoSuchProcess(pid)
123            } else {
124                e.into()
125            }
126        })
127        .and_then(|contents| future::ready(Stat::from_str(&contents).map_err(Into::into)))
128        .and_then(|mut stat| {
129            heim_host::boot_time()
130                .map_err(Into::into)
131                .map_ok(move |boot_time| {
132                    stat.create_time += boot_time;
133
134                    stat
135                })
136        })
137}