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