55
66// Pid utils
77pub mod process;
8+ pub mod process_matcher;
89
9- use clap:: { arg, crate_version, Arg , ArgAction , ArgGroup , ArgMatches , Command } ;
10- use process:: { walk_process, ProcessInformation , Teletype } ;
11- use regex:: Regex ;
12- #[ cfg( unix) ]
13- use uucore:: { display:: Quotable , signals:: signal_by_name_or_value} ;
14- use uucore:: {
15- error:: { UResult , USimpleError } ,
16- format_usage, help_about, help_usage,
17- } ;
10+ use clap:: { arg, crate_version, Arg , ArgAction , ArgGroup , Command } ;
11+ use uucore:: { error:: UResult , format_usage, help_about, help_usage} ;
1812
1913const ABOUT : & str = help_about ! ( "pgrep.md" ) ;
2014const USAGE : & str = help_usage ! ( "pgrep.md" ) ;
2115
22- struct Settings {
23- regex : Regex ,
24-
25- exact : bool ,
26- full : bool ,
27- ignore_case : bool ,
28- inverse : bool ,
29- newest : bool ,
30- oldest : bool ,
31- older : Option < u64 > ,
32- parent : Option < Vec < u64 > > ,
33- runstates : Option < String > ,
34- terminal : Option < HashSet < Teletype > > ,
35- signal : usize ,
36- require_handler : bool ,
37- }
38-
39- fn get_match_settings ( matches : & ArgMatches ) -> UResult < Settings > {
40- let pattern = try_get_pattern_from ( matches) ?;
41- let regex = Regex :: new ( & pattern) . map_err ( |e| USimpleError :: new ( 2 , e. to_string ( ) ) ) ?;
42-
43- let settings = Settings {
44- regex,
45- exact : matches. get_flag ( "exact" ) ,
46- full : matches. get_flag ( "full" ) ,
47- ignore_case : matches. get_flag ( "ignore-case" ) ,
48- inverse : matches. get_flag ( "inverse" ) ,
49- newest : matches. get_flag ( "newest" ) ,
50- oldest : matches. get_flag ( "oldest" ) ,
51- parent : matches
52- . get_many :: < u64 > ( "parent" )
53- . map ( |parents| parents. copied ( ) . collect ( ) ) ,
54- runstates : matches. get_one :: < String > ( "runstates" ) . cloned ( ) ,
55- older : matches. get_one :: < u64 > ( "older" ) . copied ( ) ,
56- terminal : matches. get_many :: < String > ( "terminal" ) . map ( |ttys| {
57- ttys. cloned ( )
58- . flat_map ( Teletype :: try_from)
59- . collect :: < HashSet < _ > > ( )
60- } ) ,
61- signal : parse_signal_value ( matches. get_one :: < String > ( "signal" ) . unwrap ( ) ) ?,
62- require_handler : matches. get_flag ( "require-handler" ) ,
63- } ;
64-
65- if ( !settings. newest
66- && !settings. oldest
67- && settings. runstates . is_none ( )
68- && settings. older . is_none ( )
69- && settings. parent . is_none ( )
70- && settings. terminal . is_none ( ) )
71- && pattern. is_empty ( )
72- {
73- return Err ( USimpleError :: new (
74- 2 ,
75- "no matching criteria specified\n Try `pgrep --help' for more information." ,
76- ) ) ;
77- }
78- Ok ( settings)
79- }
80-
81- fn find_matching_pids ( settings : & Settings ) -> Vec < ProcessInformation > {
82- let mut pids = collect_matched_pids ( settings) ;
83- #[ cfg( unix) ]
84- if settings. require_handler {
85- pids. retain ( |pid| {
86- let mask =
87- u64:: from_str_radix ( pid. clone ( ) . status ( ) . get ( "SigCgt" ) . unwrap ( ) , 16 ) . unwrap ( ) ;
88- mask & ( 1 << settings. signal ) != 0
89- } ) ;
90- }
91- if pids. is_empty ( ) {
92- uucore:: error:: set_exit_code ( 1 ) ;
93- pids
94- } else {
95- process_flag_o_n ( settings, & mut pids)
96- }
97- }
98-
9916/// # Conceptual model of `pgrep`
10017///
10118/// At first, `pgrep` command will check the patterns is legal.
@@ -111,10 +28,10 @@ fn find_matching_pids(settings: &Settings) -> Vec<ProcessInformation> {
11128#[ uucore:: main]
11229pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
11330 let matches = uu_app ( ) . try_get_matches_from ( args) ?;
114- let settings = get_match_settings ( & matches) ?;
31+ let settings = process_matcher :: get_match_settings ( & matches) ?;
11532
11633 // Collect pids
117- let pids = find_matching_pids ( & settings) ;
34+ let pids = process_matcher :: find_matching_pids ( & settings) ;
11835
11936 // Processing output
12037 let output = if matches. get_flag ( "count" ) {
@@ -152,153 +69,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
15269 Ok ( ( ) )
15370}
15471
155- /// Try to get the pattern from the command line arguments. Returns an empty string if no pattern
156- /// is specified.
157- fn try_get_pattern_from ( matches : & ArgMatches ) -> UResult < String > {
158- let pattern = match matches. get_many :: < String > ( "pattern" ) {
159- Some ( patterns) if patterns. len ( ) > 1 => {
160- return Err ( USimpleError :: new (
161- 2 ,
162- "only one pattern can be provided\n Try `pgrep --help' for more information." ,
163- ) )
164- }
165- Some ( mut patterns) => patterns. next ( ) . unwrap ( ) ,
166- None => return Ok ( String :: new ( ) ) ,
167- } ;
168-
169- let pattern = if matches. get_flag ( "ignore-case" ) {
170- & pattern. to_lowercase ( )
171- } else {
172- pattern
173- } ;
174-
175- let pattern = if matches. get_flag ( "exact" ) {
176- & format ! ( "^{}$" , pattern)
177- } else {
178- pattern
179- } ;
180-
181- Ok ( pattern. to_string ( ) )
182- }
183-
184- /// Collect pids with filter construct from command line arguments
185- fn collect_matched_pids ( settings : & Settings ) -> Vec < ProcessInformation > {
186- // Filtration general parameters
187- let filtered: Vec < ProcessInformation > = {
188- let mut tmp_vec = Vec :: new ( ) ;
189-
190- for mut pid in walk_process ( ) . collect :: < Vec < _ > > ( ) {
191- let run_state_matched = match ( & settings. runstates , pid. run_state ( ) ) {
192- ( Some ( arg_run_states) , Ok ( pid_state) ) => {
193- arg_run_states. contains ( & pid_state. to_string ( ) )
194- }
195- ( _, Err ( _) ) => false ,
196- _ => true ,
197- } ;
198-
199- let binding = pid. status ( ) ;
200- let name = binding. get ( "Name" ) . unwrap ( ) ;
201- let name = if settings. ignore_case {
202- name. to_lowercase ( )
203- } else {
204- name. into ( )
205- } ;
206- let pattern_matched = {
207- let want = if settings. exact {
208- // Equals `Name` in /proc/<pid>/status
209- // The `unwrap` operation must succeed
210- // because the REGEX has been verified as correct in `uumain`.
211- & name
212- } else if settings. full {
213- // Equals `cmdline` in /proc/<pid>/cmdline
214- & pid. cmdline
215- } else {
216- // From manpage:
217- // The process name used for matching is limited to the 15 characters present in the output of /proc/pid/stat.
218- & pid. proc_stat ( ) [ ..15 ]
219- } ;
220-
221- settings. regex . is_match ( want)
222- } ;
223-
224- let tty_matched = match & settings. terminal {
225- Some ( ttys) => ttys. contains ( & pid. tty ( ) ) ,
226- None => true ,
227- } ;
228-
229- let arg_older = settings. older . unwrap_or ( 0 ) ;
230- let older_matched = pid. start_time ( ) . unwrap ( ) >= arg_older;
231-
232- // the PPID is the fourth field in /proc/<PID>/stat
233- // (https://www.kernel.org/doc/html/latest/filesystems/proc.html#id10)
234- let stat = pid. stat ( ) ;
235- let ppid = stat. get ( 3 ) ;
236- let parent_matched = match ( & settings. parent , ppid) {
237- ( Some ( parents) , Some ( ppid) ) => parents. contains ( & ppid. parse :: < u64 > ( ) . unwrap ( ) ) ,
238- _ => true ,
239- } ;
240-
241- if ( run_state_matched
242- && pattern_matched
243- && tty_matched
244- && older_matched
245- && parent_matched)
246- ^ settings. inverse
247- {
248- tmp_vec. push ( pid) ;
249- }
250- }
251- tmp_vec
252- } ;
253-
254- filtered
255- }
256-
257- /// Sorting pids for flag `-o` and `-n`.
258- ///
259- /// This function can also be used as a filter to filter out process information.
260- fn process_flag_o_n (
261- settings : & Settings ,
262- pids : & mut [ ProcessInformation ] ,
263- ) -> Vec < ProcessInformation > {
264- if settings. oldest || settings. newest {
265- pids. sort_by ( |a, b| {
266- b. clone ( )
267- . start_time ( )
268- . unwrap ( )
269- . cmp ( & a. clone ( ) . start_time ( ) . unwrap ( ) )
270- } ) ;
271-
272- let start_time = if settings. newest {
273- pids. first ( ) . cloned ( ) . unwrap ( ) . start_time ( ) . unwrap ( )
274- } else {
275- pids. last ( ) . cloned ( ) . unwrap ( ) . start_time ( ) . unwrap ( )
276- } ;
277-
278- // There might be some process start at same time, so need to be filtered.
279- let mut filtered = pids
280- . iter ( )
281- . filter ( |it| ( * it) . clone ( ) . start_time ( ) . unwrap ( ) == start_time)
282- . collect :: < Vec < _ > > ( ) ;
283-
284- if settings. newest {
285- filtered. sort_by ( |a, b| b. pid . cmp ( & a. pid ) ) ;
286- } else {
287- filtered. sort_by ( |a, b| a. pid . cmp ( & b. pid ) ) ;
288- }
289-
290- vec ! [ filtered. first( ) . cloned( ) . unwrap( ) . clone( ) ]
291- } else {
292- pids. to_vec ( )
293- }
294- }
295-
296- #[ cfg( unix) ]
297- fn parse_signal_value ( signal_name : & str ) -> UResult < usize > {
298- signal_by_name_or_value ( signal_name)
299- . ok_or_else ( || USimpleError :: new ( 1 , format ! ( "Unknown signal {}" , signal_name. quote( ) ) ) )
300- }
301-
30272#[ allow( clippy:: cognitive_complexity) ]
30373pub fn uu_app ( ) -> Command {
30474 Command :: new ( uucore:: util_name ( ) )
0 commit comments