Skip to content

Commit

Permalink
Merge branch 'polachok-all-users'
Browse files Browse the repository at this point in the history
  • Loading branch information
ogham committed Jan 29, 2016
2 parents 3e1a0b5 + 0d9a391 commit ac85727
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 2 deletions.
11 changes: 11 additions & 0 deletions examples/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extern crate users;
use users::{User, AllUsers};

fn main() {
let mut users: Vec<User> = unsafe { AllUsers::new() }.collect();
users.sort_by(|a, b| a.uid().cmp(&b.uid()));

for user in users {
println!("User {} has name {}", user.uid(), user.name());
}
}
54 changes: 54 additions & 0 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ extern {

fn getgid() -> gid_t;
fn getegid() -> gid_t;

fn setpwent();
fn getpwent() -> *const c_passwd;
fn endpwent();
}


Expand Down Expand Up @@ -385,6 +389,56 @@ pub fn get_effective_groupname() -> Option<String> {
}


/// An iterator over every user present on the system.
///
/// This struct actually requires no fields, but has one hidden one to make it
/// `unsafe` to create.
pub struct AllUsers(());

impl AllUsers {

/// Creates a new iterator over every user present on the system.
///
/// ## Unsafety
///
/// This constructor is marked as `unsafe`, which is odd for a crate
/// that's meant to be a safe interface. It *has* to be unsafe because
/// we cannot guarantee that the underlying C functions,
/// `getpwent`/`setpwent`/`endpwent` that iterate over the system's
/// `passwd` entries, are called in a thread-safe manner.
///
/// These functions [modify a global
/// state](http://man7.org/linux/man-pages/man3/getpwent.3.html#
/// ATTRIBUTES), and if any are used at the same time, the state could
/// be reset, resulting in a data race. We cannot even place it behind
/// an internal `Mutex`, as there is nothing stopping another `extern`
/// function definition from calling it!
///
/// So to iterate all users, construct the iterator inside an `unsafe`
/// block, then make sure to not make a new instance of it until
/// iteration is over.
pub unsafe fn new() -> AllUsers {
setpwent();
AllUsers(())
}
}

impl Drop for AllUsers {
fn drop(&mut self) {
unsafe { endpwent() };
}
}

impl Iterator for AllUsers {
type Item = User;

fn next(&mut self) -> Option<User> {
unsafe { passwd_to_user(getpwent()) }
}
}



/// OS-specific extensions to users and groups.
///
/// Every OS has a different idea of what data a user or a group comes with.
Expand Down
21 changes: 19 additions & 2 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::sync::Arc;

use base::{User, Group};
use base::{User, Group, AllUsers};
use traits::{Users, Groups};


Expand Down Expand Up @@ -120,10 +120,27 @@ impl Default for UsersCache {

impl UsersCache {

/// Create a new empty cache.
/// Creates a new empty cache.
pub fn new() -> UsersCache {
UsersCache::default()
}

/// Creates a new cache that contains all the users present on the system.
///
/// This is `unsafe` because we cannot prevent data races if two caches
/// were attempted to be initialised on different threads at the same time.
pub unsafe fn with_all_users() -> UsersCache {
let cache = UsersCache::new();

for user in AllUsers::new() {
let uid = user.uid();
let user_arc = Arc::new(user);
cache.users.forward.borrow_mut().insert(uid, Some(user_arc.clone()));
cache.users.backward.borrow_mut().insert(user_arc.name_arc.clone(), Some(uid));
}

cache
}
}

impl Users for UsersCache {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub use base::{get_current_uid, get_current_username};
pub use base::{get_effective_uid, get_effective_username};
pub use base::{get_current_gid, get_current_groupname};
pub use base::{get_effective_gid, get_effective_groupname};
pub use base::AllUsers;


pub mod cache;
Expand Down

0 comments on commit ac85727

Please sign in to comment.