Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/uu/pgrep/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
30 changes: 28 additions & 2 deletions src/uu/pgrep/src/process_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,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};

Expand Down Expand Up @@ -242,6 +243,19 @@ fn get_namespaces(_pid: usize, _list: Option<Vec<&str>>) -> UResult<Namespace> {
Ok(Namespace::new())
}

/// The unit of a process's start time in /proc/<pid>/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<Vec<ProcessInformation>> {
// Filtration general parameters
Expand All @@ -260,6 +274,16 @@ fn collect_matched_pids(settings: &Settings) -> UResult<Vec<ProcessInformation>>
HashSet::from([our_pid])
};

// --older compares a process's age with the given seconds.
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
.as_ref()
Expand Down Expand Up @@ -299,8 +323,10 @@ fn collect_matched_pids(settings: &Settings) -> UResult<Vec<ProcessInformation>>

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;
let older_matched = match older_than_ticks {
Some((now, min_age)) => now.saturating_sub(pid.start_time().unwrap()) >= min_age,
None => true,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why only linux? before this, --older wasn't gated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No good reason, I removed the Linux gates. Only the tick lookup keeps a cfg(unix), because sysconf is unix-only.

let parent_matched = any_matches(&settings.parent, pid.ppid().unwrap());
let pgroup_matched = any_matches(&settings.pgroup, pid.pgid().unwrap());
Expand Down
18 changes: 18 additions & 0 deletions tests/by-util/test_pgrep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Loading