Skip to content

Commit fb0be28

Browse files
committed
pgrep/pidwait/pkill: Implement --env
1 parent 24db3f3 commit fb0be28

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

src/uu/pgrep/src/process_matcher.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub struct Settings {
4747
pub pgroup: Option<HashSet<u64>>,
4848
pub session: Option<HashSet<u64>>,
4949
pub cgroup: Option<HashSet<String>>,
50+
pub env: Option<HashSet<String>>,
5051
pub threads: bool,
5152

5253
pub pidfile: Option<String>,
@@ -111,6 +112,9 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
111112
cgroup: matches
112113
.get_many::<String>("cgroup")
113114
.map(|groups| groups.cloned().collect()),
115+
env: matches
116+
.get_many::<String>("env")
117+
.map(|env_vars| env_vars.cloned().collect()),
114118
threads: false,
115119
pidfile: matches.get_one::<String>("pidfile").cloned(),
116120
logpidfile: matches.get_flag("logpidfile"),
@@ -129,6 +133,7 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
129133
&& settings.pgroup.is_none()
130134
&& settings.session.is_none()
131135
&& settings.cgroup.is_none()
136+
&& settings.env.is_none()
132137
&& !settings.require_handler
133138
&& settings.pidfile.is_none()
134139
&& pattern.is_empty()
@@ -279,6 +284,22 @@ fn collect_matched_pids(settings: &Settings) -> UResult<Vec<ProcessInformation>>
279284
pid.cgroup_v2_path().unwrap_or("/".to_string()),
280285
);
281286

287+
let env_matched = match &settings.env {
288+
Some(env_filters) => {
289+
let env_vars = pid.env_vars().unwrap_or_default();
290+
env_filters.iter().any(|filter| {
291+
if let Some((key, expected_value)) = filter.split_once('=') {
292+
// Match specific key=value pair
293+
env_vars.get(key) == Some(&expected_value.to_string())
294+
} else {
295+
// Match key existence only
296+
env_vars.contains_key(filter)
297+
}
298+
})
299+
}
300+
None => true,
301+
};
302+
282303
let ids_matched = any_matches(&settings.uid, pid.uid().unwrap())
283304
&& any_matches(&settings.euid, pid.euid().unwrap())
284305
&& any_matches(&settings.gid, pid.gid().unwrap());
@@ -311,6 +332,7 @@ fn collect_matched_pids(settings: &Settings) -> UResult<Vec<ProcessInformation>>
311332
&& pgroup_matched
312333
&& session_matched
313334
&& cgroup_matched
335+
&& env_matched
314336
&& ids_matched
315337
&& handler_matched
316338
&& pidfile_matched)
@@ -530,6 +552,7 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec<Arg> {
530552
arg!(-r --runstates <state> "match runstates [D,S,Z,...]"),
531553
arg!(-A --"ignore-ancestors" "exclude our ancestors from results"),
532554
arg!(--cgroup <grp> "match by cgroup v2 names").value_delimiter(','),
555+
arg!(--env <"name[=val],..."> "match on environment variable").value_delimiter(','),
533556
// arg!(--ns <PID> "match the processes that belong to the same namespace as <pid>"),
534557
// arg!(--nslist <ns> "list which namespaces will be considered for the --ns option.")
535558
// .value_delimiter(',')

tests/by-util/test_pgrep.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,3 +685,38 @@ fn test_ignore_ancestors() {
685685
.stdout_does_not_match(&Regex::new(&format!("(?m)^{our_pid}$")).unwrap())
686686
.stdout_does_not_match(&Regex::new("(?m)^1$").unwrap());
687687
}
688+
689+
#[test]
690+
#[cfg(target_os = "linux")]
691+
fn test_env_nonexistent() {
692+
new_ucmd!().arg("--env=NONEXISTENT").fails().code_is(1);
693+
}
694+
695+
#[test]
696+
#[cfg(target_os = "linux")]
697+
fn test_env_nonmatching_value() {
698+
new_ucmd!()
699+
.arg("--env=PATH=not_a_valid_PATH")
700+
.fails()
701+
.code_is(1);
702+
}
703+
704+
#[test]
705+
#[cfg(target_os = "linux")]
706+
fn test_env_key_match() {
707+
new_ucmd!().arg("--env=PATH").succeeds();
708+
}
709+
710+
#[test]
711+
#[cfg(target_os = "linux")]
712+
fn test_env_key_value_match() {
713+
let home = std::env::var("HOME").unwrap();
714+
new_ucmd!().arg(format!("--env=HOME={}", home)).succeeds();
715+
}
716+
717+
#[test]
718+
#[cfg(target_os = "linux")]
719+
fn test_env_multiple_filters() {
720+
// Multiple filters use OR logic
721+
new_ucmd!().arg("--env=PATH,NONEXISTENT").succeeds();
722+
}

0 commit comments

Comments
 (0)