heim_process/sys/linux/process/procfs/
stat.rs1use 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 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 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 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 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}