Skip to content

Commit f946eb6

Browse files
committed
pgrep: Support --signal and --require-handler
This is not listed in pgrep --help, but is documented in the man page and does work. Unlike pkill the -SIGNAL short option is not supported by pgrep. Simplify parse_signal_value with ok_or_else
1 parent f0148e2 commit f946eb6

File tree

3 files changed

+46
-9
lines changed

3 files changed

+46
-9
lines changed

src/uu/pgrep/src/pgrep.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use clap::{arg, crate_version, Arg, ArgAction, ArgGroup, ArgMatches, Command};
1010
use process::{walk_process, ProcessInformation, Teletype};
1111
use regex::Regex;
1212
use std::{collections::HashSet, sync::OnceLock};
13+
#[cfg(unix)]
14+
use uucore::{display::Quotable, signals::signal_by_name_or_value};
1315
use uucore::{
1416
error::{UResult, USimpleError},
1517
format_usage, help_about, help_usage,
@@ -87,9 +89,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
8789
));
8890
}
8991

92+
// Parse signal
93+
#[cfg(unix)]
94+
let sig_num = parse_signal_value(matches.get_one::<String>("signal").unwrap())?;
95+
9096
// Collect pids
9197
let pids = {
9298
let mut pids = collect_matched_pids(&settings);
99+
#[cfg(unix)]
100+
if matches.get_flag("require-handler") {
101+
pids.retain(|pid| {
102+
let mask =
103+
u64::from_str_radix(pid.clone().status().get("SigCgt").unwrap(), 16).unwrap();
104+
mask & (1 << sig_num) != 0
105+
});
106+
}
93107
if pids.is_empty() {
94108
uucore::error::set_exit_code(1);
95109
pids
@@ -275,6 +289,12 @@ fn process_flag_o_n(
275289
}
276290
}
277291

292+
#[cfg(unix)]
293+
fn parse_signal_value(signal_name: &str) -> UResult<usize> {
294+
signal_by_name_or_value(signal_name)
295+
.ok_or_else(|| USimpleError::new(1, format!("Unknown signal {}", signal_name.quote())))
296+
}
297+
278298
#[allow(clippy::cognitive_complexity)]
279299
pub fn uu_app() -> Command {
280300
Command::new(uucore::util_name())
@@ -289,6 +309,7 @@ pub fn uu_app() -> Command {
289309
.hide_default_value(true),
290310
arg!(-l --"list-name" "list PID and process name"),
291311
arg!(-a --"list-full" "list PID and full command line"),
312+
arg!(-H --"require-handler" "match only if signal handler is present"),
292313
arg!(-v --inverse "negates the matching"),
293314
// arg!(-w --lightweight "list all TID"),
294315
arg!(-c --count "count of matching processes"),
@@ -310,7 +331,8 @@ pub fn uu_app() -> Command {
310331
// arg!(-s --session <SID> "match session IDs")
311332
// .value_delimiter(',')
312333
// .value_parser(clap::value_parser!(u64)),
313-
// arg!(--signal <sig> "signal to send (either number or name)"),
334+
arg!(--signal <sig> "signal to send (either number or name)")
335+
.default_value("SIGTERM"),
314336
arg!(-t --terminal <tty> "match by controlling terminal")
315337
.value_delimiter(','),
316338
// arg!(-u --euid <ID> ... "match by effective IDs")

src/uu/pkill/src/pkill.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,8 @@ fn handle_obsolete(args: &mut [String]) {
305305

306306
#[cfg(unix)]
307307
fn parse_signal_value(signal_name: &str) -> UResult<usize> {
308-
let optional_signal_value = signal_by_name_or_value(signal_name);
309-
match optional_signal_value {
310-
Some(x) => Ok(x),
311-
None => Err(USimpleError::new(
312-
1,
313-
format!("Unknown signal {}", signal_name.quote()),
314-
)),
315-
}
308+
signal_by_name_or_value(signal_name)
309+
.ok_or_else(|| USimpleError::new(1, format!("Unknown signal {}", signal_name.quote())))
316310
}
317311

318312
#[cfg(unix)]

tests/by-util/test_pgrep.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,24 @@ fn test_parent_non_matching_parent() {
347347
.code_is(1)
348348
.no_output();
349349
}
350+
351+
#[test]
352+
#[cfg(target_os = "linux")]
353+
fn test_require_handler() {
354+
new_ucmd!()
355+
.arg("--require-handler")
356+
.arg("--signal=INT")
357+
.arg("NONEXISTENT")
358+
.fails()
359+
.no_output();
360+
}
361+
362+
#[test]
363+
#[cfg(target_os = "linux")]
364+
fn test_invalid_signal() {
365+
new_ucmd!()
366+
.arg("--signal=foo")
367+
.arg("NONEXISTENT")
368+
.fails()
369+
.stderr_contains("Unknown signal 'foo'");
370+
}

0 commit comments

Comments
 (0)