|
| 1 | +// This file is part of the uutils procps package. |
| 2 | +// |
| 3 | +// For the full copyright and license information, please view the LICENSE |
| 4 | +// file that was distributed with this source code. |
| 5 | + |
| 6 | +use clap::ArgMatches; |
| 7 | +use std::collections::HashSet; |
| 8 | +use uu_pgrep::process::{walk_process, ProcessInformation, RunState, Teletype}; |
| 9 | +use uucore::error::UResult; |
| 10 | + |
| 11 | +#[cfg(target_family = "unix")] |
| 12 | +use nix::errno::Errno; |
| 13 | + |
| 14 | +// TODO: Temporary add to this file, this function will add to uucore. |
| 15 | +#[cfg(not(target_os = "redox"))] |
| 16 | +#[cfg(target_family = "unix")] |
| 17 | +fn getsid(pid: i32) -> Option<i32> { |
| 18 | + unsafe { |
| 19 | + let result = libc::getsid(pid); |
| 20 | + if Errno::last() == Errno::UnknownErrno { |
| 21 | + Some(result) |
| 22 | + } else { |
| 23 | + None |
| 24 | + } |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +// TODO: Temporary add to this file, this function will add to uucore. |
| 29 | +#[cfg(target_family = "windows")] |
| 30 | +fn getsid(_pid: i32) -> Option<i32> { |
| 31 | + Some(0) |
| 32 | +} |
| 33 | + |
| 34 | +fn is_session_leader(process: &ProcessInformation) -> bool { |
| 35 | + let pid = process.pid as i32; |
| 36 | + getsid(pid) == Some(pid) |
| 37 | +} |
| 38 | + |
| 39 | +pub struct ProcessSelectionSettings { |
| 40 | + /// - `-A` Select all processes. Identical to `-e`. |
| 41 | + pub select_all: bool, |
| 42 | + /// - `-a` Select all processes except both session leaders (see getsid(2)) and processes not associated with a terminal. |
| 43 | + pub select_non_session_leaders_with_tty: bool, |
| 44 | + /// - `-d` Select all processes except session leaders. |
| 45 | + pub select_non_session_leaders: bool, |
| 46 | + |
| 47 | + /// - '-x' Lift "must have a tty" restriction. |
| 48 | + pub dont_require_tty: bool, |
| 49 | + |
| 50 | + /// Select specific process IDs (-p, --pid) |
| 51 | + pub pids: Option<HashSet<usize>>, |
| 52 | + |
| 53 | + /// - `-r` Restrict the selection to only running processes. |
| 54 | + pub only_running: bool, |
| 55 | + |
| 56 | + /// - `--deselect` Negates the selection. |
| 57 | + pub negate_selection: bool, |
| 58 | +} |
| 59 | + |
| 60 | +impl ProcessSelectionSettings { |
| 61 | + pub fn from_matches(matches: &ArgMatches) -> Self { |
| 62 | + Self { |
| 63 | + select_all: matches.get_flag("A"), |
| 64 | + select_non_session_leaders_with_tty: matches.get_flag("a"), |
| 65 | + select_non_session_leaders: matches.get_flag("d"), |
| 66 | + dont_require_tty: matches.get_flag("x"), |
| 67 | + pids: matches |
| 68 | + .get_many::<Vec<usize>>("pid") |
| 69 | + .map(|xs| xs.flatten().copied().collect()), |
| 70 | + only_running: matches.get_flag("r"), |
| 71 | + negate_selection: matches.get_flag("deselect"), |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + pub fn select_processes(self) -> UResult<Vec<ProcessInformation>> { |
| 76 | + let mut current_process = ProcessInformation::current_process_info().unwrap(); |
| 77 | + let current_tty = current_process.tty(); |
| 78 | + let current_euid = current_process.euid().unwrap(); |
| 79 | + |
| 80 | + let matches_criteria = |process: &mut ProcessInformation| -> UResult<bool> { |
| 81 | + if self.only_running && !process.run_state().is_ok_and(|x| x == RunState::Running) { |
| 82 | + return Ok(false); |
| 83 | + } |
| 84 | + |
| 85 | + if let Some(ref pids) = self.pids { |
| 86 | + return Ok(pids.contains(&process.pid)); |
| 87 | + } |
| 88 | + |
| 89 | + if self.select_all { |
| 90 | + return Ok(true); |
| 91 | + } |
| 92 | + |
| 93 | + if self.select_non_session_leaders_with_tty { |
| 94 | + return Ok(!is_session_leader(process) && process.tty() != Teletype::Unknown); |
| 95 | + } |
| 96 | + |
| 97 | + if self.select_non_session_leaders { |
| 98 | + return Ok(!is_session_leader(process)); |
| 99 | + } |
| 100 | + |
| 101 | + // Default behavior: select processes with same effective user ID and same tty (except -x removes tty restriction) |
| 102 | + Ok(process.euid().unwrap() == current_euid |
| 103 | + && (self.dont_require_tty || process.tty() == current_tty)) |
| 104 | + }; |
| 105 | + |
| 106 | + let mut selected = vec![]; |
| 107 | + for mut process in walk_process() { |
| 108 | + if matches_criteria(&mut process)? ^ self.negate_selection { |
| 109 | + selected.push(process); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + Ok(selected) |
| 114 | + } |
| 115 | +} |
0 commit comments