ps: Refactor process selection

Refactor the process selection to better support cases where the
behaviour is more complex than simply union of what each individual flag
would list. This is bit similar to what is used in pgrep.
This commit is contained in:
Tuomas Tynkkynen
2025-10-31 22:48:05 +02:00
parent 0d4b961446
commit c63a93da53
4 changed files with 96 additions and 129 deletions
-107
View File
@@ -1,107 +0,0 @@
// This file is part of the uutils procps package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use clap::ArgMatches;
#[cfg(target_family = "unix")]
use nix::errno::Errno;
use std::{cell::RefCell, rc::Rc};
use uu_pgrep::process::{ProcessInformation, Teletype};
// TODO: Temporary add to this file, this function will add to uucore.
#[cfg(not(target_os = "redox"))]
#[cfg(target_family = "unix")]
fn getsid(pid: i32) -> Option<i32> {
unsafe {
let result = libc::getsid(pid);
if Errno::last() == Errno::UnknownErrno {
Some(result)
} else {
None
}
}
}
// TODO: Temporary add to this file, this function will add to uucore.
#[cfg(target_family = "windows")]
fn getsid(_pid: i32) -> Option<i32> {
Some(0)
}
// Default behavior: select processes with same terminal and same effective user ID
pub(crate) fn basic_collector(
proc_snapshot: &[Rc<RefCell<ProcessInformation>>],
) -> Vec<Rc<RefCell<ProcessInformation>>> {
let mut result = Vec::new();
let mut cur = ProcessInformation::current_process_info().unwrap();
let tty = cur.tty();
let euid = cur.euid().unwrap();
for proc_info in proc_snapshot {
if proc_info.borrow().tty() == tty && proc_info.borrow_mut().euid().unwrap() == euid {
result.push(proc_info.clone());
}
}
result
}
/// Filter for processes
///
/// - `-A` Select all processes. Identical to `-e`.
pub(crate) fn process_collector(
matches: &ArgMatches,
proc_snapshot: &[Rc<RefCell<ProcessInformation>>],
) -> Vec<Rc<RefCell<ProcessInformation>>> {
let mut result = Vec::new();
// flag `-A`
if matches.get_flag("A") {
result.extend(proc_snapshot.iter().map(Rc::clone));
}
result
}
/// Filter for session
///
/// - `-d` Select all processes except session leaders.
/// - `-a` Select all processes except both session leaders (see getsid(2)) and processes not associated with a terminal.
pub(crate) fn session_collector(
matches: &ArgMatches,
proc_snapshot: &[Rc<RefCell<ProcessInformation>>],
) -> Vec<Rc<RefCell<ProcessInformation>>> {
let mut result = Vec::new();
let tty = |proc: &Rc<RefCell<ProcessInformation>>| proc.borrow_mut().tty();
// flag `-d`
if matches.get_flag("d") {
for proc_info in proc_snapshot {
let pid = proc_info.borrow().pid;
if getsid(pid as i32) != Some(pid as i32) {
result.push(proc_info.clone());
}
}
}
// flag `-a`
// Guessing it pid=sid, and associated terminal.
if matches.get_flag("a") {
for it in proc_snapshot {
let pid = it.borrow().pid;
if let Some(sid) = getsid(pid as i32) {
// Check is session leader
if sid != (pid as i32) && tty(it) != Teletype::Unknown {
result.push(it.clone());
}
}
}
}
result
}
+87
View File
@@ -0,0 +1,87 @@
// This file is part of the uutils procps package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use clap::ArgMatches;
use uu_pgrep::process::{walk_process, ProcessInformation, Teletype};
use uucore::error::UResult;
#[cfg(target_family = "unix")]
use nix::errno::Errno;
// TODO: Temporary add to this file, this function will add to uucore.
#[cfg(not(target_os = "redox"))]
#[cfg(target_family = "unix")]
fn getsid(pid: i32) -> Option<i32> {
unsafe {
let result = libc::getsid(pid);
if Errno::last() == Errno::UnknownErrno {
Some(result)
} else {
None
}
}
}
// TODO: Temporary add to this file, this function will add to uucore.
#[cfg(target_family = "windows")]
fn getsid(_pid: i32) -> Option<i32> {
Some(0)
}
fn is_session_leader(process: &ProcessInformation) -> bool {
let pid = process.pid as i32;
getsid(pid) == Some(pid)
}
pub struct ProcessSelectionSettings {
/// - `-A` Select all processes. Identical to `-e`.
pub select_all: bool,
/// - `-a` Select all processes except both session leaders (see getsid(2)) and processes not associated with a terminal.
pub select_non_session_leaders_with_tty: bool,
/// - `-d` Select all processes except session leaders.
pub select_non_session_leaders: bool,
}
impl ProcessSelectionSettings {
pub fn from_matches(matches: &ArgMatches) -> Self {
Self {
select_all: matches.get_flag("A"),
select_non_session_leaders_with_tty: matches.get_flag("a"),
select_non_session_leaders: matches.get_flag("d"),
}
}
pub fn select_processes(self) -> UResult<Vec<ProcessInformation>> {
let mut current_process = ProcessInformation::current_process_info().unwrap();
let current_tty = current_process.tty();
let current_euid = current_process.euid().unwrap();
let matches_criteria = |process: &mut ProcessInformation| -> UResult<bool> {
if self.select_all {
return Ok(true);
}
if self.select_non_session_leaders_with_tty {
return Ok(!is_session_leader(process) && process.tty() != Teletype::Unknown);
}
if self.select_non_session_leaders {
return Ok(!is_session_leader(process));
}
// Default behavior: select processes with same terminal and same effective user ID
Ok(process.tty() == current_tty && process.euid().unwrap() == current_euid)
};
let mut selected = vec![];
for mut process in walk_process() {
if matches_criteria(&mut process)? {
selected.push(process);
}
}
Ok(selected)
}
}
+6 -18
View File
@@ -3,10 +3,10 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
mod collector;
mod mapping;
mod parser;
mod picker;
mod process_selection;
mod sorting;
use clap::crate_version;
@@ -19,8 +19,8 @@ use mapping::{
};
use parser::{parser, OptionalKeyValue};
use prettytable::{format::consts::FORMAT_CLEAN, Row, Table};
use std::{cell::RefCell, rc::Rc};
use uu_pgrep::process::walk_process;
use process_selection::ProcessSelectionSettings;
use std::cell::RefCell;
use uucore::{
error::{UError, UResult, USimpleError},
format_usage, help_about, help_usage,
@@ -33,20 +33,8 @@ const USAGE: &str = help_usage!("ps.md");
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
let snapshot = walk_process()
.map(|it| Rc::new(RefCell::new(it)))
.collect::<Vec<_>>();
let mut proc_infos = Vec::new();
if !matches.get_flag("A") && !matches.get_flag("a") && !matches.get_flag("d") {
proc_infos.extend(collector::basic_collector(&snapshot));
} else {
proc_infos.extend(collector::process_collector(&matches, &snapshot));
proc_infos.extend(collector::session_collector(&matches, &snapshot));
}
proc_infos.sort_by(|a, b| a.borrow().pid.cmp(&b.borrow().pid));
proc_infos.dedup_by(|a, b| a.borrow().pid == b.borrow().pid);
let selection_settings = ProcessSelectionSettings::from_matches(&matches);
let mut proc_infos = selection_settings.select_processes()?;
sorting::sort(&mut proc_infos, &matches);
@@ -90,7 +78,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
for proc in proc_infos {
let picked = pickers
.iter()
.map(|picker| picker(Rc::unwrap_or_clone(proc.clone())));
.map(|picker| picker(RefCell::new(proc.clone())));
rows.push(Row::from_iter(picked));
}
+3 -4
View File
@@ -4,15 +4,14 @@
// file that was distributed with this source code.
use clap::ArgMatches;
use std::{cell::RefCell, rc::Rc};
use uu_pgrep::process::ProcessInformation;
// TODO: Implementing sorting flags.
pub(crate) fn sort(input: &mut [Rc<RefCell<ProcessInformation>>], _matches: &ArgMatches) {
pub(crate) fn sort(input: &mut [ProcessInformation], _matches: &ArgMatches) {
sort_by_pid(input);
}
/// Sort by pid. (Default)
fn sort_by_pid(input: &mut [Rc<RefCell<ProcessInformation>>]) {
input.sort_by(|a, b| a.borrow().pid.cmp(&b.borrow().pid));
fn sort_by_pid(input: &mut [ProcessInformation]) {
input.sort_by(|a, b| a.pid.cmp(&b.pid));
}