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