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 1/3] 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() { From ea9a62f37cfa130ecb14bc6a5b5703b91a77a38b Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Mon, 9 Nov 2026 14:23:39 +0530 Subject: [PATCH 2/3] pgrep: read the uptime through uucore and report when it fails --- src/uu/pgrep/Cargo.toml | 2 +- src/uu/pgrep/src/process_matcher.rs | 39 +++++++++++++---------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/uu/pgrep/Cargo.toml b/src/uu/pgrep/Cargo.toml index 19bfa12b..c848e647 100644 --- a/src/uu/pgrep/Cargo.toml +++ b/src/uu/pgrep/Cargo.toml @@ -17,7 +17,7 @@ workspace = true clap = { workspace = true } regex = { workspace = true } rustix = { workspace = true, features = ["fs", "process", "thread"] } -uucore = { workspace = true, features = ["entries", "signals"] } +uucore = { workspace = true, features = ["entries", "signals", "uptime"] } walkdir = { workspace = true } [lib] diff --git a/src/uu/pgrep/src/process_matcher.rs b/src/uu/pgrep/src/process_matcher.rs index 0cf740c1..9147d240 100644 --- a/src/uu/pgrep/src/process_matcher.rs +++ b/src/uu/pgrep/src/process_matcher.rs @@ -11,6 +11,7 @@ use std::{collections::HashSet, io}; use clap::{arg, Arg, ArgAction, ArgMatches}; use regex::Regex; #[cfg(unix)] +use uucore::uptime::get_uptime; use uucore::{ display::Quotable, entries::{grp2gid, usr2uid}, @@ -242,23 +243,19 @@ 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")] +/// The unit of a process's start time in /proc//stat. +#[cfg(unix)] fn clock_ticks_per_second() -> u64 { // SAFETY: sysconf has no preconditions. unsafe { uucore::libc::sysconf(uucore::libc::_SC_CLK_TCK) as u64 } } +// No process is listed on these platforms, so no start time gets compared. +#[cfg(not(unix))] +fn clock_ticks_per_second() -> u64 { + 1 +} + /// Collect pids with filter construct from command line arguments fn collect_matched_pids(settings: &Settings) -> UResult> { // Filtration general parameters @@ -278,13 +275,14 @@ fn collect_matched_pids(settings: &Settings) -> UResult> }; // --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 older_than_ticks = settings + .older + .map(|secs| -> UResult<(u64, u64)> { + let ticks = clock_ticks_per_second(); + let uptime = (get_uptime(None)? as u64).saturating_mul(ticks); + Ok((uptime, secs.saturating_mul(ticks))) + }) + .transpose()?; let pid_from_pidfile = settings .pidfile @@ -325,13 +323,10 @@ fn collect_matched_pids(settings: &Settings) -> UResult> let tty_matched = any_matches(&settings.terminal, pid.tty()); - #[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()); From 8e2cda3565e5c8830fe1707787777c0d0c602164 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Mon, 9 Nov 2026 14:49:09 +0530 Subject: [PATCH 3/3] pgrep: move the uptime import out of the unix-only block --- src/uu/pgrep/src/process_matcher.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/pgrep/src/process_matcher.rs b/src/uu/pgrep/src/process_matcher.rs index 9147d240..bf930cde 100644 --- a/src/uu/pgrep/src/process_matcher.rs +++ b/src/uu/pgrep/src/process_matcher.rs @@ -11,7 +11,6 @@ use std::{collections::HashSet, io}; use clap::{arg, Arg, ArgAction, ArgMatches}; use regex::Regex; #[cfg(unix)] -use uucore::uptime::get_uptime; use uucore::{ display::Quotable, entries::{grp2gid, usr2uid}, @@ -19,6 +18,7 @@ use uucore::{ }; use uucore::error::{UResult, USimpleError}; +use uucore::uptime::get_uptime; use crate::process::{walk_process, walk_threads, Namespace, ProcessInformation, Teletype};