From 6dd29c59b8ead9c97b45315280312cd5b3fc9910 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Wed, 26 Feb 2025 22:16:34 +0200 Subject: [PATCH] process_matcher: Add support for --pgroup and --session --- src/uu/pgrep/src/process_matcher.rs | 58 ++++++++++++++++++++++++++--- tests/by-util/test_pgrep.rs | 48 ++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/src/uu/pgrep/src/process_matcher.rs b/src/uu/pgrep/src/process_matcher.rs index 460e947..e3d722d 100644 --- a/src/uu/pgrep/src/process_matcher.rs +++ b/src/uu/pgrep/src/process_matcher.rs @@ -11,6 +11,8 @@ use std::{collections::HashSet, io}; use clap::{arg, Arg, ArgAction, ArgMatches}; use regex::Regex; #[cfg(unix)] +use uucore::libc::{getpgrp, getsid}; +#[cfg(unix)] use uucore::{ display::Quotable, entries::{grp2gid, usr2uid}, @@ -40,6 +42,8 @@ pub struct Settings { pub uid: Option>, pub euid: Option>, pub gid: Option>, + pub pgroup: Option>, + pub session: Option>, } pub fn get_match_settings(matches: &ArgMatches) -> UResult { @@ -76,6 +80,26 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult { gid: matches .get_many::("group") .map(|ids| ids.cloned().collect()), + pgroup: matches.get_many::("pgroup").map(|xs| { + xs.map(|pg| { + if *pg == 0 { + unsafe { getpgrp() as u64 } + } else { + *pg + } + }) + .collect() + }), + session: matches.get_many::("session").map(|xs| { + xs.map(|sid| { + if *sid == 0 { + unsafe { getsid(0) as u64 } + } else { + *sid + } + }) + .collect() + }), }; if !settings.newest @@ -87,6 +111,8 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult { && settings.uid.is_none() && settings.euid.is_none() && settings.gid.is_none() + && settings.pgroup.is_none() + && settings.session.is_none() && pattern.is_empty() { return Err(USimpleError::new( @@ -207,6 +233,8 @@ fn collect_matched_pids(settings: &Settings) -> Vec { let older_matched = pid.start_time().unwrap() >= arg_older; let parent_matched = any_matches(&settings.parent, pid.ppid().unwrap()); + let pgroup_matched = any_matches(&settings.pgroup, pid.pgid().unwrap()); + let session_matched = any_matches(&settings.session, pid.sid().unwrap()); let ids_matched = any_matches(&settings.uid, pid.uid().unwrap()) && any_matches(&settings.euid, pid.euid().unwrap()) @@ -217,6 +245,8 @@ fn collect_matched_pids(settings: &Settings) -> Vec { && tty_matched && older_matched && parent_matched + && pgroup_matched + && session_matched && ids_matched) ^ settings.inverse { @@ -290,6 +320,22 @@ pub fn grp2gid(_name: &str) -> io::Result { )) } +/// # Safety +/// +/// Dummy implementation for unsupported platforms. +#[cfg(not(unix))] +pub unsafe fn getpgrp() -> u32 { + panic!("unsupported on this platform"); +} + +/// # Safety +/// +/// Dummy implementation for unsupported platforms. +#[cfg(not(unix))] +pub unsafe fn getsid(_pid: u32) -> u32 { + panic!("unsupported on this platform"); +} + fn parse_uid_or_username(uid_or_username: &str) -> io::Result { uid_or_username .parse::() @@ -315,9 +361,9 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec { arg!(-H --"require-handler" "match only if signal handler is present"), arg!(-c --count "count of matching processes"), arg!(-f --full "use full process name to match"), - // arg!(-g --pgroup "match listed process group IDs") - // .value_delimiter(',') - // .value_parser(clap::value_parser!(u64)), + arg!(-g --pgroup "match listed process group IDs") + .value_delimiter(',') + .value_parser(clap::value_parser!(u64)), arg!(-G --group "match real group IDs") .value_delimiter(',') .value_parser(parse_gid_or_group_name), @@ -331,9 +377,9 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec { arg!(-P --parent "match only child processes of the given parent") .value_delimiter(',') .value_parser(clap::value_parser!(u64)), - // arg!(-s --session "match session IDs") - // .value_delimiter(',') - // .value_parser(clap::value_parser!(u64)), + arg!(-s --session "match session IDs") + .value_delimiter(',') + .value_parser(clap::value_parser!(u64)), arg!(--signal "signal to send (either number or name)") .default_value("SIGTERM"), arg!(-t --terminal "match by controlling terminal").value_delimiter(','), diff --git a/tests/by-util/test_pgrep.rs b/tests/by-util/test_pgrep.rs index 5dd0a7a..b03c6de 100644 --- a/tests/by-util/test_pgrep.rs +++ b/tests/by-util/test_pgrep.rs @@ -430,3 +430,51 @@ fn test_does_not_match_current_process() { .fails() .no_output(); } + +#[test] +#[cfg(target_os = "linux")] +fn test_pgroup() { + let our_pid = std::process::id(); + let our_pgroup = unsafe { uucore::libc::getpgid(0) }; + new_ucmd!() + .arg("--pgroup") + .arg(our_pgroup.to_string()) + .succeeds() + .stdout_contains(our_pid.to_string()); + + new_ucmd!() + .arg("--pgroup") + .arg("0") + .succeeds() + .stdout_contains(our_pid.to_string()); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_nonexisting_pgroup() { + new_ucmd!().arg("--pgroup=9999999999").fails(); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_session() { + let our_pid = std::process::id(); + let our_sid = unsafe { uucore::libc::getsid(0) }; + new_ucmd!() + .arg("--session") + .arg(our_sid.to_string()) + .succeeds() + .stdout_contains(our_pid.to_string()); + + new_ucmd!() + .arg("--session") + .arg("0") + .succeeds() + .stdout_contains(our_pid.to_string()); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_nonexisting_session() { + new_ucmd!().arg("--session=9999999999").fails(); +}