sysinfo/unix/apple/macos/
process.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use 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    /// Status of process (running, stopped, waiting, etc). `None` means `sysinfo` doesn't have
40    /// enough rights to get this information.
41    ///
42    /// This is very likely this one that you want instead of `process_status`.
43    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 the status is `Run`, then it's very likely wrong so we instead
166        // return a `ProcessStatus` converted from the `ThreadStatus`.
167        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)] // Because of libc::mach_absolute_time.
245pub(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            // This is the "backup way" of CPU computation.
274            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    // If it doesn't work, we just don't have memory information for this process
304    // so it's "fine".
305    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    // In case we are iterating all pids we got from `proc_listallpids`, then
318    // there is no point checking if the process is alive since it was returned
319    // from this function.
320    if !check_if_alive {
321        return true;
322    }
323    unsafe {
324        if kill(pid.0, 0) == 0 {
325            return true;
326        }
327        // `kill` failed but it might not be because the process is dead.
328        let errno = crate::unix::libc_errno();
329        // If errno is equal to ESCHR, it means the process is dead.
330        !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            // If we can't even have the name, no point in keeping it.
374            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        // If we can't even have the name, no point in keeping it.
388        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
419/// Less efficient way to retrieve `exe` and `name`.
420unsafe 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    /*
492     * /---------------\ 0x00000000
493     * | ::::::::::::: |
494     * |---------------| <-- Beginning of data returned by sysctl() is here.
495     * | argc          |
496     * |---------------|
497     * | exec_path     |
498     * |---------------|
499     * | 0             |
500     * |---------------|
501     * | arg[0]        |
502     * |---------------|
503     * | 0             |
504     * |---------------|
505     * | arg[n]        |
506     * |---------------|
507     * | 0             |
508     * |---------------|
509     * | env[0]        |
510     * |---------------|
511     * | 0             |
512     * |---------------|
513     * | env[n]        |
514     * |---------------|
515     * | ::::::::::::: |
516     * |---------------| <-- Top of stack.
517     * :               :
518     * :               :
519     * \---------------/ 0xffffffff
520     */
521    let mut mib: [libc::c_int; 3] = [libc::CTL_KERN, libc::KERN_PROCARGS2, process.pid.0 as _];
522    let mut arg_max = 0;
523    // First we retrieve the size we will need for our data (in `arg_max`).
524    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; // not enough rights I assume?
538    }
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; // What changed since the previous call? Dark magic!
555    }
556
557    proc_args.set_len(arg_max);
558
559    if proc_args.is_empty() {
560        return false;
561    }
562    // We copy the number of arguments (`argc`) to `n_args`.
563    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    // We skip `argc`.
571    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        // Nothing else to be done!
590        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                    // We don't want it to be removed, just replaced.
676                    p.updated = true;
677                    // To ensure the name and exe path will be updated.
678                    p.name.clear();
679                    p.exe = None;
680                    // The owner of this PID changed.
681                    return create_new_process(pid, now, refresh_kind, Some(info), timebase_to_ms);
682                }
683                let parent = get_parent(&info);
684                // Update the parent if it changed.
685                if p.parent != parent {
686                    p.parent = parent;
687                }
688            } else {
689                // Weird that we can't get this information. Sometimes, mac can list PIDs that do
690                // not exist anymore. So let's ensure that the process is actually still alive.
691                if !get_exe_and_name_backup(p, refresh_kind, true) {
692                    // So it's not actually alive, then let's un-update it so it will be removed.
693                    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                // It very likely means that the process is dead...
724                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}