@@ -7,7 +7,7 @@ use fcntl::FcntlArg::F_SETFD;
77use libc:: { self , c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
88 uid_t, gid_t, mode_t, PATH_MAX } ;
99use std:: { fmt, mem, ptr} ;
10- use std:: ffi:: { CStr , OsString , OsStr } ;
10+ use std:: ffi:: { CString , CStr , OsString , OsStr } ;
1111use std:: os:: unix:: ffi:: { OsStringExt , OsStrExt } ;
1212use std:: os:: unix:: io:: RawFd ;
1313use std:: path:: PathBuf ;
@@ -2424,3 +2424,255 @@ pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
24242424 } ) ?;
24252425 Errno :: result ( res) . map ( drop)
24262426}
2427+
2428+ /// Representation of a User, based on `libc::passwd`
2429+ ///
2430+ /// The reason some fields in this struct are `String` and others are `CString` is because some
2431+ /// fields are based on the user's locale, which could be non-UTF8, while other fields are
2432+ /// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only
2433+ /// contains ASCII.
2434+ #[ derive( Debug , Clone , PartialEq ) ]
2435+ pub struct User {
2436+ /// Username
2437+ pub name : String ,
2438+ /// User password (probably encrypted)
2439+ pub passwd : CString ,
2440+ /// User ID
2441+ pub uid : Uid ,
2442+ /// Group ID
2443+ pub gid : Gid ,
2444+ /// User information
2445+ #[ cfg( not( target_os = "android" ) ) ]
2446+ pub gecos : CString ,
2447+ /// Home directory
2448+ pub dir : PathBuf ,
2449+ /// Path to shell
2450+ pub shell : PathBuf ,
2451+ /// Login class
2452+ #[ cfg( not( any( target_os = "android" , target_os = "linux" ) ) ) ]
2453+ pub class : CString ,
2454+ /// Last password change
2455+ #[ cfg( not( any( target_os = "android" , target_os = "linux" ) ) ) ]
2456+ pub change : libc:: time_t ,
2457+ /// Expiration time of account
2458+ #[ cfg( not( any( target_os = "android" , target_os = "linux" ) ) ) ]
2459+ pub expire : libc:: time_t
2460+ }
2461+
2462+ impl From < & libc:: passwd > for User {
2463+ fn from ( pw : & libc:: passwd ) -> User {
2464+ unsafe {
2465+ User {
2466+ name : CStr :: from_ptr ( ( * pw) . pw_name ) . to_string_lossy ( ) . into_owned ( ) ,
2467+ passwd : CString :: new ( CStr :: from_ptr ( ( * pw) . pw_passwd ) . to_bytes ( ) ) . unwrap ( ) ,
2468+ #[ cfg( not( target_os = "android" ) ) ]
2469+ gecos : CString :: new ( CStr :: from_ptr ( ( * pw) . pw_gecos ) . to_bytes ( ) ) . unwrap ( ) ,
2470+ dir : PathBuf :: from ( OsStr :: from_bytes ( CStr :: from_ptr ( ( * pw) . pw_dir ) . to_bytes ( ) ) ) ,
2471+ shell : PathBuf :: from ( OsStr :: from_bytes ( CStr :: from_ptr ( ( * pw) . pw_shell ) . to_bytes ( ) ) ) ,
2472+ uid : Uid :: from_raw ( ( * pw) . pw_uid ) ,
2473+ gid : Gid :: from_raw ( ( * pw) . pw_gid ) ,
2474+ #[ cfg( not( any( target_os = "android" , target_os = "linux" ) ) ) ]
2475+ class : CString :: new ( CStr :: from_ptr ( ( * pw) . pw_class ) . to_bytes ( ) ) . unwrap ( ) ,
2476+ #[ cfg( not( any( target_os = "android" , target_os = "linux" ) ) ) ]
2477+ change : ( * pw) . pw_change ,
2478+ #[ cfg( not( any( target_os = "android" , target_os = "linux" ) ) ) ]
2479+ expire : ( * pw) . pw_expire
2480+ }
2481+ }
2482+ }
2483+ }
2484+
2485+ impl User {
2486+ fn from_anything < F > ( f : F ) -> Result < Option < Self > >
2487+ where
2488+ F : Fn ( * mut libc:: passwd ,
2489+ * mut libc:: c_char ,
2490+ libc:: size_t ,
2491+ * mut * mut libc:: passwd ) -> libc:: c_int
2492+ {
2493+ let buflimit = 16384 ;
2494+ let bufsize = match sysconf ( SysconfVar :: GETPW_R_SIZE_MAX ) {
2495+ Ok ( Some ( n) ) => n as usize ,
2496+ Ok ( None ) | Err ( _) => buflimit as usize ,
2497+ } ;
2498+
2499+ let mut cbuf = Vec :: with_capacity ( bufsize) ;
2500+ let mut pwd = mem:: MaybeUninit :: < libc:: passwd > :: uninit ( ) ;
2501+ let mut res = ptr:: null_mut ( ) ;
2502+
2503+ loop {
2504+ let error = f ( pwd. as_mut_ptr ( ) , cbuf. as_mut_ptr ( ) , cbuf. capacity ( ) , & mut res) ;
2505+ if error == 0 {
2506+ if res. is_null ( ) {
2507+ return Ok ( None ) ;
2508+ } else {
2509+ let pwd = unsafe { pwd. assume_init ( ) } ;
2510+ return Ok ( Some ( User :: from ( & pwd) ) ) ;
2511+ }
2512+ } else if Errno :: last ( ) == Errno :: ERANGE {
2513+ // Trigger the internal buffer resizing logic.
2514+ reserve_double_buffer_size ( & mut cbuf, buflimit) ?;
2515+ } else {
2516+ return Err ( Error :: Sys ( Errno :: last ( ) ) ) ;
2517+ }
2518+ }
2519+ }
2520+
2521+ /// Get a user by UID.
2522+ ///
2523+ /// Internally, this function calls
2524+ /// [getpwuid_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
2525+ ///
2526+ /// # Examples
2527+ ///
2528+ /// ```
2529+ /// use nix::unistd::{Uid, User};
2530+ /// // Returns an Result<Option<User>>, thus the double unwrap.
2531+ /// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap();
2532+ /// assert!(res.name == "root");
2533+ /// ```
2534+ pub fn from_uid ( uid : Uid ) -> Result < Option < Self > > {
2535+ User :: from_anything ( |pwd, cbuf, cap, res| {
2536+ unsafe { libc:: getpwuid_r ( uid. 0 , pwd, cbuf, cap, res) }
2537+ } )
2538+ }
2539+
2540+ /// Get a user by name.
2541+ ///
2542+ /// Internally, this function calls
2543+ /// [getpwnam_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
2544+ ///
2545+ /// # Examples
2546+ ///
2547+ /// ```
2548+ /// use nix::unistd::User;
2549+ /// // Returns an Result<Option<User>>, thus the double unwrap.
2550+ /// let res = User::from_name("root").unwrap().unwrap();
2551+ /// assert!(res.name == "root");
2552+ /// ```
2553+ pub fn from_name ( name : & str ) -> Result < Option < Self > > {
2554+ let name = CString :: new ( name) . unwrap ( ) ;
2555+ User :: from_anything ( |pwd, cbuf, cap, res| {
2556+ unsafe { libc:: getpwnam_r ( name. as_ptr ( ) , pwd, cbuf, cap, res) }
2557+ } )
2558+ }
2559+ }
2560+
2561+ /// Representation of a Group, based on `libc::group`
2562+ #[ derive( Debug , Clone , PartialEq ) ]
2563+ pub struct Group {
2564+ /// Group name
2565+ pub name : String ,
2566+ /// Group ID
2567+ pub gid : Gid ,
2568+ /// List of Group members
2569+ pub mem : Vec < String >
2570+ }
2571+
2572+ impl From < & libc:: group > for Group {
2573+ fn from ( gr : & libc:: group ) -> Group {
2574+ unsafe {
2575+ Group {
2576+ name : CStr :: from_ptr ( ( * gr) . gr_name ) . to_string_lossy ( ) . into_owned ( ) ,
2577+ gid : Gid :: from_raw ( ( * gr) . gr_gid ) ,
2578+ mem : Group :: members ( ( * gr) . gr_mem )
2579+ }
2580+ }
2581+ }
2582+ }
2583+
2584+ impl Group {
2585+ unsafe fn members ( mem : * mut * mut c_char ) -> Vec < String > {
2586+ let mut ret = Vec :: new ( ) ;
2587+
2588+ for i in 0 .. {
2589+ let u = mem. offset ( i) ;
2590+ if ( * u) . is_null ( ) {
2591+ break ;
2592+ } else {
2593+ let s = CStr :: from_ptr ( * u) . to_string_lossy ( ) . into_owned ( ) ;
2594+ ret. push ( s) ;
2595+ }
2596+ }
2597+
2598+ ret
2599+ }
2600+
2601+ fn from_anything < F > ( f : F ) -> Result < Option < Self > >
2602+ where
2603+ F : Fn ( * mut libc:: group ,
2604+ * mut libc:: c_char ,
2605+ libc:: size_t ,
2606+ * mut * mut libc:: group ) -> libc:: c_int
2607+ {
2608+ let buflimit = 16384 ;
2609+ let bufsize = match sysconf ( SysconfVar :: GETGR_R_SIZE_MAX ) {
2610+ Ok ( Some ( n) ) => n as usize ,
2611+ Ok ( None ) | Err ( _) => buflimit as usize ,
2612+ } ;
2613+
2614+ let mut cbuf = Vec :: with_capacity ( bufsize) ;
2615+ let mut grp = mem:: MaybeUninit :: < libc:: group > :: uninit ( ) ;
2616+ let mut res = ptr:: null_mut ( ) ;
2617+
2618+ loop {
2619+ let error = f ( grp. as_mut_ptr ( ) , cbuf. as_mut_ptr ( ) , cbuf. capacity ( ) , & mut res) ;
2620+ if error == 0 {
2621+ if res. is_null ( ) {
2622+ return Ok ( None ) ;
2623+ } else {
2624+ let grp = unsafe { grp. assume_init ( ) } ;
2625+ return Ok ( Some ( Group :: from ( & grp) ) ) ;
2626+ }
2627+ } else if Errno :: last ( ) == Errno :: ERANGE {
2628+ // Trigger the internal buffer resizing logic.
2629+ reserve_double_buffer_size ( & mut cbuf, buflimit) ?;
2630+ } else {
2631+ return Err ( Error :: Sys ( Errno :: last ( ) ) ) ;
2632+ }
2633+ }
2634+ }
2635+
2636+ /// Get a group by GID.
2637+ ///
2638+ /// Internally, this function calls
2639+ /// [getgrgid_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
2640+ ///
2641+ /// # Examples
2642+ ///
2643+ // Disable this test on all OS except Linux as root group may not exist.
2644+ #[ cfg_attr( not( target_os = "linux" ) , doc = " ```no_run" ) ]
2645+ #[ cfg_attr( target_os = "linux" , doc = " ```" ) ]
2646+ /// use nix::unistd::{Gid, Group};
2647+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
2648+ /// let res = Group::from_gid(Gid::from_raw(0)).unwrap().unwrap();
2649+ /// assert!(res.name == "root");
2650+ /// ```
2651+ pub fn from_gid ( gid : Gid ) -> Result < Option < Self > > {
2652+ Group :: from_anything ( |grp, cbuf, cap, res| {
2653+ unsafe { libc:: getgrgid_r ( gid. 0 , grp, cbuf, cap, res) }
2654+ } )
2655+ }
2656+
2657+ /// Get a group by name.
2658+ ///
2659+ /// Internally, this function calls
2660+ /// [getgrnam_r(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
2661+ ///
2662+ /// # Examples
2663+ ///
2664+ // Disable this test on all OS except Linux as root group may not exist.
2665+ #[ cfg_attr( not( target_os = "linux" ) , doc = " ```no_run" ) ]
2666+ #[ cfg_attr( target_os = "linux" , doc = " ```" ) ]
2667+ /// use nix::unistd::Group;
2668+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
2669+ /// let res = Group::from_name("root").unwrap().unwrap();
2670+ /// assert!(res.name == "root");
2671+ /// ```
2672+ pub fn from_name ( name : & str ) -> Result < Option < Self > > {
2673+ let name = CString :: new ( name) . unwrap ( ) ;
2674+ Group :: from_anything ( |grp, cbuf, cap, res| {
2675+ unsafe { libc:: getgrnam_r ( name. as_ptr ( ) , grp, cbuf, cap, res) }
2676+ } )
2677+ }
2678+ }
0 commit comments