44// file that was distributed with this source code.
55
66// spell-checker:ignore exitstatus cmdline kworker pgrep pwait snice procps
7+ // spell-checker:ignore egid euid gettid ppid
78
89//! Set of functions to manage IDs
910//!
@@ -129,6 +130,14 @@ pub struct ProcessInformation {
129130 cached_stat : Option < Rc < Vec < String > > > ,
130131
131132 cached_start_time : Option < u64 > ,
133+
134+ cached_thread_ids : Option < Rc < Vec < usize > > > ,
135+ }
136+
137+ #[ derive( Clone , Copy , Debug ) ]
138+ enum UidGid {
139+ Uid ,
140+ Gid ,
132141}
133142
134143impl ProcessInformation {
@@ -237,6 +246,43 @@ impl ProcessInformation {
237246 Ok ( time)
238247 }
239248
249+ pub fn ppid ( & mut self ) -> Result < u64 , io:: Error > {
250+ // the PPID is the fourth field in /proc/<PID>/stat
251+ // (https://www.kernel.org/doc/html/latest/filesystems/proc.html#id10)
252+ self . stat ( )
253+ . get ( 3 )
254+ . ok_or ( io:: ErrorKind :: InvalidData ) ?
255+ . parse :: < u64 > ( )
256+ . map_err ( |_| io:: ErrorKind :: InvalidData . into ( ) )
257+ }
258+
259+ fn get_uid_or_gid_field ( & mut self , field : UidGid , index : usize ) -> Result < u32 , io:: Error > {
260+ self . status ( )
261+ . get ( & format ! ( "{:?}" , field) )
262+ . ok_or ( io:: ErrorKind :: InvalidData ) ?
263+ . split_whitespace ( )
264+ . nth ( index)
265+ . ok_or ( io:: ErrorKind :: InvalidData ) ?
266+ . parse :: < u32 > ( )
267+ . map_err ( |_| io:: ErrorKind :: InvalidData . into ( ) )
268+ }
269+
270+ pub fn uid ( & mut self ) -> Result < u32 , io:: Error > {
271+ self . get_uid_or_gid_field ( UidGid :: Uid , 0 )
272+ }
273+
274+ pub fn euid ( & mut self ) -> Result < u32 , io:: Error > {
275+ self . get_uid_or_gid_field ( UidGid :: Uid , 1 )
276+ }
277+
278+ pub fn gid ( & mut self ) -> Result < u32 , io:: Error > {
279+ self . get_uid_or_gid_field ( UidGid :: Gid , 0 )
280+ }
281+
282+ pub fn egid ( & mut self ) -> Result < u32 , io:: Error > {
283+ self . get_uid_or_gid_field ( UidGid :: Gid , 1 )
284+ }
285+
240286 /// Fetch run state from [ProcessInformation::cached_stat]
241287 ///
242288 /// - [The /proc Filesystem: Table 1-4](https://docs.kernel.org/filesystems/proc.html#id10)
@@ -271,8 +317,33 @@ impl ProcessInformation {
271317
272318 Teletype :: Unknown
273319 }
274- }
275320
321+ pub fn thread_ids ( & mut self ) -> Rc < Vec < usize > > {
322+ if let Some ( c) = & self . cached_thread_ids {
323+ return Rc :: clone ( c) ;
324+ }
325+
326+ let thread_ids_dir = format ! ( "/proc/{}/task" , self . pid) ;
327+ let result = Rc :: new (
328+ WalkDir :: new ( thread_ids_dir)
329+ . min_depth ( 1 )
330+ . max_depth ( 1 )
331+ . follow_links ( false )
332+ . into_iter ( )
333+ . flatten ( )
334+ . flat_map ( |it| {
335+ it. path ( )
336+ . file_name ( )
337+ . and_then ( |it| it. to_str ( ) )
338+ . and_then ( |it| it. parse :: < usize > ( ) . ok ( ) )
339+ } )
340+ . collect :: < Vec < _ > > ( ) ,
341+ ) ;
342+
343+ self . cached_thread_ids = Some ( Rc :: clone ( & result) ) ;
344+ Rc :: clone ( & result)
345+ }
346+ }
276347impl TryFrom < DirEntry > for ProcessInformation {
277348 type Error = io:: Error ;
278349
@@ -399,6 +470,25 @@ mod tests {
399470 ) ;
400471 }
401472
473+ #[ test]
474+ fn test_thread_ids ( ) {
475+ let main_tid = unsafe { uucore:: libc:: gettid ( ) } ;
476+ std:: thread:: spawn ( move || {
477+ let mut pid_entry = ProcessInformation :: try_new (
478+ PathBuf :: from_str ( & format ! ( "/proc/{}" , current_pid( ) ) ) . unwrap ( ) ,
479+ )
480+ . unwrap ( ) ;
481+ let thread_ids = pid_entry. thread_ids ( ) ;
482+
483+ assert ! ( thread_ids. contains( & ( main_tid as usize ) ) ) ;
484+
485+ let new_thread_tid = unsafe { uucore:: libc:: gettid ( ) } ;
486+ assert ! ( thread_ids. contains( & ( new_thread_tid as usize ) ) ) ;
487+ } )
488+ . join ( )
489+ . unwrap ( ) ;
490+ }
491+
402492 #[ test]
403493 fn test_stat_split ( ) {
404494 let case = "32 (idle_inject/3) S 2 0 0 0 -1 69238848 0 0 0 0 0 0 0 0 -51 0 1 0 34 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 3 50 1 0 0 0 0 0 0 0 0 0 0 0" ;
@@ -410,4 +500,16 @@ mod tests {
410500 let case = "47246 (kworker /10:1-events) I 2 0 0 0 -1 69238880 0 0 0 0 17 29 0 0 20 0 1 0 1396260 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 10 0 0 0 0 0 0 0 0 0 0 0 0 0" ;
411501 assert ! ( stat_split( case) [ 1 ] == "kworker /10:1-events" ) ;
412502 }
503+
504+ #[ test]
505+ fn test_uid_gid ( ) {
506+ let mut pid_entry = ProcessInformation :: try_new (
507+ PathBuf :: from_str ( & format ! ( "/proc/{}" , current_pid( ) ) ) . unwrap ( ) ,
508+ )
509+ . unwrap ( ) ;
510+ assert_eq ! ( pid_entry. uid( ) . unwrap( ) , uucore:: process:: getuid( ) ) ;
511+ assert_eq ! ( pid_entry. euid( ) . unwrap( ) , uucore:: process:: geteuid( ) ) ;
512+ assert_eq ! ( pid_entry. gid( ) . unwrap( ) , uucore:: process:: getgid( ) ) ;
513+ assert_eq ! ( pid_entry. egid( ) . unwrap( ) , uucore:: process:: getegid( ) ) ;
514+ }
413515}
0 commit comments