55
66// Common process matcher logic shared by pgrep, pkill and pidwait
77
8- use std:: collections:: HashSet ;
8+ use std:: hash:: Hash ;
9+ use std:: { collections:: HashSet , io} ;
910
1011use clap:: { arg, Arg , ArgAction , ArgMatches } ;
1112use regex:: Regex ;
12- use uucore:: error:: { UResult , USimpleError } ;
1313#[ cfg( unix) ]
14- use uucore:: { display:: Quotable , signals:: signal_by_name_or_value} ;
14+ use uucore:: {
15+ display:: Quotable ,
16+ entries:: { grp2gid, usr2uid} ,
17+ signals:: signal_by_name_or_value,
18+ } ;
19+
20+ use uucore:: error:: { UResult , USimpleError } ;
1521
1622use crate :: process:: { walk_process, ProcessInformation , Teletype } ;
1723
@@ -25,12 +31,15 @@ pub struct Settings {
2531 pub newest : bool ,
2632 pub oldest : bool ,
2733 pub older : Option < u64 > ,
28- pub parent : Option < Vec < u64 > > ,
34+ pub parent : Option < HashSet < u64 > > ,
2935 pub runstates : Option < String > ,
3036 pub terminal : Option < HashSet < Teletype > > ,
3137 #[ cfg( unix) ]
3238 pub signal : usize ,
3339 pub require_handler : bool ,
40+ pub uid : Option < HashSet < u32 > > ,
41+ pub euid : Option < HashSet < u32 > > ,
42+ pub gid : Option < HashSet < u32 > > ,
3443}
3544
3645pub fn get_match_settings ( matches : & ArgMatches ) -> UResult < Settings > {
@@ -58,14 +67,26 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
5867 #[ cfg( unix) ]
5968 signal : parse_signal_value ( matches. get_one :: < String > ( "signal" ) . unwrap ( ) ) ?,
6069 require_handler : matches. get_flag ( "require-handler" ) ,
70+ uid : matches
71+ . get_many :: < u32 > ( "uid" )
72+ . map ( |ids| ids. cloned ( ) . collect ( ) ) ,
73+ euid : matches
74+ . get_many :: < u32 > ( "euid" )
75+ . map ( |ids| ids. cloned ( ) . collect ( ) ) ,
76+ gid : matches
77+ . get_many :: < u32 > ( "group" )
78+ . map ( |ids| ids. cloned ( ) . collect ( ) ) ,
6179 } ;
6280
63- if ( !settings. newest
81+ if !settings. newest
6482 && !settings. oldest
6583 && settings. runstates . is_none ( )
6684 && settings. older . is_none ( )
6785 && settings. parent . is_none ( )
68- && settings. terminal . is_none ( ) )
86+ && settings. terminal . is_none ( )
87+ && settings. uid . is_none ( )
88+ && settings. euid . is_none ( )
89+ && settings. gid . is_none ( )
6990 && pattern. is_empty ( )
7091 {
7192 return Err ( USimpleError :: new (
@@ -137,6 +158,10 @@ fn try_get_pattern_from(matches: &ArgMatches) -> UResult<String> {
137158 Ok ( pattern. to_string ( ) )
138159}
139160
161+ fn any_matches < T : Eq + Hash > ( optional_ids : & Option < HashSet < T > > , id : T ) -> bool {
162+ optional_ids. as_ref ( ) . is_none_or ( |ids| ids. contains ( & id) )
163+ }
164+
140165/// Collect pids with filter construct from command line arguments
141166fn collect_matched_pids ( settings : & Settings ) -> Vec < ProcessInformation > {
142167 // Filtration general parameters
@@ -171,28 +196,23 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
171196 settings. regex . is_match ( want)
172197 } ;
173198
174- let tty_matched = match & settings. terminal {
175- Some ( ttys) => ttys. contains ( & pid. tty ( ) ) ,
176- None => true ,
177- } ;
199+ let tty_matched = any_matches ( & settings. terminal , pid. tty ( ) ) ;
178200
179201 let arg_older = settings. older . unwrap_or ( 0 ) ;
180202 let older_matched = pid. start_time ( ) . unwrap ( ) >= arg_older;
181203
182- // the PPID is the fourth field in /proc/<PID>/stat
183- // (https://www.kernel.org/doc/html/latest/filesystems/proc.html#id10)
184- let stat = pid. stat ( ) ;
185- let ppid = stat. get ( 3 ) ;
186- let parent_matched = match ( & settings. parent , ppid) {
187- ( Some ( parents) , Some ( ppid) ) => parents. contains ( & ppid. parse :: < u64 > ( ) . unwrap ( ) ) ,
188- _ => true ,
189- } ;
204+ let parent_matched = any_matches ( & settings. parent , pid. ppid ( ) . unwrap ( ) ) ;
205+
206+ let ids_matched = any_matches ( & settings. uid , pid. uid ( ) . unwrap ( ) )
207+ && any_matches ( & settings. euid , pid. euid ( ) . unwrap ( ) )
208+ && any_matches ( & settings. gid , pid. gid ( ) . unwrap ( ) ) ;
190209
191210 if ( run_state_matched
192211 && pattern_matched
193212 && tty_matched
194213 && older_matched
195- && parent_matched)
214+ && parent_matched
215+ && ids_matched)
196216 ^ settings. inverse
197217 {
198218 tmp_vec. push ( pid) ;
@@ -249,6 +269,36 @@ fn parse_signal_value(signal_name: &str) -> UResult<usize> {
249269 . ok_or_else ( || USimpleError :: new ( 1 , format ! ( "Unknown signal {}" , signal_name. quote( ) ) ) )
250270}
251271
272+ #[ cfg( not( unix) ) ]
273+ pub fn usr2uid ( _name : & str ) -> io:: Result < u32 > {
274+ Err ( io:: Error :: new (
275+ io:: ErrorKind :: InvalidInput ,
276+ "unsupported on this platform" ,
277+ ) )
278+ }
279+
280+ #[ cfg( not( unix) ) ]
281+ pub fn grp2gid ( _name : & str ) -> io:: Result < u32 > {
282+ Err ( io:: Error :: new (
283+ io:: ErrorKind :: InvalidInput ,
284+ "unsupported on this platform" ,
285+ ) )
286+ }
287+
288+ fn parse_uid_or_username ( uid_or_username : & str ) -> io:: Result < u32 > {
289+ uid_or_username
290+ . parse :: < u32 > ( )
291+ . or_else ( |_| usr2uid ( uid_or_username) )
292+ . map_err ( |_| io:: Error :: new ( io:: ErrorKind :: InvalidInput , "invalid user name" ) )
293+ }
294+
295+ fn parse_gid_or_group_name ( gid_or_group_name : & str ) -> io:: Result < u32 > {
296+ gid_or_group_name
297+ . parse :: < u32 > ( )
298+ . or_else ( |_| grp2gid ( gid_or_group_name) )
299+ . map_err ( |_| io:: Error :: new ( io:: ErrorKind :: InvalidInput , "invalid group name" ) )
300+ }
301+
252302#[ allow( clippy:: cognitive_complexity) ]
253303pub fn clap_args ( pattern_help : & ' static str , enable_v_flag : bool ) -> Vec < Arg > {
254304 vec ! [
@@ -263,9 +313,9 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec<Arg> {
263313 // arg!(-g --pgroup <PGID> "match listed process group IDs")
264314 // .value_delimiter(',')
265315 // .value_parser(clap::value_parser!(u64)),
266- // arg!(-G --group <GID> "match real group IDs")
267- // .value_delimiter(',')
268- // .value_parser(clap::value_parser!(u64) ),
316+ arg!( -G --group <GID > "match real group IDs" )
317+ . value_delimiter( ',' )
318+ . value_parser( parse_gid_or_group_name ) ,
269319 arg!( -i --"ignore-case" "match case insensitively" ) ,
270320 arg!( -n --newest "select most recently started" )
271321 . group( "oldest_newest_inverse" ) ,
@@ -282,12 +332,12 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec<Arg> {
282332 arg!( --signal <sig> "signal to send (either number or name)" )
283333 . default_value( "SIGTERM" ) ,
284334 arg!( -t --terminal <tty> "match by controlling terminal" ) . value_delimiter( ',' ) ,
285- // arg!(-u --euid <ID> "match by effective IDs")
286- // .value_delimiter(',')
287- // .value_parser(clap::value_parser!(u64) ),
288- // arg!(-U --uid <ID> "match by real IDs")
289- // .value_delimiter(',')
290- // .value_parser(clap::value_parser!(u64) ),
335+ arg!( -u --euid <ID > "match by effective IDs" )
336+ . value_delimiter( ',' )
337+ . value_parser( parse_uid_or_username ) ,
338+ arg!( -U --uid <ID > "match by real IDs" )
339+ . value_delimiter( ',' )
340+ . value_parser( parse_uid_or_username ) ,
291341 arg!( -x --exact "match exactly with the command name" ) ,
292342 // arg!(-F --pidfile <file> "read PIDs from file"),
293343 // arg!(-L --logpidfile "fail if PID file is not locked"),
0 commit comments