From f223b379d1c9a2033de6e2b0a48def9386240755 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Mon, 26 Oct 2026 01:19:51 +0530 Subject: [PATCH] pgrep: compare --older with the process's age The start time in /proc//stat counts clock ticks since boot, so comparing it with the --older seconds matched nearly every process. Compare the time since the process started instead. --- src/uu/pgrep/src/process_matcher.rs | 35 +++++++++++++++++++++++++++-- tests/by-util/test_pgrep.rs | 18 +++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/uu/pgrep/src/process_matcher.rs b/src/uu/pgrep/src/process_matcher.rs index e82ef1af..0cf740c1 100644 --- a/src/uu/pgrep/src/process_matcher.rs +++ b/src/uu/pgrep/src/process_matcher.rs @@ -242,6 +242,23 @@ fn get_namespaces(_pid: usize, _list: Option>) -> UResult { Ok(Namespace::new()) } +/// Return the time since boot in clock ticks, the unit of a process's start +/// time in /proc//stat. +#[cfg(target_os = "linux")] +fn ticks_since_boot() -> u64 { + let uptime = std::fs::read_to_string("/proc/uptime") + .ok() + .and_then(|s| s.split_whitespace().next()?.parse::().ok()) + .unwrap_or(0.0); + (uptime * clock_ticks_per_second() as f64) as u64 +} + +#[cfg(target_os = "linux")] +fn clock_ticks_per_second() -> u64 { + // SAFETY: sysconf has no preconditions. + unsafe { uucore::libc::sysconf(uucore::libc::_SC_CLK_TCK) as u64 } +} + /// Collect pids with filter construct from command line arguments fn collect_matched_pids(settings: &Settings) -> UResult> { // Filtration general parameters @@ -260,6 +277,15 @@ fn collect_matched_pids(settings: &Settings) -> UResult> HashSet::from([our_pid]) }; + // --older compares a process's age with the given seconds. + #[cfg(target_os = "linux")] + let older_than_ticks = settings.older.map(|secs| { + ( + ticks_since_boot(), + secs.saturating_mul(clock_ticks_per_second()), + ) + }); + let pid_from_pidfile = settings .pidfile .as_ref() @@ -299,8 +325,13 @@ fn collect_matched_pids(settings: &Settings) -> UResult> let tty_matched = any_matches(&settings.terminal, pid.tty()); - let arg_older = settings.older.unwrap_or(0); - let older_matched = pid.start_time().unwrap() >= arg_older; + #[cfg(target_os = "linux")] + let older_matched = match older_than_ticks { + Some((now, min_age)) => now.saturating_sub(pid.start_time().unwrap()) >= min_age, + None => true, + }; + #[cfg(not(target_os = "linux"))] + let older_matched = true; let parent_matched = any_matches(&settings.parent, pid.ppid().unwrap()); let pgroup_matched = any_matches(&settings.pgroup, pid.pgid().unwrap()); diff --git a/tests/by-util/test_pgrep.rs b/tests/by-util/test_pgrep.rs index efa3f2fe..de4975f9 100644 --- a/tests/by-util/test_pgrep.rs +++ b/tests/by-util/test_pgrep.rs @@ -131,6 +131,24 @@ fn test_older_non_matching_pattern() { .no_stdout(); } +#[test] +#[cfg(target_os = "linux")] +fn test_older_compares_process_age() { + // A process started just now is younger than a minute. + let mut child = Command::new("sleep").arg("10").spawn().unwrap(); + let pid = child.id().to_string(); + + let result = new_ucmd!() + .args(&["--older", "0", "-x", "sleep"]) + .succeeds(); + assert!(result.stdout_str().lines().any(|line| line == pid)); + let result = new_ucmd!().args(&["--older", "60", "-x", "sleep"]).run(); + assert!(!result.stdout_str().lines().any(|line| line == pid)); + + child.kill().unwrap(); + child.wait().unwrap(); +} + #[test] #[cfg(target_os = "linux")] fn test_full() {