Skip to content

Commit 540ba88

Browse files
Merge pull request #564 from dezgeg/ps_flags
ps: Support more process selection flags
2 parents 822b4e3 + 13c2f75 commit 540ba88

File tree

3 files changed

+332
-31
lines changed

3 files changed

+332
-31
lines changed

src/uu/ps/src/process_selection.rs

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,25 @@ pub struct ProcessSelectionSettings {
4444
/// - `-d` Select all processes except session leaders.
4545
pub select_non_session_leaders: bool,
4646

47-
/// - '-x' Lift "must have a tty" restriction.
47+
/// - `-x` Lift "must have a tty" restriction.
4848
pub dont_require_tty: bool,
4949

50-
/// Select specific process IDs (-p, --pid)
50+
/// - `-C` Select by command name
51+
pub command_names: Option<HashSet<String>>,
52+
/// - `-p, --pid` Select specific process IDs
5153
pub pids: Option<HashSet<usize>>,
54+
/// - `--ppid` Select specific parent process IDs
55+
pub ppids: Option<HashSet<usize>>,
56+
/// - `--sid` Select specific session IDs
57+
pub sids: Option<HashSet<usize>>,
58+
/// - `-G, --Group` Select by real group ID or name
59+
pub real_groups: Option<HashSet<u32>>,
60+
/// - `-g, --group` Select by effective group ID or name
61+
pub eff_groups: Option<HashSet<u32>>,
62+
/// - `-U, --User` Select by real user ID or name
63+
pub real_users: Option<HashSet<u32>>,
64+
/// - `-u, --user` Select by effective user ID or name
65+
pub eff_users: Option<HashSet<u32>>,
5266

5367
/// - `-r` Restrict the selection to only running processes.
5468
pub only_running: bool,
@@ -64,9 +78,30 @@ impl ProcessSelectionSettings {
6478
select_non_session_leaders_with_tty: matches.get_flag("a"),
6579
select_non_session_leaders: matches.get_flag("d"),
6680
dont_require_tty: matches.get_flag("x"),
81+
command_names: matches
82+
.get_many::<Vec<String>>("command")
83+
.map(|xs| xs.flatten().cloned().collect()),
6784
pids: matches
6885
.get_many::<Vec<usize>>("pid")
6986
.map(|xs| xs.flatten().copied().collect()),
87+
ppids: matches
88+
.get_many::<Vec<usize>>("ppid")
89+
.map(|xs| xs.flatten().copied().collect()),
90+
sids: matches
91+
.get_many::<Vec<usize>>("sid")
92+
.map(|xs| xs.flatten().copied().collect()),
93+
real_groups: matches
94+
.get_many::<Vec<u32>>("real-group")
95+
.map(|xs| xs.flatten().copied().collect()),
96+
eff_groups: matches
97+
.get_many::<Vec<u32>>("effective-group")
98+
.map(|xs| xs.flatten().copied().collect()),
99+
real_users: matches
100+
.get_many::<Vec<u32>>("real-user")
101+
.map(|xs| xs.flatten().copied().collect()),
102+
eff_users: matches
103+
.get_many::<Vec<u32>>("effective-user")
104+
.map(|xs| xs.flatten().copied().collect()),
70105
only_running: matches.get_flag("r"),
71106
negate_selection: matches.get_flag("deselect"),
72107
}
@@ -82,14 +117,36 @@ impl ProcessSelectionSettings {
82117
return Ok(false);
83118
}
84119

85-
if let Some(ref pids) = self.pids {
86-
return Ok(pids.contains(&process.pid));
87-
}
88-
89120
if self.select_all {
90121
return Ok(true);
91122
}
92123

124+
// Flags in this group seem to cause rest of the flags to be ignored
125+
let mut matched: Option<bool> = None;
126+
fn update_match<T, U>(
127+
matched: &mut Option<bool>,
128+
set_opt: &Option<HashSet<T>>,
129+
value: U,
130+
) where
131+
T: std::cmp::Eq + std::hash::Hash + std::borrow::Borrow<U>,
132+
U: std::cmp::Eq + std::hash::Hash,
133+
{
134+
if let Some(ref set) = set_opt {
135+
*matched.get_or_insert_default() |= set.contains(&value);
136+
}
137+
}
138+
update_match(&mut matched, &self.command_names, process.name().unwrap());
139+
update_match(&mut matched, &self.pids, process.pid);
140+
update_match(&mut matched, &self.ppids, process.ppid().unwrap() as usize);
141+
update_match(&mut matched, &self.sids, process.sid().unwrap() as usize);
142+
update_match(&mut matched, &self.real_users, process.uid().unwrap());
143+
update_match(&mut matched, &self.eff_users, process.euid().unwrap());
144+
update_match(&mut matched, &self.real_groups, process.gid().unwrap());
145+
update_match(&mut matched, &self.eff_groups, process.egid().unwrap());
146+
if let Some(m) = matched {
147+
return Ok(m);
148+
}
149+
93150
if self.select_non_session_leaders_with_tty {
94151
return Ok(!is_session_leader(process) && process.tty() != Teletype::Unknown);
95152
}

src/uu/ps/src/ps.rs

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use parser::{parser, OptionalKeyValue};
2121
use prettytable::{format::consts::FORMAT_CLEAN, Row, Table};
2222
use process_selection::ProcessSelectionSettings;
2323
use std::cell::RefCell;
24+
#[cfg(unix)]
25+
use uucore::entries::{grp2gid, usr2uid};
2426
use uucore::{
2527
error::{UError, UResult, USimpleError},
2628
format_usage, help_about, help_usage,
@@ -29,6 +31,22 @@ use uucore::{
2931
const ABOUT: &str = help_about!("ps.md");
3032
const USAGE: &str = help_usage!("ps.md");
3133

34+
#[cfg(not(unix))]
35+
pub fn usr2uid(_name: &str) -> std::io::Result<u32> {
36+
Err(std::io::Error::new(
37+
std::io::ErrorKind::InvalidInput,
38+
"unsupported on this platform",
39+
))
40+
}
41+
42+
#[cfg(not(unix))]
43+
pub fn grp2gid(_name: &str) -> std::io::Result<u32> {
44+
Err(std::io::Error::new(
45+
std::io::ErrorKind::InvalidInput,
46+
"unsupported on this platform",
47+
))
48+
}
49+
3250
#[uucore::main]
3351
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
3452
let matches = uu_app().try_get_matches_from(args)?;
@@ -138,15 +156,45 @@ fn collect_format(
138156
Ok(collect)
139157
}
140158

141-
fn parse_numeric_list(s: &str) -> Result<Vec<usize>, String> {
159+
fn split_arg_list(s: &str) -> impl Iterator<Item = &str> {
142160
s.split(|c: char| c.is_whitespace() || c == ',')
161+
}
162+
163+
fn parse_numeric_list(s: &str) -> Result<Vec<usize>, String> {
164+
split_arg_list(s)
143165
.map(|word| {
144166
word.parse::<usize>()
145167
.map_err(|_| format!("invalid number: '{}'", word))
146168
})
147169
.collect()
148170
}
149171

172+
fn parse_uid_list(s: &str) -> Result<Vec<u32>, String> {
173+
split_arg_list(s)
174+
.map(|uid_or_username| {
175+
uid_or_username
176+
.parse::<u32>()
177+
.or_else(|_| usr2uid(uid_or_username))
178+
.map_err(|_| format!("invalid user name '{}'", uid_or_username))
179+
})
180+
.collect()
181+
}
182+
183+
fn parse_gid_list(s: &str) -> Result<Vec<u32>, String> {
184+
split_arg_list(s)
185+
.map(|gid_or_group_name| {
186+
gid_or_group_name
187+
.parse::<u32>()
188+
.or_else(|_| grp2gid(gid_or_group_name))
189+
.map_err(|_| format!("invalid group name '{}'", gid_or_group_name))
190+
})
191+
.collect()
192+
}
193+
194+
fn parse_command_list(s: &str) -> Result<Vec<String>, String> {
195+
Ok(split_arg_list(s).map(|part| part.to_string()).collect())
196+
}
197+
150198
#[allow(clippy::cognitive_complexity)]
151199
pub fn uu_app() -> Command {
152200
Command::new(uucore::util_name())
@@ -271,6 +319,13 @@ pub fn uu_app() -> Command {
271319
.action(ArgAction::SetTrue)
272320
.help("do not print header at all"),
273321
)
322+
.arg(
323+
Arg::new("command")
324+
.short('C')
325+
.action(ArgAction::Append)
326+
.value_parser(parse_command_list)
327+
.help("select by command name"),
328+
)
274329
.arg(
275330
Arg::new("pid")
276331
.short('p')
@@ -279,33 +334,57 @@ pub fn uu_app() -> Command {
279334
.value_parser(parse_numeric_list)
280335
.help("select by process ID"),
281336
)
337+
.arg(
338+
Arg::new("ppid")
339+
.long("ppid")
340+
.action(ArgAction::Append)
341+
.value_parser(parse_numeric_list)
342+
.help("select by parent process ID"),
343+
)
344+
.arg(
345+
Arg::new("sid")
346+
.long("sid")
347+
.action(ArgAction::Append)
348+
.value_parser(parse_numeric_list)
349+
.help("select by session ID"),
350+
)
351+
.arg(
352+
Arg::new("real-group")
353+
.short('G')
354+
.long("Group")
355+
.action(ArgAction::Append)
356+
.value_parser(parse_gid_list)
357+
.help("select by real group ID (RGID) or name"),
358+
)
359+
.arg(
360+
Arg::new("effective-group")
361+
.short('g')
362+
.long("group")
363+
.action(ArgAction::Append)
364+
.value_parser(parse_gid_list)
365+
.help("select by effective group ID (EGID) or name"),
366+
)
367+
.arg(
368+
Arg::new("real-user")
369+
.short('U')
370+
.long("User")
371+
.action(ArgAction::Append)
372+
.value_parser(parse_uid_list)
373+
.help("select by real user ID (RUID) or name"),
374+
)
375+
.arg(
376+
Arg::new("effective-user")
377+
.long("user")
378+
.action(ArgAction::Append)
379+
.value_parser(parse_uid_list)
380+
.help("select by effective user ID (EUID) or name"),
381+
)
282382
// .args([
283-
// Arg::new("command").short('c').help("command name"),
284-
// Arg::new("GID")
285-
// .short('G')
286-
// .long("Group")
287-
// .help("real group id or name"),
288-
// Arg::new("group")
289-
// .short('g')
290-
// .long("group")
291-
// .help("session or effective group name"),
292383
// Arg::new("pPID").long("ppid").help("parent process id"),
293384
// Arg::new("qPID")
294385
// .short('q')
295386
// .long("quick-pid")
296387
// .help("process id"),
297-
// Arg::new("session")
298-
// .short('s')
299-
// .long("sid")
300-
// .help("session id"),
301388
// Arg::new("t").short('t').long("tty").help("terminal"),
302-
// Arg::new("eUID")
303-
// .short('u')
304-
// .long("user")
305-
// .help("effective user id or name"),
306-
// Arg::new("rUID")
307-
// .short('U')
308-
// .long("User")
309-
// .help("real user id or name"),
310389
// ])
311390
}

0 commit comments

Comments
 (0)