55
66// Common process matcher logic shared by pgrep, pkill and pidwait
77
8- use std:: fs;
98use std:: hash:: Hash ;
9+ #[ cfg( unix) ]
10+ use std:: os:: fd:: AsRawFd ;
1011use std:: { collections:: HashSet , io} ;
1112
1213use clap:: { arg, Arg , ArgAction , ArgMatches } ;
@@ -49,6 +50,7 @@ pub struct Settings {
4950 pub threads : bool ,
5051
5152 pub pidfile : Option < String > ,
53+ pub logpidfile : bool ,
5254}
5355
5456pub fn get_match_settings ( matches : & ArgMatches ) -> UResult < Settings > {
@@ -110,6 +112,7 @@ pub fn get_match_settings(matches: &ArgMatches) -> UResult<Settings> {
110112 . map ( |groups| groups. cloned ( ) . collect ( ) ) ,
111113 threads : false ,
112114 pidfile : matches. get_one :: < String > ( "pidfile" ) . cloned ( ) ,
115+ logpidfile : matches. get_flag ( "logpidfile" ) ,
113116 } ;
114117
115118 if !settings. newest
@@ -208,7 +211,7 @@ fn collect_matched_pids(settings: &Settings) -> UResult<Vec<ProcessInformation>>
208211 let pid_from_pidfile = settings
209212 . pidfile
210213 . as_ref ( )
211- . map ( |filename| read_pidfile ( filename) )
214+ . map ( |filename| read_pidfile ( filename, settings . logpidfile ) )
212215 . transpose ( ) ?;
213216
214217 for mut pid in pids {
@@ -392,7 +395,7 @@ fn parse_gid_or_group_name(gid_or_group_name: &str) -> io::Result<u32> {
392395 . map_err ( |_| io:: Error :: new ( io:: ErrorKind :: InvalidInput , "invalid group name" ) )
393396}
394397
395- pub fn parse_pidfile_content ( content : & str ) -> Option < i64 > {
398+ fn parse_pidfile_content ( content : & str ) -> Option < i64 > {
396399 let re = Regex :: new ( r"(?-m)^[[:blank:]]*(-?[0-9]+)(?:\s|$)" ) . unwrap ( ) ;
397400 re. captures ( content) ?. get ( 1 ) ?. as_str ( ) . parse :: < i64 > ( ) . ok ( )
398401}
@@ -411,8 +414,48 @@ fn test_parse_pidfile_content_valid() {
411414 assert_eq ! ( parse_pidfile_content( "\n 123\n " ) , None ) ;
412415}
413416
414- pub fn read_pidfile ( filename : & str ) -> UResult < i64 > {
415- let content = fs:: read_to_string ( filename)
417+ #[ cfg( unix) ]
418+ fn is_locked ( file : & std:: fs:: File ) -> bool {
419+ // On Linux, fcntl and flock locks are independent, so need to check both
420+ let mut flock_struct = uucore:: libc:: flock {
421+ l_type : uucore:: libc:: F_RDLCK as uucore:: libc:: c_short ,
422+ l_whence : uucore:: libc:: SEEK_SET as uucore:: libc:: c_short ,
423+ l_start : 0 ,
424+ l_len : 0 ,
425+ l_pid : 0 ,
426+ } ;
427+ let fd = file. as_raw_fd ( ) ;
428+ let result = unsafe { uucore:: libc:: fcntl ( fd, uucore:: libc:: F_GETLK , & mut flock_struct) } ;
429+ if result == 0 && flock_struct. l_type != uucore:: libc:: F_UNLCK as uucore:: libc:: c_short {
430+ return true ;
431+ }
432+
433+ let result = unsafe { uucore:: libc:: flock ( fd, uucore:: libc:: LOCK_SH | uucore:: libc:: LOCK_NB ) } ;
434+ if result == -1 && std:: io:: Error :: last_os_error ( ) . kind ( ) == std:: io:: ErrorKind :: WouldBlock {
435+ return true ;
436+ }
437+
438+ false
439+ }
440+
441+ #[ cfg( not( unix) ) ]
442+ fn is_locked ( _file : & std:: fs:: File ) -> bool {
443+ // Dummy implementation just to make it compile
444+ false
445+ }
446+
447+ fn read_pidfile ( filename : & str , check_locked : bool ) -> UResult < i64 > {
448+ let file = std:: fs:: File :: open ( filename)
449+ . map_err ( |e| USimpleError :: new ( 1 , format ! ( "Failed to open pidfile {}: {}" , filename, e) ) ) ?;
450+
451+ if check_locked && !is_locked ( & file) {
452+ return Err ( USimpleError :: new (
453+ 1 ,
454+ format ! ( "Pidfile {} is not locked" , filename) ,
455+ ) ) ;
456+ }
457+
458+ let content = std:: fs:: read_to_string ( filename)
416459 . map_err ( |e| USimpleError :: new ( 1 , format ! ( "Failed to read pidfile {}: {}" , filename, e) ) ) ?;
417460
418461 let pid = parse_pidfile_content ( & content)
@@ -462,7 +505,7 @@ pub fn clap_args(pattern_help: &'static str, enable_v_flag: bool) -> Vec<Arg> {
462505 . value_parser( parse_uid_or_username) ,
463506 arg!( -x --exact "match exactly with the command name" ) ,
464507 arg!( -F --pidfile <file> "read PIDs from file" ) ,
465- // arg!(-L --logpidfile "fail if PID file is not locked"),
508+ arg!( -L --logpidfile "fail if PID file is not locked" ) ,
466509 arg!( -r --runstates <state> "match runstates [D,S,Z,...]" ) ,
467510 // arg!(-A --"ignore-ancestors" "exclude our ancestors from results"),
468511 arg!( --cgroup <grp> "match by cgroup v2 names" ) . value_delimiter( ',' ) ,
0 commit comments