@@ -7,7 +7,7 @@ use fcntl::FcntlArg::F_SETFD;
7
7
use libc:: { self , c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
8
8
uid_t, gid_t, mode_t, PATH_MAX } ;
9
9
use std:: { fmt, mem, ptr} ;
10
- use std:: ffi:: { CStr , OsString , OsStr } ;
10
+ use std:: ffi:: { CString , CStr , OsString , OsStr } ;
11
11
use std:: os:: unix:: ffi:: { OsStringExt , OsStrExt } ;
12
12
use std:: os:: unix:: io:: RawFd ;
13
13
use std:: path:: PathBuf ;
@@ -2424,3 +2424,255 @@ pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
2424
2424
} ) ?;
2425
2425
Errno :: result ( res) . map ( drop)
2426
2426
}
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