55
66// Common process matcher logic shared by pgrep, pkill and pidwait
77
8+ use std:: fs;
89use std:: hash:: Hash ;
910use std:: { collections:: HashSet , io} ;
1011
@@ -46,6 +47,8 @@ pub struct Settings {
4647 pub session : Option < HashSet < u64 > > ,
4748 pub cgroup : Option < HashSet < String > > ,
4849 pub threads : bool ,
50+
51+ pub pidfile : Option < String > ,
4952}
5053
5154pub fn get_match_settings ( matches : & ArgMatches ) -> UResult < Settings > {
@@ -106,6 +109,7 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
106109 . get_many :: < String > ( "cgroup" )
107110 . map ( |groups| groups. cloned ( ) . collect ( ) ) ,
108111 threads : false ,
112+ pidfile : matches. get_one :: < String > ( "pidfile" ) . cloned ( ) ,
109113 } ;
110114
111115 if !settings. newest
@@ -121,6 +125,7 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
121125 && settings. session . is_none ( )
122126 && settings. cgroup . is_none ( )
123127 && !settings. require_handler
128+ && settings. pidfile . is_none ( )
124129 && pattern. is_empty ( )
125130 {
126131 return Err ( USimpleError :: new (
@@ -142,13 +147,13 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
142147 Ok ( settings)
143148}
144149
145- pub fn find_matching_pids ( settings : & Settings ) -> Vec < ProcessInformation > {
146- let mut pids = collect_matched_pids ( settings) ;
150+ pub fn find_matching_pids ( settings : & Settings ) -> UResult < Vec < ProcessInformation > > {
151+ let mut pids = collect_matched_pids ( settings) ? ;
147152 if pids. is_empty ( ) {
148153 uucore:: error:: set_exit_code ( 1 ) ;
149- pids
154+ Ok ( pids)
150155 } else {
151- process_flag_o_n ( settings, & mut pids)
156+ Ok ( process_flag_o_n ( settings, & mut pids) )
152157 }
153158}
154159
@@ -189,7 +194,7 @@ fn any_matches<T: Eq + Hash>(optional_ids: &Option<HashSet<T>>, id: T) -> bool {
189194}
190195
191196/// Collect pids with filter construct from command line arguments
192- fn collect_matched_pids ( settings : & Settings ) -> Vec < ProcessInformation > {
197+ fn collect_matched_pids ( settings : & Settings ) -> UResult < Vec < ProcessInformation > > {
193198 // Filtration general parameters
194199 let filtered: Vec < ProcessInformation > = {
195200 let mut tmp_vec = Vec :: new ( ) ;
@@ -200,6 +205,11 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
200205 walk_process ( ) . collect :: < Vec < _ > > ( )
201206 } ;
202207 let our_pid = std:: process:: id ( ) as usize ;
208+ let pid_from_pidfile = settings
209+ . pidfile
210+ . as_ref ( )
211+ . map ( |filename| read_pidfile ( filename) )
212+ . transpose ( ) ?;
203213
204214 for mut pid in pids {
205215 if pid. pid == our_pid {
@@ -267,6 +277,8 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
267277 #[ cfg( not( unix) ) ]
268278 let handler_matched = true ;
269279
280+ let pidfile_matched = pid_from_pidfile. is_none_or ( |p| p == pid. pid as i64 ) ;
281+
270282 if ( run_state_matched
271283 && pattern_matched
272284 && tty_matched
@@ -276,7 +288,8 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
276288 && session_matched
277289 && cgroup_matched
278290 && ids_matched
279- && handler_matched)
291+ && handler_matched
292+ && pidfile_matched)
280293 ^ settings. inverse
281294 {
282295 tmp_vec. push ( pid) ;
@@ -285,7 +298,7 @@ fn collect_matched_pids(settings: &Settings) -> Vec<ProcessInformation> {
285298 tmp_vec
286299 } ;
287300
288- filtered
301+ Ok ( filtered)
289302}
290303
291304/// Sorting pids for flag `-o` and `-n`.
@@ -379,6 +392,35 @@ fn parse_gid_or_group_name(gid_or_group_name: &str) -> io::Result<u32> {
379392 . map_err ( |_| io:: Error :: new ( io:: ErrorKind :: InvalidInput , "invalid group name" ) )
380393}
381394
395+ pub fn parse_pidfile_content ( content : & str ) -> Option < i64 > {
396+ let re = Regex :: new ( r"(?-m)^[[:blank:]]*(-?[0-9]+)(?:\s|$)" ) . unwrap ( ) ;
397+ re. captures ( content) ?. get ( 1 ) ?. as_str ( ) . parse :: < i64 > ( ) . ok ( )
398+ }
399+
400+ #[ test]
401+ fn test_parse_pidfile_content_valid ( ) {
402+ assert_eq ! ( parse_pidfile_content( " 1234" ) , Some ( 1234 ) ) ;
403+ assert_eq ! ( parse_pidfile_content( "-5678 " ) , Some ( -5678 ) ) ;
404+ assert_eq ! ( parse_pidfile_content( " 42\n foo\n " ) , Some ( 42 ) ) ;
405+ assert_eq ! ( parse_pidfile_content( "\t -99\t bar\n " ) , Some ( -99 ) ) ;
406+
407+ assert_eq ! ( parse_pidfile_content( "" ) , None ) ;
408+ assert_eq ! ( parse_pidfile_content( "abc" ) , None ) ;
409+ assert_eq ! ( parse_pidfile_content( "0x42" ) , None ) ;
410+ assert_eq ! ( parse_pidfile_content( "2.3" ) , None ) ;
411+ assert_eq ! ( parse_pidfile_content( "\n 123\n " ) , None ) ;
412+ }
413+
414+ pub fn read_pidfile ( filename : & str ) -> UResult < i64 > {
415+ let content = fs:: read_to_string ( filename)
416+ . map_err ( |e| USimpleError :: new ( 1 , format ! ( "Failed to read pidfile {}: {}" , filename, e) ) ) ?;
417+
418+ let pid = parse_pidfile_content ( & content)
419+ . ok_or_else ( || USimpleError :: new ( 1 , format ! ( "Pidfile {} not valid" , filename) ) ) ?;
420+
421+ Ok ( pid)
422+ }
423+
382424#[ allow( clippy:: cognitive_complexity) ]
383425pub fn clap_args ( pattern_help : & ' static str , enable_v_flag : bool ) -> Vec < Arg > {
384426 vec ! [
@@ -419,7 +461,7 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec<Arg> {
419461 . value_delimiter( ',' )
420462 . value_parser( parse_uid_or_username) ,
421463 arg!( -x --exact "match exactly with the command name" ) ,
422- // arg!(-F --pidfile <file> "read PIDs from file"),
464+ arg!( -F --pidfile <file> "read PIDs from file" ) ,
423465 // arg!(-L --logpidfile "fail if PID file is not locked"),
424466 arg!( -r --runstates <state> "match runstates [D,S,Z,...]" ) ,
425467 // arg!(-A --"ignore-ancestors" "exclude our ancestors from results"),
0 commit comments