Skip to content

Commit 1f72da9

Browse files
authored
Merge pull request #373 from dezgeg/pidof-x
pidof: Implement '-x' flag
2 parents e9e99a1 + 191b10a commit 1f72da9

File tree

2 files changed

+64
-10
lines changed

2 files changed

+64
-10
lines changed

src/uu/pidof/src/pidof.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,33 @@ fn match_process_name(
4949
process: &mut ProcessInformation,
5050
name_to_match: &str,
5151
with_workers: bool,
52+
match_scripts: bool,
5253
) -> bool {
5354
let binding = process.cmdline.split(' ').collect::<Vec<_>>();
54-
let mut path = binding.first().unwrap().to_string();
55+
let path = binding.first().unwrap().to_string();
5556

5657
if path.is_empty() {
5758
if !with_workers {
5859
return false;
5960
}
60-
path.clone_from(&process.status()["Name"]);
61+
return process.name().unwrap() == name_to_match;
6162
};
6263

63-
PathBuf::from(path).file_name().unwrap().to_str().unwrap() == name_to_match
64+
if PathBuf::from(path).file_name().unwrap().to_str().unwrap() == name_to_match {
65+
return true;
66+
}
67+
68+
// When a script (ie. file starting with e.g. #!/bin/sh) is run like `./script.sh`, then
69+
// its cmdline will look like `/bin/sh ./script.sh` but its .name() will be `script.sh`.
70+
// As name() gets truncated to 15 characters, the original pidof seems to always do a prefix match.
71+
if match_scripts && binding.len() > 1 {
72+
return PathBuf::from(binding[1])
73+
.file_name()
74+
.map(|f| f.to_str().unwrap())
75+
.is_some_and(|f| f == name_to_match && f.starts_with(&process.name().unwrap()));
76+
}
77+
78+
false
6479
}
6580

6681
fn collect_matched_pids(matches: &ArgMatches) -> Vec<usize> {
@@ -70,6 +85,7 @@ fn collect_matched_pids(matches: &ArgMatches) -> Vec<usize> {
7085
.cloned()
7186
.collect();
7287
let with_workers = matches.get_flag("with-workers");
88+
let match_scripts = matches.get_flag("x");
7389

7490
let collected = walk_process().collect::<Vec<_>>();
7591
let arg_omit_pid = matches
@@ -83,7 +99,8 @@ fn collect_matched_pids(matches: &ArgMatches) -> Vec<usize> {
8399
.flat_map(|program| {
84100
let mut processed = Vec::new();
85101
for mut process in collected.clone() {
86-
let contains = match_process_name(&mut process, &program, with_workers);
102+
let contains =
103+
match_process_name(&mut process, &program, with_workers, match_scripts);
87104
let should_omit = arg_omit_pid.contains(&process.pid);
88105

89106
if contains && !should_omit {
@@ -184,10 +201,10 @@ pub fn uu_app() -> Command {
184201
.help("Show kernel worker threads as well")
185202
.action(ArgAction::SetTrue),
186203
)
187-
// .arg(
188-
// Arg::new("x")
189-
// .short('x')
190-
// .help("Return PIDs of shells running scripts with a matching name")
191-
// .action(ArgAction::SetTrue),
192-
// )
204+
.arg(
205+
Arg::new("x")
206+
.short('x')
207+
.help("Return PIDs of shells running scripts with a matching name")
208+
.action(ArgAction::SetTrue),
209+
)
193210
}

tests/by-util/test_pidof.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,40 @@ fn test_threads() {
111111
.join()
112112
.unwrap();
113113
}
114+
115+
#[test]
116+
#[cfg(target_os = "linux")]
117+
fn test_script() {
118+
use std::os::unix::fs::PermissionsExt;
119+
120+
let temp_dir = tempfile::tempdir().unwrap();
121+
let script_path = temp_dir
122+
.path()
123+
.join("dummy_test_script_with_very_long_name");
124+
std::fs::write(&script_path, "#!/bin/sh\nsleep 2").unwrap();
125+
std::fs::set_permissions(&script_path, std::fs::Permissions::from_mode(0o755)).unwrap();
126+
127+
let mut directly_executed_child = std::process::Command::new(&script_path).spawn().unwrap();
128+
new_ucmd!()
129+
.arg("dummy_test_script_with_very_long_name")
130+
.fails();
131+
new_ucmd!()
132+
.args(&["-x", "dummy_test_script_with_very_long_name"])
133+
.succeeds()
134+
.stdout_contains(directly_executed_child.id().to_string());
135+
directly_executed_child.kill().unwrap();
136+
directly_executed_child.wait().unwrap();
137+
138+
let mut executed_via_sh_child = std::process::Command::new("/bin/sh")
139+
.arg(&script_path)
140+
.spawn()
141+
.unwrap();
142+
new_ucmd!()
143+
.arg("dummy_test_script_with_very_long_name")
144+
.fails();
145+
new_ucmd!()
146+
.args(&["-x", "dummy_test_script_with_very_long_name"])
147+
.fails();
148+
executed_via_sh_child.kill().unwrap();
149+
executed_via_sh_child.wait().unwrap();
150+
}

0 commit comments

Comments
 (0)