1use std::ffi::{OsStr, OsString};
4use std::mem::{self, MaybeUninit};
5use std::os::unix::ffi::{OsStrExt, OsStringExt};
6use std::path::{Path, PathBuf};
7use std::process::ExitStatus;
8
9use libc::{c_int, c_void, kill};
10
11use crate::{DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, Uid};
12
13use crate::sys::process::ThreadStatus;
14use crate::sys::system::Wrap;
15use crate::unix::utils::cstr_to_rust_with_size;
16
17pub(crate) struct ProcessInner {
18 pub(crate) name: OsString,
19 pub(crate) cmd: Vec<OsString>,
20 pub(crate) exe: Option<PathBuf>,
21 pid: Pid,
22 parent: Option<Pid>,
23 pub(crate) environ: Vec<OsString>,
24 cwd: Option<PathBuf>,
25 pub(crate) root: Option<PathBuf>,
26 pub(crate) memory: u64,
27 pub(crate) virtual_memory: u64,
28 old_utime: u64,
29 old_stime: u64,
30 start_time: u64,
31 run_time: u64,
32 pub(crate) updated: bool,
33 cpu_usage: f32,
34 user_id: Option<Uid>,
35 effective_user_id: Option<Uid>,
36 group_id: Option<Gid>,
37 effective_group_id: Option<Gid>,
38 pub(crate) process_status: ProcessStatus,
39 pub(crate) status: Option<ThreadStatus>,
44 pub(crate) old_read_bytes: u64,
45 pub(crate) old_written_bytes: u64,
46 pub(crate) read_bytes: u64,
47 pub(crate) written_bytes: u64,
48 accumulated_cpu_time: u64,
49 exists: bool,
50}
51
52impl ProcessInner {
53 pub(crate) fn new_empty(pid: Pid) -> Self {
54 Self {
55 name: OsString::new(),
56 pid,
57 parent: None,
58 cmd: Vec::new(),
59 environ: Vec::new(),
60 exe: None,
61 cwd: None,
62 root: None,
63 memory: 0,
64 virtual_memory: 0,
65 cpu_usage: 0.,
66 old_utime: 0,
67 old_stime: 0,
68 updated: true,
69 start_time: 0,
70 run_time: 0,
71 user_id: None,
72 effective_user_id: None,
73 group_id: None,
74 effective_group_id: None,
75 process_status: ProcessStatus::Unknown(0),
76 status: None,
77 old_read_bytes: 0,
78 old_written_bytes: 0,
79 read_bytes: 0,
80 written_bytes: 0,
81 accumulated_cpu_time: 0,
82 exists: true,
83 }
84 }
85
86 pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64, run_time: u64) -> Self {
87 Self {
88 name: OsString::new(),
89 pid,
90 parent,
91 cmd: Vec::new(),
92 environ: Vec::new(),
93 exe: None,
94 cwd: None,
95 root: None,
96 memory: 0,
97 virtual_memory: 0,
98 cpu_usage: 0.,
99 old_utime: 0,
100 old_stime: 0,
101 updated: true,
102 start_time,
103 run_time,
104 user_id: None,
105 effective_user_id: None,
106 group_id: None,
107 effective_group_id: None,
108 process_status: ProcessStatus::Unknown(0),
109 status: None,
110 old_read_bytes: 0,
111 old_written_bytes: 0,
112 read_bytes: 0,
113 written_bytes: 0,
114 accumulated_cpu_time: 0,
115 exists: true,
116 }
117 }
118
119 pub(crate) fn kill_with(&self, signal: Signal) -> Option<bool> {
120 let c_signal = crate::sys::system::convert_signal(signal)?;
121 unsafe { Some(kill(self.pid.0, c_signal) == 0) }
122 }
123
124 pub(crate) fn name(&self) -> &OsStr {
125 &self.name
126 }
127
128 pub(crate) fn cmd(&self) -> &[OsString] {
129 &self.cmd
130 }
131
132 pub(crate) fn exe(&self) -> Option<&Path> {
133 self.exe.as_deref()
134 }
135
136 pub(crate) fn pid(&self) -> Pid {
137 self.pid
138 }
139
140 pub(crate) fn environ(&self) -> &[OsString] {
141 &self.environ
142 }
143
144 pub(crate) fn cwd(&self) -> Option<&Path> {
145 self.cwd.as_deref()
146 }
147
148 pub(crate) fn root(&self) -> Option<&Path> {
149 self.root.as_deref()
150 }
151
152 pub(crate) fn memory(&self) -> u64 {
153 self.memory
154 }
155
156 pub(crate) fn virtual_memory(&self) -> u64 {
157 self.virtual_memory
158 }
159
160 pub(crate) fn parent(&self) -> Option<Pid> {
161 self.parent
162 }
163
164 pub(crate) fn status(&self) -> ProcessStatus {
165 if self.process_status == ProcessStatus::Run {
168 if let Some(thread_status) = self.status {
169 return ProcessStatus::from(thread_status);
170 }
171 }
172 self.process_status
173 }
174
175 pub(crate) fn start_time(&self) -> u64 {
176 self.start_time
177 }
178
179 pub(crate) fn run_time(&self) -> u64 {
180 self.run_time
181 }
182
183 pub(crate) fn cpu_usage(&self) -> f32 {
184 self.cpu_usage
185 }
186
187 pub(crate) fn accumulated_cpu_time(&self) -> u64 {
188 self.accumulated_cpu_time
189 }
190
191 pub(crate) fn disk_usage(&self) -> DiskUsage {
192 DiskUsage {
193 read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
194 total_read_bytes: self.read_bytes,
195 written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
196 total_written_bytes: self.written_bytes,
197 }
198 }
199
200 pub(crate) fn user_id(&self) -> Option<&Uid> {
201 self.user_id.as_ref()
202 }
203
204 pub(crate) fn effective_user_id(&self) -> Option<&Uid> {
205 self.effective_user_id.as_ref()
206 }
207
208 pub(crate) fn group_id(&self) -> Option<Gid> {
209 self.group_id
210 }
211
212 pub(crate) fn effective_group_id(&self) -> Option<Gid> {
213 self.effective_group_id
214 }
215
216 pub(crate) fn wait(&self) -> Option<ExitStatus> {
217 crate::unix::utils::wait_process(self.pid)
218 }
219
220 pub(crate) fn session_id(&self) -> Option<Pid> {
221 unsafe {
222 let session_id = libc::getsid(self.pid.0);
223 if session_id < 0 {
224 None
225 } else {
226 Some(Pid(session_id))
227 }
228 }
229 }
230
231 pub(crate) fn switch_updated(&mut self) -> bool {
232 std::mem::replace(&mut self.updated, false)
233 }
234
235 pub(crate) fn set_nonexistent(&mut self) {
236 self.exists = false;
237 }
238
239 pub(crate) fn exists(&self) -> bool {
240 self.exists
241 }
242}
243
244#[allow(deprecated)] pub(crate) fn compute_cpu_usage(
246 p: &mut ProcessInner,
247 task_info: libc::proc_taskinfo,
248 system_time: u64,
249 user_time: u64,
250 time_interval: Option<f64>,
251) {
252 if let Some(time_interval) = time_interval {
253 let total_existing_time = p.old_stime.saturating_add(p.old_utime);
254 let mut updated_cpu_usage = false;
255 if time_interval > 0.000001 && total_existing_time > 0 {
256 let total_current_time = task_info
257 .pti_total_system
258 .saturating_add(task_info.pti_total_user);
259
260 let total_time_diff = total_current_time.saturating_sub(total_existing_time);
261 if total_time_diff > 0 {
262 p.cpu_usage = (total_time_diff as f64 / time_interval * 100.) as f32;
263 updated_cpu_usage = true;
264 }
265 }
266 if !updated_cpu_usage {
267 p.cpu_usage = 0.;
268 }
269 p.old_stime = task_info.pti_total_system;
270 p.old_utime = task_info.pti_total_user;
271 } else {
272 unsafe {
273 let time = libc::mach_absolute_time();
275 let task_time = user_time
276 .saturating_add(system_time)
277 .saturating_add(task_info.pti_total_user)
278 .saturating_add(task_info.pti_total_system);
279
280 let system_time_delta = if task_time < p.old_utime {
281 task_time
282 } else {
283 task_time.saturating_sub(p.old_utime)
284 };
285 let time_delta = if time < p.old_stime {
286 time
287 } else {
288 time.saturating_sub(p.old_stime)
289 };
290 p.old_utime = task_time;
291 p.old_stime = time;
292 p.cpu_usage = if time_delta == 0 {
293 0f32
294 } else {
295 (system_time_delta as f64 * 100f64 / time_delta as f64) as f32
296 };
297 }
298 }
299}
300
301unsafe fn get_task_info(pid: Pid) -> libc::proc_taskinfo {
302 let mut task_info = mem::zeroed::<libc::proc_taskinfo>();
303 libc::proc_pidinfo(
306 pid.0,
307 libc::PROC_PIDTASKINFO,
308 0,
309 &mut task_info as *mut libc::proc_taskinfo as *mut c_void,
310 mem::size_of::<libc::proc_taskinfo>() as _,
311 );
312 task_info
313}
314
315#[inline]
316fn check_if_pid_is_alive(pid: Pid, check_if_alive: bool) -> bool {
317 if !check_if_alive {
321 return true;
322 }
323 unsafe {
324 if kill(pid.0, 0) == 0 {
325 return true;
326 }
327 let errno = crate::unix::libc_errno();
329 !errno.is_null() && *errno != libc::ESRCH
331 }
332}
333
334unsafe fn get_bsd_info(pid: Pid) -> Option<libc::proc_bsdinfo> {
335 let mut info = mem::zeroed::<libc::proc_bsdinfo>();
336
337 if libc::proc_pidinfo(
338 pid.0,
339 libc::PROC_PIDTBSDINFO,
340 0,
341 &mut info as *mut _ as *mut _,
342 mem::size_of::<libc::proc_bsdinfo>() as _,
343 ) != mem::size_of::<libc::proc_bsdinfo>() as c_int
344 {
345 None
346 } else {
347 Some(info)
348 }
349}
350
351fn get_parent(info: &libc::proc_bsdinfo) -> Option<Pid> {
352 match info.pbi_ppid as i32 {
353 0 => None,
354 p => Some(Pid(p)),
355 }
356}
357
358unsafe fn create_new_process(
359 pid: Pid,
360 now: u64,
361 refresh_kind: ProcessRefreshKind,
362 info: Option<libc::proc_bsdinfo>,
363 timebase_to_ms: f64,
364) -> Result<Option<Process>, ()> {
365 let info = match info {
366 Some(info) => info,
367 None => {
368 let mut p = ProcessInner::new_empty(pid);
369 if get_exe_and_name_backup(&mut p, refresh_kind, false) {
370 get_cwd_root(&mut p, refresh_kind);
371 return Ok(Some(Process { inner: p }));
372 }
373 return Err(());
375 }
376 };
377
378 let parent = get_parent(&info);
379
380 let start_time = info.pbi_start_tvsec;
381 let run_time = now.saturating_sub(start_time);
382
383 let mut p = ProcessInner::new(pid, parent, start_time, run_time);
384 if !get_process_infos(&mut p, refresh_kind)
385 && !get_exe_and_name_backup(&mut p, refresh_kind, false)
386 {
387 return Err(());
389 }
390 get_cwd_root(&mut p, refresh_kind);
391
392 if refresh_kind.cpu() || refresh_kind.memory() {
393 let task_info = get_task_info(pid);
394
395 if refresh_kind.cpu() {
396 p.accumulated_cpu_time = (task_info
397 .pti_total_user
398 .saturating_add(task_info.pti_total_system)
399 as f64
400 * timebase_to_ms) as u64;
401 }
402 if refresh_kind.memory() {
403 p.memory = task_info.pti_resident_size;
404 p.virtual_memory = task_info.pti_virtual_size;
405 }
406 }
407
408 p.user_id = Some(Uid(info.pbi_ruid));
409 p.effective_user_id = Some(Uid(info.pbi_uid));
410 p.group_id = Some(Gid(info.pbi_rgid));
411 p.effective_group_id = Some(Gid(info.pbi_gid));
412 p.process_status = ProcessStatus::from(info.pbi_status);
413 if refresh_kind.disk_usage() {
414 update_proc_disk_activity(&mut p);
415 }
416 Ok(Some(Process { inner: p }))
417}
418
419unsafe fn get_exe_and_name_backup(
421 process: &mut ProcessInner,
422 refresh_kind: ProcessRefreshKind,
423 force_check: bool,
424) -> bool {
425 let exe_needs_update = refresh_kind.exe().needs_update(|| process.exe.is_none());
426 if !process.name.is_empty() && !exe_needs_update && !force_check {
427 return true;
428 }
429 let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
430 match libc::proc_pidpath(
431 process.pid.0,
432 buffer.as_mut_ptr() as *mut _,
433 libc::PROC_PIDPATHINFO_MAXSIZE as _,
434 ) {
435 x if x > 0 => {
436 buffer.set_len(x as _);
437 let tmp = OsString::from_vec(buffer);
438 let exe = PathBuf::from(tmp);
439 if process.name.is_empty() {
440 exe.file_name()
441 .unwrap_or_default()
442 .clone_into(&mut process.name);
443 }
444 if exe_needs_update {
445 process.exe = Some(exe);
446 }
447 true
448 }
449 _ => false,
450 }
451}
452
453unsafe fn convert_node_path_info(node: &libc::vnode_info_path) -> Option<PathBuf> {
454 if node.vip_vi.vi_stat.vst_dev == 0 {
455 return None;
456 }
457 cstr_to_rust_with_size(
458 node.vip_path.as_ptr() as _,
459 Some(mem::size_of_val(&node.vip_path)),
460 )
461 .map(PathBuf::from)
462}
463
464unsafe fn get_cwd_root(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) {
465 let cwd_needs_update = refresh_kind.cwd().needs_update(|| process.cwd.is_none());
466 let root_needs_update = refresh_kind.root().needs_update(|| process.root.is_none());
467 if !cwd_needs_update && !root_needs_update {
468 return;
469 }
470 let mut vnodepathinfo = mem::zeroed::<libc::proc_vnodepathinfo>();
471 let result = libc::proc_pidinfo(
472 process.pid.0,
473 libc::PROC_PIDVNODEPATHINFO,
474 0,
475 &mut vnodepathinfo as *mut _ as *mut _,
476 mem::size_of::<libc::proc_vnodepathinfo>() as _,
477 );
478 if result < 1 {
479 sysinfo_debug!("Failed to retrieve cwd and root for {}", process.pid.0);
480 return;
481 }
482 if cwd_needs_update {
483 process.cwd = convert_node_path_info(&vnodepathinfo.pvi_cdir);
484 }
485 if root_needs_update {
486 process.root = convert_node_path_info(&vnodepathinfo.pvi_rdir);
487 }
488}
489
490unsafe fn get_process_infos(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) -> bool {
491 let mut mib: [libc::c_int; 3] = [libc::CTL_KERN, libc::KERN_PROCARGS2, process.pid.0 as _];
522 let mut arg_max = 0;
523 if libc::sysctl(
525 mib.as_mut_ptr(),
526 mib.len() as _,
527 std::ptr::null_mut(),
528 &mut arg_max,
529 std::ptr::null_mut(),
530 0,
531 ) == -1
532 {
533 sysinfo_debug!(
534 "couldn't get arguments and environment size for PID {}",
535 process.pid.0
536 );
537 return false; }
539
540 let mut proc_args: Vec<u8> = Vec::with_capacity(arg_max as _);
541 if libc::sysctl(
542 mib.as_mut_ptr(),
543 mib.len() as _,
544 proc_args.as_mut_slice().as_mut_ptr() as *mut _,
545 &mut arg_max,
546 std::ptr::null_mut(),
547 0,
548 ) == -1
549 {
550 sysinfo_debug!(
551 "couldn't get arguments and environment for PID {}",
552 process.pid.0
553 );
554 return false; }
556
557 proc_args.set_len(arg_max);
558
559 if proc_args.is_empty() {
560 return false;
561 }
562 let mut n_args: c_int = 0;
564 libc::memcpy(
565 &mut n_args as *mut _ as *mut _,
566 proc_args.as_slice().as_ptr() as *const _,
567 mem::size_of::<c_int>(),
568 );
569
570 let proc_args = &proc_args[mem::size_of::<c_int>()..];
572
573 let (exe, proc_args) = get_exe(proc_args);
574 if process.name.is_empty() {
575 exe.file_name()
576 .unwrap_or_default()
577 .clone_into(&mut process.name);
578 }
579
580 if refresh_kind.exe().needs_update(|| process.exe.is_none()) {
581 process.exe = Some(exe.to_owned());
582 }
583
584 let environ_needs_update = refresh_kind
585 .environ()
586 .needs_update(|| process.environ.is_empty());
587 let cmd_needs_update = refresh_kind.cmd().needs_update(|| process.cmd.is_empty());
588 if !environ_needs_update && !cmd_needs_update {
589 return true;
591 }
592 let proc_args = get_arguments(&mut process.cmd, proc_args, n_args, cmd_needs_update);
593 if environ_needs_update {
594 get_environ(&mut process.environ, proc_args);
595 }
596 true
597}
598
599fn get_exe(data: &[u8]) -> (&Path, &[u8]) {
600 let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len());
601 let (exe, proc_args) = data.split_at(pos);
602 (Path::new(OsStr::from_bytes(exe)), proc_args)
603}
604
605fn get_arguments<'a>(
606 cmd: &mut Vec<OsString>,
607 mut data: &'a [u8],
608 mut n_args: c_int,
609 refresh_cmd: bool,
610) -> &'a [u8] {
611 if refresh_cmd {
612 cmd.clear();
613 }
614
615 if n_args < 1 {
616 return data;
617 }
618 while data.first() == Some(&0) {
619 data = &data[1..];
620 }
621
622 while n_args > 0 && !data.is_empty() {
623 let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len());
624 let arg = &data[..pos];
625 if !arg.is_empty() && refresh_cmd {
626 cmd.push(OsStr::from_bytes(arg).to_os_string());
627 }
628 data = &data[pos..];
629 while data.first() == Some(&0) {
630 data = &data[1..];
631 }
632 n_args -= 1;
633 }
634 data
635}
636
637fn get_environ(environ: &mut Vec<OsString>, mut data: &[u8]) {
638 environ.clear();
639
640 while data.first() == Some(&0) {
641 data = &data[1..];
642 }
643
644 while !data.is_empty() {
645 let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len());
646 let arg = &data[..pos];
647 if arg.is_empty() {
648 return;
649 }
650 environ.push(OsStr::from_bytes(arg).to_os_string());
651 data = &data[pos..];
652 while data.first() == Some(&0) {
653 data = &data[1..];
654 }
655 }
656}
657
658pub(crate) fn update_process(
659 wrap: &Wrap,
660 pid: Pid,
661 time_interval: Option<f64>,
662 now: u64,
663 refresh_kind: ProcessRefreshKind,
664 check_if_alive: bool,
665 timebase_to_ms: f64,
666) -> Result<Option<Process>, ()> {
667 unsafe {
668 if let Some(ref mut p) = (*wrap.0.get()).get_mut(&pid) {
669 let p = &mut p.inner;
670
671 let mut extra_checked = false;
672
673 if let Some(info) = get_bsd_info(pid) {
674 if info.pbi_start_tvsec != p.start_time {
675 p.updated = true;
677 p.name.clear();
679 p.exe = None;
680 return create_new_process(pid, now, refresh_kind, Some(info), timebase_to_ms);
682 }
683 let parent = get_parent(&info);
684 if p.parent != parent {
686 p.parent = parent;
687 }
688 } else {
689 if !get_exe_and_name_backup(p, refresh_kind, true) {
692 p.updated = false;
694 return Ok(None);
695 }
696 extra_checked = true;
697 }
698
699 if !get_process_infos(p, refresh_kind) && !extra_checked {
700 get_exe_and_name_backup(p, refresh_kind, false);
701 }
702 get_cwd_root(p, refresh_kind);
703
704 if refresh_kind.disk_usage() {
705 update_proc_disk_activity(p);
706 }
707
708 let mut thread_info = mem::zeroed::<libc::proc_threadinfo>();
709 let (user_time, system_time, thread_status) = if libc::proc_pidinfo(
710 pid.0,
711 libc::PROC_PIDTHREADINFO,
712 0,
713 &mut thread_info as *mut libc::proc_threadinfo as *mut c_void,
714 mem::size_of::<libc::proc_threadinfo>() as _,
715 ) != 0
716 {
717 (
718 thread_info.pth_user_time,
719 thread_info.pth_system_time,
720 Some(ThreadStatus::from(thread_info.pth_run_state)),
721 )
722 } else {
723 if check_if_pid_is_alive(pid, check_if_alive) {
725 (0, 0, Some(ThreadStatus::Running))
726 } else {
727 return Err(());
728 }
729 };
730 p.status = thread_status;
731 p.run_time = now.saturating_sub(p.start_time);
732
733 if refresh_kind.cpu() || refresh_kind.memory() {
734 let task_info = get_task_info(pid);
735
736 if refresh_kind.cpu() {
737 compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
738 p.accumulated_cpu_time = (task_info
739 .pti_total_user
740 .saturating_add(task_info.pti_total_system)
741 as f64
742 * timebase_to_ms) as u64;
743 }
744 if refresh_kind.memory() {
745 p.memory = task_info.pti_resident_size;
746 p.virtual_memory = task_info.pti_virtual_size;
747 }
748 }
749 p.updated = true;
750 Ok(None)
751 } else {
752 create_new_process(pid, now, refresh_kind, get_bsd_info(pid), timebase_to_ms)
753 }
754 }
755}
756
757fn update_proc_disk_activity(p: &mut ProcessInner) {
758 p.old_read_bytes = p.read_bytes;
759 p.old_written_bytes = p.written_bytes;
760
761 let mut pidrusage = MaybeUninit::<libc::rusage_info_v2>::uninit();
762
763 unsafe {
764 let retval = libc::proc_pid_rusage(
765 p.pid().0 as _,
766 libc::RUSAGE_INFO_V2,
767 pidrusage.as_mut_ptr() as _,
768 );
769
770 if retval < 0 {
771 sysinfo_debug!("proc_pid_rusage failed: {:?}", retval);
772 } else {
773 let pidrusage = pidrusage.assume_init();
774 p.read_bytes = pidrusage.ri_diskio_bytesread;
775 p.written_bytes = pidrusage.ri_diskio_byteswritten;
776 }
777 }
778}
779
780#[allow(clippy::uninit_vec)]
781pub(crate) fn get_proc_list() -> Option<Vec<Pid>> {
782 unsafe {
783 let count = libc::proc_listallpids(::std::ptr::null_mut(), 0);
784 if count < 1 {
785 return None;
786 }
787 let mut pids: Vec<Pid> = Vec::with_capacity(count as usize);
788 pids.set_len(count as usize);
789 let count = count * mem::size_of::<Pid>() as i32;
790 let x = libc::proc_listallpids(pids.as_mut_ptr() as *mut c_void, count);
791
792 if x < 1 || x as usize >= pids.len() {
793 None
794 } else {
795 pids.set_len(x as usize);
796 Some(pids)
797 }
798 }
799}