Repository: uutils/procps
Pull request #360 (2025-03-21 19:12:31)

pgrep/pkill/pidwait: --require-handler fixes
See commit messages


Patch:
diff --git a/src/uu/pgrep/src/process_matcher.rs b/src/uu/pgrep/src/process_matcher.rs
index 9394dfb..6f1b85a 100644
--- a/src/uu/pgrep/src/process_matcher.rs
+++ b/src/uu/pgrep/src/process_matcher.rs
@@ -115,6 +115,7 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
         && settings.gid.is_none()
         && settings.pgroup.is_none()
         && settings.session.is_none()
+        && !settings.require_handler
         && pattern.is_empty()
     {
         return Err(USimpleError::new(
@@ -138,14 +139,6 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
 
 pub fn find_matching_pids(settings: &Settings) -> Vec<ProcessInformation> {
     let mut pids = collect_matched_pids(settings);
-    #[cfg(unix)]
-    if settings.require_handler {
-        pids.retain(|pid| {
-            let mask =
-                u64::from_str_radix(pid.clone().status().get("SigCgt").unwrap(), 16).unwrap();
-            mask & (1 << settings.signal) != 0
-        });
-    }
     if pids.is_empty() {
         uucore::error::set_exit_code(1);
         pids
@@ -247,6 +240,24 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
                 && any_matches(&settings.euid, pid.euid().unwrap())
                 && any_matches(&settings.gid, pid.gid().unwrap());
 
+            #[cfg(unix)]
+            let handler_matched = if settings.require_handler {
+                // Bits in SigCgt are off by one (ie. bit 0 represents signal 1, etc.)
+                let mask_to_test = if settings.signal == 0 {
+                    // In original pgrep, testing for signal 0 seems to return results for signal 64 instead.
+                    1 << (64 - 1)
+                } else {
+                    1 << (settings.signal - 1)
+                };
+                let mask =
+                    u64::from_str_radix(pid.clone().status().get("SigCgt").unwrap(), 16).unwrap();
+                mask & mask_to_test != 0
+            } else {
+                true
+            };
+            #[cfg(not(unix))]
+            let handler_matched = true;
+
             if (run_state_matched
                 && pattern_matched
                 && tty_matched
@@ -254,7 +265,8 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
                 && parent_matched
                 && pgroup_matched
                 && session_matched
-                && ids_matched)
+                && ids_matched
+                && handler_matched)
                 ^ settings.inverse
             {
                 tmp_vec.push(pid);


Test patch:
diff --git a/tests/by-util/test_pgrep.rs b/tests/by-util/test_pgrep.rs
index fca2b42..376eed7 100644
--- a/tests/by-util/test_pgrep.rs
+++ b/tests/by-util/test_pgrep.rs
@@ -369,6 +369,26 @@ fn test_invalid_signal() {
         .stderr_contains("Unknown signal 'foo'");
 }
 
+#[test]
+#[cfg(target_os = "linux")]
+fn test_signal_that_never_matches() {
+    new_ucmd!()
+        .arg("--require-handler")
+        .arg("--signal=KILL")
+        .arg(".*")
+        .fails()
+        .no_output();
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_lone_require_handler_is_allowed() {
+    new_ucmd!()
+        .arg("--require-handler")
+        .run()
+        .stderr_does_not_contain("no matching criteria specified");
+}
+
 #[test]
 #[cfg(target_os = "linux")]
 fn test_does_not_match_pid() {