diff --git a/src/uu/pgrep/src/process_matcher.rs b/src/uu/pgrep/src/process_matcher.rs index 430abf7f..47799c39 100644 --- a/src/uu/pgrep/src/process_matcher.rs +++ b/src/uu/pgrep/src/process_matcher.rs @@ -51,6 +51,7 @@ pub struct Settings { pub pidfile: Option, pub logpidfile: bool, + pub ignore_ancestors: bool, } pub fn get_match_settings(matches: &ArgMatches) -> UResult { @@ -113,6 +114,7 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult { threads: false, pidfile: matches.get_one::("pidfile").cloned(), logpidfile: matches.get_flag("logpidfile"), + ignore_ancestors: matches.get_flag("ignore-ancestors"), }; if !settings.newest @@ -196,18 +198,37 @@ fn any_matches(optional_ids: &Option>, id: T) -> bool { optional_ids.as_ref().is_none_or(|ids| ids.contains(&id)) } +fn get_ancestors(process_infos: &mut [ProcessInformation], mut pid: usize) -> HashSet { + let mut ret = HashSet::from([pid]); + while pid != 1 { + if let Some(process) = process_infos.iter_mut().find(|p| p.pid == pid) { + pid = process.ppid().unwrap() as usize; + ret.insert(pid); + } else { + break; + } + } + ret +} + /// Collect pids with filter construct from command line arguments fn collect_matched_pids(settings: &Settings) -> UResult> { // Filtration general parameters let filtered: Vec = { let mut tmp_vec = Vec::new(); - let pids = if settings.threads { + let mut pids = if settings.threads { walk_threads().collect::>() } else { walk_process().collect::>() }; let our_pid = std::process::id() as usize; + let ignored_pids = if settings.ignore_ancestors { + get_ancestors(&mut pids, our_pid) + } else { + HashSet::from([our_pid]) + }; + let pid_from_pidfile = settings .pidfile .as_ref() @@ -215,7 +236,7 @@ fn collect_matched_pids(settings: &Settings) -> UResult> .transpose()?; for mut pid in pids { - if pid.pid == our_pid { + if ignored_pids.contains(&pid.pid) { continue; } @@ -507,7 +528,7 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec { arg!(-F --pidfile "read PIDs from file"), arg!(-L --logpidfile "fail if PID file is not locked"), arg!(-r --runstates "match runstates [D,S,Z,...]"), - // arg!(-A --"ignore-ancestors" "exclude our ancestors from results"), + arg!(-A --"ignore-ancestors" "exclude our ancestors from results"), arg!(--cgroup "match by cgroup v2 names").value_delimiter(','), // arg!(--ns "match the processes that belong to the same namespace as "), // arg!(--nslist "list which namespaces will be considered for the --ns option.") diff --git a/tests/by-util/test_pgrep.rs b/tests/by-util/test_pgrep.rs index c25f9692..30cac607 100644 --- a/tests/by-util/test_pgrep.rs +++ b/tests/by-util/test_pgrep.rs @@ -673,3 +673,15 @@ fn test_pidfile_fcntl_locked() { flock_process.kill().unwrap(); flock_process.wait().unwrap(); } + +#[test] +#[cfg(target_os = "linux")] +fn test_ignore_ancestors() { + let our_pid = std::process::id(); + new_ucmd!() + .arg("--ignore-ancestors") + .arg(".*") + .succeeds() + .stdout_does_not_match(&Regex::new(&format!("(?m)^{our_pid}$")).unwrap()) + .stdout_does_not_match(&Regex::new("(?m)^1$").unwrap()); +}