]>
Commit | Line | Data |
---|---|---|
923072b8 FG |
1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | ||
3 | use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid}; | |
4 | ||
5 | use std::fmt; | |
6 | use std::path::{Path, PathBuf}; | |
7 | ||
8 | use super::utils::{get_sys_value_str, WrapMap}; | |
9 | ||
10 | #[doc(hidden)] | |
11 | impl From<libc::c_char> for ProcessStatus { | |
12 | fn from(status: libc::c_char) -> ProcessStatus { | |
13 | match status { | |
14 | libc::SIDL => ProcessStatus::Idle, | |
15 | libc::SRUN => ProcessStatus::Run, | |
16 | libc::SSLEEP => ProcessStatus::Sleep, | |
17 | libc::SSTOP => ProcessStatus::Stop, | |
18 | libc::SZOMB => ProcessStatus::Zombie, | |
19 | libc::SWAIT => ProcessStatus::Dead, | |
20 | libc::SLOCK => ProcessStatus::LockBlocked, | |
21 | x => ProcessStatus::Unknown(x as _), | |
22 | } | |
23 | } | |
24 | } | |
25 | ||
26 | impl fmt::Display for ProcessStatus { | |
27 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
28 | f.write_str(match *self { | |
29 | ProcessStatus::Idle => "Idle", | |
30 | ProcessStatus::Run => "Runnable", | |
31 | ProcessStatus::Sleep => "Sleeping", | |
32 | ProcessStatus::Stop => "Stopped", | |
33 | ProcessStatus::Zombie => "Zombie", | |
34 | ProcessStatus::Dead => "Dead", | |
35 | ProcessStatus::LockBlocked => "LockBlocked", | |
36 | _ => "Unknown", | |
37 | }) | |
38 | } | |
39 | } | |
40 | ||
41 | #[doc = include_str!("../../md_doc/process.md")] | |
42 | pub struct Process { | |
43 | pub(crate) name: String, | |
44 | pub(crate) cmd: Vec<String>, | |
45 | pub(crate) exe: PathBuf, | |
46 | pub(crate) pid: Pid, | |
47 | parent: Option<Pid>, | |
48 | pub(crate) environ: Vec<String>, | |
49 | pub(crate) cwd: PathBuf, | |
50 | pub(crate) root: PathBuf, | |
51 | pub(crate) memory: u64, | |
52 | pub(crate) virtual_memory: u64, | |
53 | pub(crate) updated: bool, | |
54 | cpu_usage: f32, | |
55 | start_time: u64, | |
56 | run_time: u64, | |
57 | pub(crate) status: ProcessStatus, | |
58 | user_id: Uid, | |
59 | group_id: Gid, | |
60 | read_bytes: u64, | |
61 | old_read_bytes: u64, | |
62 | written_bytes: u64, | |
63 | old_written_bytes: u64, | |
64 | } | |
65 | ||
66 | impl ProcessExt for Process { | |
67 | fn kill_with(&self, signal: Signal) -> Option<bool> { | |
68 | let c_signal = super::system::convert_signal(signal)?; | |
69 | unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) } | |
70 | } | |
71 | ||
72 | fn name(&self) -> &str { | |
73 | &self.name | |
74 | } | |
75 | ||
76 | fn cmd(&self) -> &[String] { | |
77 | &self.cmd | |
78 | } | |
79 | ||
80 | fn exe(&self) -> &Path { | |
81 | self.exe.as_path() | |
82 | } | |
83 | ||
84 | fn pid(&self) -> Pid { | |
85 | self.pid | |
86 | } | |
87 | ||
88 | fn environ(&self) -> &[String] { | |
89 | &self.environ | |
90 | } | |
91 | ||
92 | fn cwd(&self) -> &Path { | |
93 | self.cwd.as_path() | |
94 | } | |
95 | ||
96 | fn root(&self) -> &Path { | |
97 | self.root.as_path() | |
98 | } | |
99 | ||
100 | fn memory(&self) -> u64 { | |
101 | self.memory | |
102 | } | |
103 | ||
104 | fn virtual_memory(&self) -> u64 { | |
105 | self.virtual_memory | |
106 | } | |
107 | ||
108 | fn parent(&self) -> Option<Pid> { | |
109 | self.parent | |
110 | } | |
111 | ||
112 | fn status(&self) -> ProcessStatus { | |
113 | self.status | |
114 | } | |
115 | ||
116 | fn start_time(&self) -> u64 { | |
117 | self.start_time | |
118 | } | |
119 | ||
120 | fn run_time(&self) -> u64 { | |
121 | self.run_time | |
122 | } | |
123 | ||
124 | fn cpu_usage(&self) -> f32 { | |
125 | self.cpu_usage | |
126 | } | |
127 | ||
128 | fn disk_usage(&self) -> DiskUsage { | |
129 | DiskUsage { | |
130 | written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes), | |
131 | total_written_bytes: self.written_bytes, | |
132 | read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes), | |
133 | total_read_bytes: self.read_bytes, | |
134 | } | |
135 | } | |
136 | ||
137 | fn user_id(&self) -> Option<&Uid> { | |
138 | Some(&self.user_id) | |
139 | } | |
140 | ||
141 | fn group_id(&self) -> Option<Gid> { | |
142 | Some(self.group_id) | |
143 | } | |
144 | } | |
145 | ||
146 | pub(crate) unsafe fn get_process_data( | |
147 | kproc: &libc::kinfo_proc, | |
148 | wrap: &WrapMap, | |
149 | page_size: isize, | |
150 | fscale: f32, | |
151 | now: u64, | |
152 | refresh_kind: ProcessRefreshKind, | |
153 | ) -> Result<Option<Process>, ()> { | |
154 | if kproc.ki_pid != 1 && (kproc.ki_flag as libc::c_int & libc::P_SYSTEM) != 0 { | |
155 | // We filter out the kernel threads. | |
156 | return Err(()); | |
157 | } | |
158 | ||
159 | // We now get the values needed for both new and existing process. | |
160 | let cpu_usage = if refresh_kind.cpu() { | |
161 | (100 * kproc.ki_pctcpu) as f32 / fscale | |
162 | } else { | |
163 | 0. | |
164 | }; | |
165 | // Processes can be reparented apparently? | |
166 | let parent = if kproc.ki_ppid != 0 { | |
167 | Some(Pid(kproc.ki_ppid)) | |
168 | } else { | |
169 | None | |
170 | }; | |
171 | let status = ProcessStatus::from(kproc.ki_stat); | |
172 | ||
173 | // from FreeBSD source /src/usr.bin/top/machine.c | |
174 | let virtual_memory = (kproc.ki_size / 1_000) as u64; | |
175 | let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _) / 1_000; | |
176 | // FIXME: This is to get the "real" run time (in micro-seconds). | |
177 | // let run_time = (kproc.ki_runtime + 5_000) / 10_000; | |
178 | ||
179 | if let Some(proc_) = (*wrap.0.get()).get_mut(&Pid(kproc.ki_pid)) { | |
180 | proc_.cpu_usage = cpu_usage; | |
181 | proc_.parent = parent; | |
182 | proc_.status = status; | |
183 | proc_.virtual_memory = virtual_memory; | |
184 | proc_.memory = memory; | |
185 | proc_.run_time = now.saturating_sub(proc_.start_time); | |
186 | proc_.updated = true; | |
187 | ||
188 | if refresh_kind.disk_usage() { | |
189 | proc_.old_read_bytes = proc_.read_bytes; | |
190 | proc_.read_bytes = kproc.ki_rusage.ru_inblock as _; | |
191 | proc_.old_written_bytes = proc_.written_bytes; | |
192 | proc_.written_bytes = kproc.ki_rusage.ru_oublock as _; | |
193 | } | |
194 | ||
195 | return Ok(None); | |
196 | } | |
197 | ||
198 | // This is a new process, we need to get more information! | |
199 | let mut buffer = [0; libc::PATH_MAX as usize + 1]; | |
200 | ||
201 | let exe = get_sys_value_str( | |
202 | &[ | |
203 | libc::CTL_KERN, | |
204 | libc::KERN_PROC, | |
205 | libc::KERN_PROC_PATHNAME, | |
206 | kproc.ki_pid, | |
207 | ], | |
208 | &mut buffer, | |
209 | ) | |
210 | .unwrap_or_default(); | |
211 | // For some reason, it can return completely invalid path like `p\u{5}`. So we need to use | |
212 | // procstat to get around this problem. | |
213 | // let cwd = get_sys_value_str( | |
214 | // &[ | |
215 | // libc::CTL_KERN, | |
216 | // libc::KERN_PROC, | |
217 | // libc::KERN_PROC_CWD, | |
218 | // kproc.ki_pid, | |
219 | // ], | |
220 | // &mut buffer, | |
221 | // ) | |
222 | // .map(|s| s.into()) | |
223 | // .unwrap_or_else(PathBuf::new); | |
224 | ||
225 | let start_time = kproc.ki_start.tv_sec as u64; | |
226 | Ok(Some(Process { | |
227 | pid: Pid(kproc.ki_pid), | |
228 | parent, | |
229 | user_id: Uid(kproc.ki_ruid), | |
230 | group_id: Gid(kproc.ki_rgid), | |
231 | start_time, | |
232 | run_time: now.saturating_sub(start_time), | |
233 | cpu_usage, | |
234 | virtual_memory, | |
235 | memory, | |
236 | // procstat_getfiles | |
237 | cwd: PathBuf::new(), | |
238 | exe: exe.into(), | |
239 | // kvm_getargv isn't thread-safe so we get it in the main thread. | |
240 | name: String::new(), | |
241 | // kvm_getargv isn't thread-safe so we get it in the main thread. | |
242 | cmd: Vec::new(), | |
243 | // kvm_getargv isn't thread-safe so we get it in the main thread. | |
244 | root: PathBuf::new(), | |
245 | // kvm_getenvv isn't thread-safe so we get it in the main thread. | |
246 | environ: Vec::new(), | |
247 | status, | |
248 | read_bytes: kproc.ki_rusage.ru_inblock as _, | |
249 | old_read_bytes: 0, | |
250 | written_bytes: kproc.ki_rusage.ru_oublock as _, | |
251 | old_written_bytes: 0, | |
252 | updated: true, | |
253 | })) | |
254 | } |