@@ -187,6 +187,37 @@ impl TryFrom<&String> for RunState {
187187 }
188188}
189189
190+ /// Represents an entry in `/proc/<pid>/cgroup`.
191+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
192+ pub struct CgroupMembership {
193+ pub hierarchy_id : u32 ,
194+ pub controllers : Vec < String > ,
195+ pub cgroup_path : String ,
196+ }
197+
198+ impl TryFrom < & str > for CgroupMembership {
199+ type Error = io:: Error ;
200+
201+ fn try_from ( value : & str ) -> Result < Self , Self :: Error > {
202+ let parts: Vec < & str > = value. split ( ':' ) . collect ( ) ;
203+ if parts. len ( ) != 3 {
204+ return Err ( io:: ErrorKind :: InvalidData . into ( ) ) ;
205+ }
206+
207+ Ok ( CgroupMembership {
208+ hierarchy_id : parts[ 0 ]
209+ . parse :: < u32 > ( )
210+ . map_err ( |_| io:: ErrorKind :: InvalidData ) ?,
211+ controllers : if parts[ 1 ] . is_empty ( ) {
212+ vec ! [ ]
213+ } else {
214+ parts[ 1 ] . split ( ',' ) . map ( String :: from) . collect ( )
215+ } ,
216+ cgroup_path : parts[ 2 ] . to_string ( ) ,
217+ } )
218+ }
219+ }
220+
190221/// Process ID and its information
191222#[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
192223pub struct ProcessInformation {
@@ -383,6 +414,24 @@ impl ProcessInformation {
383414 read_link ( format ! ( "/proc/{}/root" , self . pid) )
384415 }
385416
417+ /// Returns cgroups (both v1 and v2) that the process belongs to.
418+ pub fn cgroups ( & mut self ) -> Result < Vec < CgroupMembership > , io:: Error > {
419+ fs:: read_to_string ( format ! ( "/proc/{}/cgroup" , self . pid) ) ?
420+ . lines ( )
421+ . map ( CgroupMembership :: try_from)
422+ . collect ( )
423+ }
424+
425+ /// Returns path to the v2 cgroup that the process belongs to.
426+ pub fn cgroup_v2_path ( & mut self ) -> Result < String , io:: Error > {
427+ const V2_HIERARCHY_ID : u32 = 0 ;
428+ self . cgroups ( ) ?
429+ . iter ( )
430+ . find ( |cg| cg. hierarchy_id == V2_HIERARCHY_ID )
431+ . map ( |cg| cg. cgroup_path . clone ( ) )
432+ . ok_or ( io:: ErrorKind :: NotFound . into ( ) )
433+ }
434+
386435 /// Fetch run state from [ProcessInformation::cached_stat]
387436 ///
388437 /// - [The /proc Filesystem: Table 1-4](https://docs.kernel.org/filesystems/proc.html#id10)
@@ -625,4 +674,22 @@ mod tests {
625674 let mut pid_entry = ProcessInformation :: current_process_info ( ) . unwrap ( ) ;
626675 assert_eq ! ( pid_entry. root( ) . unwrap( ) , PathBuf :: from( "/" ) ) ;
627676 }
677+
678+ #[ test]
679+ #[ cfg( target_os = "linux" ) ]
680+ fn test_cgroups ( ) {
681+ let mut pid_entry = ProcessInformation :: try_new ( "/proc/1" . into ( ) ) . unwrap ( ) ;
682+ if pid_entry. name ( ) . unwrap ( ) == "systemd" {
683+ let cgroups = pid_entry. cgroups ( ) . unwrap ( ) ;
684+ if let Some ( membership) = cgroups. iter ( ) . find ( |cg| cg. hierarchy_id == 0 ) {
685+ let expected = CgroupMembership {
686+ hierarchy_id : 0 ,
687+ controllers : vec ! [ ] ,
688+ cgroup_path : "/init.scope" . to_string ( ) ,
689+ } ;
690+ assert_eq ! ( membership, & expected) ;
691+ assert_eq ! ( pid_entry. cgroup_v2_path( ) . unwrap( ) , "/init.scope" ) ;
692+ }
693+ }
694+ }
628695}
0 commit comments