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