1use std::cell::UnsafeCell;
4use std::collections::{HashMap, HashSet};
5use std::ffi::OsStr;
6use std::fmt;
7use std::fs::{self, DirEntry, File};
8use std::io::Read;
9use std::path::{Path, PathBuf};
10use std::str::FromStr;
11
12use libc::{c_ulong, gid_t, kill, uid_t};
13
14use crate::sys::system::SystemInfo;
15use crate::sys::utils::{
16 get_all_data, get_all_data_from_file, realpath, FileCounter, PathHandler, PathPush,
17};
18use crate::{
19 DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, ThreadKind, Uid,
20};
21
22#[doc(hidden)]
23impl From<char> for ProcessStatus {
24 fn from(status: char) -> ProcessStatus {
25 match status {
26 'R' => ProcessStatus::Run,
27 'S' => ProcessStatus::Sleep,
28 'I' => ProcessStatus::Idle,
29 'D' => ProcessStatus::UninterruptibleDiskSleep,
30 'Z' => ProcessStatus::Zombie,
31 'T' => ProcessStatus::Stop,
32 't' => ProcessStatus::Tracing,
33 'X' | 'x' => ProcessStatus::Dead,
34 'K' => ProcessStatus::Wakekill,
35 'W' => ProcessStatus::Waking,
36 'P' => ProcessStatus::Parked,
37 x => ProcessStatus::Unknown(x as u32),
38 }
39 }
40}
41
42impl fmt::Display for ProcessStatus {
43 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44 f.write_str(match *self {
45 ProcessStatus::Idle => "Idle",
46 ProcessStatus::Run => "Runnable",
47 ProcessStatus::Sleep => "Sleeping",
48 ProcessStatus::Stop => "Stopped",
49 ProcessStatus::Zombie => "Zombie",
50 ProcessStatus::Tracing => "Tracing",
51 ProcessStatus::Dead => "Dead",
52 ProcessStatus::Wakekill => "Wakekill",
53 ProcessStatus::Waking => "Waking",
54 ProcessStatus::Parked => "Parked",
55 ProcessStatus::UninterruptibleDiskSleep => "UninterruptibleDiskSleep",
56 _ => "Unknown",
57 })
58 }
59}
60
61#[allow(dead_code)]
62#[repr(usize)]
63enum ProcIndex {
64 Pid = 0,
65 ShortExe,
66 State,
67 ParentPid,
68 GroupId,
69 SessionId,
70 Tty,
71 ForegroundProcessGroupId,
72 Flags,
73 MinorFaults,
74 ChildrenMinorFaults,
75 MajorFaults,
76 ChildrenMajorFaults,
77 UserTime,
78 SystemTime,
79 ChildrenUserTime,
80 ChildrenKernelTime,
81 Priority,
82 Nice,
83 NumberOfThreads,
84 IntervalTimerSigalarm,
85 StartTime,
86 VirtualSize,
87 ResidentSetSize,
88 }
90
91pub(crate) struct ProcessInner {
92 pub(crate) name: String,
93 pub(crate) cmd: Vec<String>,
94 pub(crate) exe: Option<PathBuf>,
95 pub(crate) pid: Pid,
96 parent: Option<Pid>,
97 pub(crate) environ: Vec<String>,
98 pub(crate) cwd: Option<PathBuf>,
99 pub(crate) root: Option<PathBuf>,
100 pub(crate) memory: u64,
101 pub(crate) virtual_memory: u64,
102 utime: u64,
103 stime: u64,
104 old_utime: u64,
105 old_stime: u64,
106 start_time_without_boot_time: u64,
107 start_time: u64,
108 run_time: u64,
109 pub(crate) updated: bool,
110 cpu_usage: f32,
111 user_id: Option<Uid>,
112 effective_user_id: Option<Uid>,
113 group_id: Option<Gid>,
114 effective_group_id: Option<Gid>,
115 pub(crate) status: ProcessStatus,
116 pub(crate) tasks: Option<HashSet<Pid>>,
117 pub(crate) stat_file: Option<FileCounter>,
118 old_read_bytes: u64,
119 old_written_bytes: u64,
120 read_bytes: u64,
121 written_bytes: u64,
122 thread_kind: Option<ThreadKind>,
123 proc_path: PathBuf,
124}
125
126impl ProcessInner {
127 pub(crate) fn new(pid: Pid, proc_path: PathBuf) -> Self {
128 Self {
129 name: String::new(),
130 pid,
131 parent: None,
132 cmd: Vec::new(),
133 environ: Vec::new(),
134 exe: None,
135 cwd: None,
136 root: None,
137 memory: 0,
138 virtual_memory: 0,
139 cpu_usage: 0.,
140 utime: 0,
141 stime: 0,
142 old_utime: 0,
143 old_stime: 0,
144 updated: true,
145 start_time_without_boot_time: 0,
146 start_time: 0,
147 run_time: 0,
148 user_id: None,
149 effective_user_id: None,
150 group_id: None,
151 effective_group_id: None,
152 status: ProcessStatus::Unknown(0),
153 tasks: None,
154 stat_file: None,
155 old_read_bytes: 0,
156 old_written_bytes: 0,
157 read_bytes: 0,
158 written_bytes: 0,
159 thread_kind: None,
160 proc_path,
161 }
162 }
163
164 pub(crate) fn kill_with(&self, signal: Signal) -> Option<bool> {
165 let c_signal = crate::sys::convert_signal(signal)?;
166 unsafe { Some(kill(self.pid.0, c_signal) == 0) }
167 }
168
169 pub(crate) fn name(&self) -> &str {
170 &self.name
171 }
172
173 pub(crate) fn cmd(&self) -> &[String] {
174 &self.cmd
175 }
176
177 pub(crate) fn exe(&self) -> Option<&Path> {
178 self.exe.as_deref()
179 }
180
181 pub(crate) fn pid(&self) -> Pid {
182 self.pid
183 }
184
185 pub(crate) fn environ(&self) -> &[String] {
186 &self.environ
187 }
188
189 pub(crate) fn cwd(&self) -> Option<&Path> {
190 self.cwd.as_deref()
191 }
192
193 pub(crate) fn root(&self) -> Option<&Path> {
194 self.root.as_deref()
195 }
196
197 pub(crate) fn memory(&self) -> u64 {
198 self.memory
199 }
200
201 pub(crate) fn virtual_memory(&self) -> u64 {
202 self.virtual_memory
203 }
204
205 pub(crate) fn parent(&self) -> Option<Pid> {
206 self.parent
207 }
208
209 pub(crate) fn status(&self) -> ProcessStatus {
210 self.status
211 }
212
213 pub(crate) fn start_time(&self) -> u64 {
214 self.start_time
215 }
216
217 pub(crate) fn run_time(&self) -> u64 {
218 self.run_time
219 }
220
221 pub(crate) fn cpu_usage(&self) -> f32 {
222 self.cpu_usage
223 }
224
225 pub(crate) fn disk_usage(&self) -> DiskUsage {
226 DiskUsage {
227 written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
228 total_written_bytes: self.written_bytes,
229 read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
230 total_read_bytes: self.read_bytes,
231 }
232 }
233
234 pub(crate) fn user_id(&self) -> Option<&Uid> {
235 self.user_id.as_ref()
236 }
237
238 pub(crate) fn effective_user_id(&self) -> Option<&Uid> {
239 self.effective_user_id.as_ref()
240 }
241
242 pub(crate) fn group_id(&self) -> Option<Gid> {
243 self.group_id
244 }
245
246 pub(crate) fn effective_group_id(&self) -> Option<Gid> {
247 self.effective_group_id
248 }
249
250 pub(crate) fn wait(&self) {
251 let mut status = 0;
252 unsafe {
254 if retry_eintr!(libc::waitpid(self.pid.0, &mut status, 0)) < 0 {
255 let duration = std::time::Duration::from_millis(10);
257 while kill(self.pid.0, 0) == 0 {
258 std::thread::sleep(duration);
259 }
260 }
261 }
262 }
263
264 pub(crate) fn session_id(&self) -> Option<Pid> {
265 unsafe {
266 let session_id = libc::getsid(self.pid.0);
267 if session_id < 0 {
268 None
269 } else {
270 Some(Pid(session_id))
271 }
272 }
273 }
274
275 pub(crate) fn thread_kind(&self) -> Option<ThreadKind> {
276 self.thread_kind
277 }
278}
279
280pub(crate) fn compute_cpu_usage(p: &mut ProcessInner, total_time: f32, max_value: f32) {
281 if p.old_utime == 0 && p.old_stime == 0 {
283 return;
284 }
285
286 p.cpu_usage = (p
289 .utime
290 .saturating_sub(p.old_utime)
291 .saturating_add(p.stime.saturating_sub(p.old_stime)) as f32
292 / total_time
293 * 100.)
294 .min(max_value);
295}
296
297pub(crate) fn unset_updated(p: &mut ProcessInner) {
298 p.updated = false;
299}
300
301pub(crate) fn set_time(p: &mut ProcessInner, utime: u64, stime: u64) {
302 p.old_utime = p.utime;
303 p.old_stime = p.stime;
304 p.utime = utime;
305 p.stime = stime;
306 p.updated = true;
307}
308
309pub(crate) fn update_process_disk_activity(p: &mut ProcessInner, path: &mut PathHandler) {
310 let data = match get_all_data(path.join("io"), 16_384) {
311 Ok(d) => d,
312 Err(_) => return,
313 };
314 let mut done = 0;
315 for line in data.split('\n') {
316 let mut parts = line.split(": ");
317 match parts.next() {
318 Some("read_bytes") => {
319 p.old_read_bytes = p.read_bytes;
320 p.read_bytes = parts
321 .next()
322 .and_then(|x| x.parse::<u64>().ok())
323 .unwrap_or(p.old_read_bytes);
324 }
325 Some("write_bytes") => {
326 p.old_written_bytes = p.written_bytes;
327 p.written_bytes = parts
328 .next()
329 .and_then(|x| x.parse::<u64>().ok())
330 .unwrap_or(p.old_written_bytes);
331 }
332 _ => continue,
333 }
334 done += 1;
335 if done > 1 {
336 break;
338 }
339 }
340}
341
342struct Wrap<'a, T>(UnsafeCell<&'a mut T>);
343
344impl<'a, T> Wrap<'a, T> {
345 fn get(&self) -> &'a mut T {
346 unsafe { *(self.0.get()) }
347 }
348}
349
350#[allow(clippy::non_send_fields_in_send_ty)]
351unsafe impl<'a, T> Send for Wrap<'a, T> {}
352unsafe impl<'a, T> Sync for Wrap<'a, T> {}
353
354#[inline(always)]
355fn compute_start_time_without_boot_time(parts: &[&str], info: &SystemInfo) -> u64 {
356 u64::from_str(parts[ProcIndex::StartTime as usize]).unwrap_or(0) / info.clock_cycle
359}
360
361fn _get_stat_data(path: &Path, stat_file: &mut Option<FileCounter>) -> Result<String, ()> {
362 let mut file = File::open(path.join("stat")).map_err(|_| ())?;
363 let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?;
364 *stat_file = FileCounter::new(file);
365 Ok(data)
366}
367
368#[inline(always)]
369fn get_status(p: &mut ProcessInner, part: &str) {
370 p.status = part
371 .chars()
372 .next()
373 .map(ProcessStatus::from)
374 .unwrap_or_else(|| ProcessStatus::Unknown(0));
375}
376
377fn refresh_user_group_ids(
378 p: &mut ProcessInner,
379 path: &mut PathHandler,
380 refresh_kind: ProcessRefreshKind,
381) {
382 if !refresh_kind.user().needs_update(|| p.user_id.is_none()) {
383 return;
384 }
385
386 if let Some(((user_id, effective_user_id), (group_id, effective_group_id))) =
387 get_uid_and_gid(path.join("status"))
388 {
389 p.user_id = Some(Uid(user_id));
390 p.effective_user_id = Some(Uid(effective_user_id));
391 p.group_id = Some(Gid(group_id));
392 p.effective_group_id = Some(Gid(effective_group_id));
393 }
394}
395
396#[allow(clippy::too_many_arguments)]
397fn update_proc_info(
398 p: &mut ProcessInner,
399 parent_pid: Option<Pid>,
400 refresh_kind: ProcessRefreshKind,
401 proc_path: &mut PathHandler,
402 parts: &[&str],
403 uptime: u64,
404 info: &SystemInfo,
405) {
406 update_parent_pid(p, parent_pid, parts);
407
408 get_status(p, parts[ProcIndex::State as usize]);
409 refresh_user_group_ids(p, proc_path, refresh_kind);
410
411 if refresh_kind.exe().needs_update(|| p.exe.is_none()) {
412 p.exe = realpath(proc_path.join("exe"));
415 }
416
417 if refresh_kind.cmd().needs_update(|| p.cmd.is_empty()) {
418 p.cmd = copy_from_file(proc_path.join("cmdline"));
419 }
420 if refresh_kind.environ().needs_update(|| p.environ.is_empty()) {
421 p.environ = copy_from_file(proc_path.join("environ"));
422 }
423 if refresh_kind.cwd().needs_update(|| p.cwd.is_none()) {
424 p.cwd = realpath(proc_path.join("cwd"));
425 }
426 if refresh_kind.root().needs_update(|| p.root.is_none()) {
427 p.root = realpath(proc_path.join("root"));
428 }
429
430 update_time_and_memory(proc_path, p, parts, uptime, info, refresh_kind);
431 if refresh_kind.disk_usage() {
432 update_process_disk_activity(p, proc_path);
433 }
434}
435
436fn update_parent_pid(p: &mut ProcessInner, parent_pid: Option<Pid>, str_parts: &[&str]) {
437 p.parent = match parent_pid {
438 Some(parent_pid) if parent_pid.0 != 0 => Some(parent_pid),
439 _ => match Pid::from_str(str_parts[ProcIndex::ParentPid as usize]) {
440 Ok(p) if p.0 != 0 => Some(p),
441 _ => None,
442 },
443 };
444}
445
446fn retrieve_all_new_process_info(
447 pid: Pid,
448 parent_pid: Option<Pid>,
449 parts: &[&str],
450 path: &Path,
451 info: &SystemInfo,
452 refresh_kind: ProcessRefreshKind,
453 uptime: u64,
454) -> Process {
455 let mut p = ProcessInner::new(pid, path.to_owned());
456 let mut proc_path = PathHandler::new(path);
457 let name = parts[ProcIndex::ShortExe as usize];
458
459 p.start_time_without_boot_time = compute_start_time_without_boot_time(parts, info);
460 p.start_time = p
461 .start_time_without_boot_time
462 .saturating_add(info.boot_time);
463
464 p.name = name.into();
465 if c_ulong::from_str(parts[ProcIndex::Flags as usize])
466 .map(|flags| flags & libc::PF_KTHREAD as c_ulong != 0)
467 .unwrap_or(false)
468 {
469 p.thread_kind = Some(ThreadKind::Kernel);
470 } else if parent_pid.is_some() {
471 p.thread_kind = Some(ThreadKind::Userland);
472 }
473
474 update_proc_info(
475 &mut p,
476 parent_pid,
477 refresh_kind,
478 &mut proc_path,
479 parts,
480 uptime,
481 info,
482 );
483
484 Process { inner: p }
485}
486
487pub(crate) fn _get_process_data(
488 path: &Path,
489 proc_list: &mut HashMap<Pid, Process>,
490 pid: Pid,
491 parent_pid: Option<Pid>,
492 uptime: u64,
493 info: &SystemInfo,
494 refresh_kind: ProcessRefreshKind,
495) -> Result<(Option<Process>, Pid), ()> {
496 let data;
497 let parts = if let Some(ref mut entry) = proc_list.get_mut(&pid) {
498 let entry = &mut entry.inner;
499 data = if let Some(mut f) = entry.stat_file.take() {
500 match get_all_data_from_file(&mut f, 1024) {
501 Ok(data) => {
502 entry.stat_file = Some(f);
504 data
505 }
506 Err(_) => {
507 _get_stat_data(&entry.proc_path, &mut entry.stat_file)?
510 }
511 }
512 } else {
513 _get_stat_data(path, &mut entry.stat_file)?
514 };
515 let parts = parse_stat_file(&data).ok_or(())?;
516 let start_time_without_boot_time = compute_start_time_without_boot_time(&parts, info);
517
518 if start_time_without_boot_time == entry.start_time_without_boot_time {
522 let mut proc_path = PathHandler::new(path);
523
524 update_proc_info(
525 entry,
526 parent_pid,
527 refresh_kind,
528 &mut proc_path,
529 &parts,
530 uptime,
531 info,
532 );
533
534 refresh_user_group_ids(entry, &mut proc_path, refresh_kind);
535 return Ok((None, pid));
536 }
537 parts
538 } else {
539 let mut stat_file = None;
540 let data = _get_stat_data(path, &mut stat_file)?;
541 let parts = parse_stat_file(&data).ok_or(())?;
542
543 let mut p = retrieve_all_new_process_info(
544 pid,
545 parent_pid,
546 &parts,
547 path,
548 info,
549 refresh_kind,
550 uptime,
551 );
552 p.inner.stat_file = stat_file;
553 return Ok((Some(p), pid));
554 };
555
556 let p =
558 retrieve_all_new_process_info(pid, parent_pid, &parts, path, info, refresh_kind, uptime);
559 match proc_list.get_mut(&pid) {
560 Some(ref mut entry) => **entry = p,
561 None => unreachable!(),
564 }
565 Ok((None, pid))
567}
568
569fn old_get_memory(entry: &mut ProcessInner, parts: &[&str], info: &SystemInfo) {
570 entry.memory = u64::from_str(parts[ProcIndex::ResidentSetSize as usize])
572 .unwrap_or(0)
573 .saturating_mul(info.page_size_b);
574 entry.virtual_memory = u64::from_str(parts[ProcIndex::VirtualSize as usize]).unwrap_or(0);
577}
578
579fn slice_to_nb(s: &[u8]) -> u64 {
580 let mut nb: u64 = 0;
581
582 for c in s {
583 nb = nb * 10 + (c - b'0') as u64;
584 }
585 nb
586}
587
588fn get_memory(path: &Path, entry: &mut ProcessInner, info: &SystemInfo) -> bool {
589 let mut file = match File::open(path) {
590 Ok(f) => f,
591 Err(_e) => {
592 sysinfo_debug!(
593 "Using old memory information (failed to open {:?}: {_e:?})",
594 path
595 );
596 return false;
597 }
598 };
599 let mut buf = Vec::new();
600 if let Err(_e) = file.read_to_end(&mut buf) {
601 sysinfo_debug!(
602 "Using old memory information (failed to read {:?}: {_e:?})",
603 path
604 );
605 return false;
606 }
607 let mut parts = buf.split(|c| *c == b' ');
608 entry.virtual_memory = parts
609 .next()
610 .map(slice_to_nb)
611 .unwrap_or(0)
612 .saturating_mul(info.page_size_b);
613 entry.memory = parts
614 .next()
615 .map(slice_to_nb)
616 .unwrap_or(0)
617 .saturating_mul(info.page_size_b);
618 true
619}
620
621#[allow(clippy::too_many_arguments)]
622fn update_time_and_memory(
623 path: &mut PathHandler,
624 entry: &mut ProcessInner,
625 parts: &[&str],
626 uptime: u64,
627 info: &SystemInfo,
628 refresh_kind: ProcessRefreshKind,
629) {
630 {
631 #[allow(clippy::collapsible_if)]
632 if refresh_kind.memory() {
633 if !get_memory(path.join("statm"), entry, info) {
635 old_get_memory(entry, parts, info);
636 }
637 }
638 set_time(
639 entry,
640 u64::from_str(parts[ProcIndex::UserTime as usize]).unwrap_or(0),
641 u64::from_str(parts[ProcIndex::SystemTime as usize]).unwrap_or(0),
642 );
643 entry.run_time = uptime.saturating_sub(entry.start_time_without_boot_time);
644 }
645}
646
647struct ProcAndTasks {
648 pid: Pid,
649 parent_pid: Option<Pid>,
650 path: PathBuf,
651 tasks: Option<HashSet<Pid>>,
652}
653
654fn get_all_pid_entries(
655 parent: Option<&OsStr>,
656 parent_pid: Option<Pid>,
657 entry: DirEntry,
658 data: &mut Vec<ProcAndTasks>,
659) -> Option<Pid> {
660 let Ok(file_type) = entry.file_type() else {
661 return None;
662 };
663 if !file_type.is_dir() {
664 return None;
665 }
666
667 let entry = entry.path();
668 let name = entry.file_name();
669
670 if name == parent {
671 return None;
673 }
674 let name = name?;
675 let pid = Pid::from(usize::from_str(&name.to_string_lossy()).ok()?);
676
677 let tasks_dir = Path::join(&entry, "task");
678
679 let tasks = if let Ok(entries) = fs::read_dir(tasks_dir) {
680 let mut tasks = HashSet::new();
681 for task in entries
682 .into_iter()
683 .filter_map(|entry| get_all_pid_entries(Some(name), Some(pid), entry.ok()?, data))
684 {
685 tasks.insert(task);
686 }
687 Some(tasks)
688 } else {
689 None
690 };
691
692 data.push(ProcAndTasks {
693 pid,
694 parent_pid,
695 path: entry,
696 tasks,
697 });
698 Some(pid)
699}
700
701#[cfg(feature = "multithread")]
702#[inline]
703pub(crate) fn iter<T>(val: T) -> rayon::iter::IterBridge<T>
704where
705 T: rayon::iter::ParallelBridge,
706{
707 val.par_bridge()
708}
709
710#[cfg(not(feature = "multithread"))]
711#[inline]
712pub(crate) fn iter<T>(val: T) -> T
713where
714 T: Iterator,
715{
716 val
717}
718
719pub(crate) fn refresh_procs(
720 proc_list: &mut HashMap<Pid, Process>,
721 path: &Path,
722 uptime: u64,
723 info: &SystemInfo,
724 filter: Option<&[Pid]>,
725 refresh_kind: ProcessRefreshKind,
726) -> bool {
727 #[cfg(feature = "multithread")]
728 use rayon::iter::ParallelIterator;
729
730 #[inline(always)]
731 fn real_filter(e: &ProcAndTasks, filter: &[Pid]) -> bool {
732 filter.contains(&e.pid)
733 }
734
735 #[inline(always)]
736 fn empty_filter(_e: &ProcAndTasks, _filter: &[Pid]) -> bool {
737 true
738 }
739
740 #[allow(clippy::type_complexity)]
741 let (filter, filter_callback): (
742 &[Pid],
743 &(dyn Fn(&ProcAndTasks, &[Pid]) -> bool + Sync + Send),
744 ) = if let Some(filter) = filter {
745 (filter, &real_filter)
746 } else {
747 (&[], &empty_filter)
748 };
749
750 let procs = {
753 let d = match fs::read_dir(path) {
754 Ok(d) => d,
755 Err(_) => return false,
756 };
757 let proc_list = Wrap(UnsafeCell::new(proc_list));
758
759 iter(d)
760 .map(|entry| {
761 let Ok(entry) = entry else { return Vec::new() };
762 let mut entries = Vec::new();
763 get_all_pid_entries(None, None, entry, &mut entries);
764 entries
765 })
766 .flatten()
767 .filter(|e| filter_callback(e, filter))
768 .filter_map(|e| {
769 let (mut p, _) = _get_process_data(
770 e.path.as_path(),
771 proc_list.get(),
772 e.pid,
773 e.parent_pid,
774 uptime,
775 info,
776 refresh_kind,
777 )
778 .ok()?;
779 if let Some(ref mut p) = p {
780 p.inner.tasks = e.tasks;
781 }
782 p
783 })
784 .collect::<Vec<_>>()
785 };
786 for proc_ in procs {
787 proc_list.insert(proc_.pid(), proc_);
788 }
789 true
790}
791
792fn copy_from_file(entry: &Path) -> Vec<String> {
793 match File::open(entry) {
794 Ok(mut f) => {
795 let mut data = Vec::with_capacity(16_384);
796
797 if let Err(_e) = f.read_to_end(&mut data) {
798 sysinfo_debug!("Failed to read file in `copy_from_file`: {:?}", _e);
799 Vec::new()
800 } else {
801 let mut out = Vec::with_capacity(10);
802 let mut data = data.as_slice();
803 while let Some(pos) = data.iter().position(|c| *c == 0) {
804 match std::str::from_utf8(&data[..pos]).map(|s| s.trim()) {
805 Ok(s) if !s.is_empty() => out.push(s.to_string()),
806 _ => {}
807 }
808 data = &data[pos + 1..];
809 }
810 out
811 }
812 }
813 Err(_e) => {
814 sysinfo_debug!("Failed to open file in `copy_from_file`: {:?}", _e);
815 Vec::new()
816 }
817 }
818}
819
820fn get_uid_and_gid(file_path: &Path) -> Option<((uid_t, uid_t), (gid_t, gid_t))> {
822 let status_data = get_all_data(file_path, 16_385).ok()?;
823
824 let f = |h: &str, n: &str| -> (Option<uid_t>, Option<uid_t>) {
829 if h.starts_with(n) {
830 let mut ids = h.split_whitespace();
831 let real = ids.nth(1).unwrap_or("0").parse().ok();
832 let effective = ids.next().unwrap_or("0").parse().ok();
833
834 (real, effective)
835 } else {
836 (None, None)
837 }
838 };
839 let mut uid = None;
840 let mut effective_uid = None;
841 let mut gid = None;
842 let mut effective_gid = None;
843 for line in status_data.lines() {
844 if let (Some(real), Some(effective)) = f(line, "Uid:") {
845 debug_assert!(uid.is_none() && effective_uid.is_none());
846 uid = Some(real);
847 effective_uid = Some(effective);
848 } else if let (Some(real), Some(effective)) = f(line, "Gid:") {
849 debug_assert!(gid.is_none() && effective_gid.is_none());
850 gid = Some(real);
851 effective_gid = Some(effective);
852 } else {
853 continue;
854 }
855 if uid.is_some() && gid.is_some() {
856 break;
857 }
858 }
859 match (uid, effective_uid, gid, effective_gid) {
860 (Some(uid), Some(effective_uid), Some(gid), Some(effective_gid)) => {
861 Some(((uid, effective_uid), (gid, effective_gid)))
862 }
863 _ => None,
864 }
865}
866
867fn parse_stat_file(data: &str) -> Option<Vec<&str>> {
868 let mut parts = Vec::with_capacity(52);
878 let mut data_it = data.splitn(2, ' ');
879 parts.push(data_it.next()?);
880 let mut data_it = data_it.next()?.rsplitn(2, ')');
881 let data = data_it.next()?;
882 parts.push(data_it.next()?);
883 parts.extend(data.split_whitespace());
884 if let Some(name) = parts[ProcIndex::ShortExe as usize].strip_prefix('(') {
886 parts[ProcIndex::ShortExe as usize] = name;
887 }
888 Some(parts)
889}