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
35 changes: 33 additions & 2 deletions src/uu/pgrep/src/process_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,23 @@ fn get_namespaces(_pid: usize, _list: Option<Vec<&str>>) -> UResult<Namespace> {
Ok(Namespace::new())
}

/// Return the time since boot in clock ticks, the unit of a process's start
/// time in /proc/<pid>/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::<f64>().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<Vec<ProcessInformation>> {
// Filtration general parameters
Expand All @@ -260,6 +277,15 @@ fn collect_matched_pids(settings: &Settings) -> UResult<Vec<ProcessInformation>>
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()
Expand Down Expand Up @@ -299,8 +325,13 @@ 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;
#[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());
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