#![crate_name = "users"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![allow(unused_features)]
#![feature(core, libc, std_misc)]
extern crate libc;
use libc::{c_char, c_int, uid_t, gid_t, time_t};
use std::ffi::c_str_to_bytes;
use std::ptr::read;
use std::str::from_utf8_unchecked;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
pub mod mock;
pub trait Users {
fn get_user_by_uid(&mut self, uid: i32) -> Option<User>;
fn get_user_by_name(&mut self, username: String) -> Option<User>;
fn get_group_by_gid(&mut self, gid: u32) -> Option<Group>;
fn get_group_by_name(&mut self, group_name: String) -> Option<Group>;
fn get_current_uid(&mut self) -> i32;
fn get_current_username(&mut self) -> Option<String>;
}
#[repr(C)]
struct c_passwd {
pub pw_name: *const c_char, pub pw_passwd: *const c_char,
pub pw_uid: c_int, pub pw_gid: c_int, pub pw_change: time_t,
pub pw_class: *const c_char,
pub pw_gecos: *const c_char, pub pw_dir: *const c_char, pub pw_shell: *const c_char, pub pw_expire: time_t, }
impl Copy for c_passwd { }
#[repr(C)]
struct c_group {
pub gr_name: *const c_char, pub gr_passwd: *const c_char, pub gr_gid: gid_t, pub gr_mem: *const *const c_char, }
impl Copy for c_group { }
extern {
fn getpwuid(uid: c_int) -> *const c_passwd;
fn getpwnam(user_name: *const c_char) -> *const c_passwd;
fn getgrgid(gid: uid_t) -> *const c_group;
fn getgrnam(group_name: *const c_char) -> *const c_group;
fn getuid() -> c_int;
}
#[derive(Clone)]
pub struct User {
pub uid: i32,
pub name: String,
pub primary_group: u32,
}
#[derive(Clone)]
pub struct Group {
pub gid: u32,
pub name: String,
pub members: Vec<String>,
}
#[derive(Clone)]
pub struct OSUsers {
users: HashMap<i32, Option<User>>,
users_back: HashMap<String, Option<i32>>,
groups: HashMap<u32, Option<Group>>,
groups_back: HashMap<String, Option<u32>>,
uid: Option<i32>,
}
unsafe fn from_raw_buf(p: *const i8) -> String {
from_utf8_unchecked(c_str_to_bytes(&p)).to_string()
}
unsafe fn passwd_to_user(pointer: *const c_passwd) -> Option<User> {
if !pointer.is_null() {
let pw = read(pointer);
Some(User { uid: pw.pw_uid, name: from_raw_buf(pw.pw_name as *const i8), primary_group: pw.pw_gid as u32 })
}
else {
None
}
}
unsafe fn struct_to_group(pointer: *const c_group) -> Option<Group> {
if !pointer.is_null() {
let gr = read(pointer);
let name = from_raw_buf(gr.gr_name as *const i8);
let members = members(gr.gr_mem);
Some(Group { gid: gr.gr_gid, name: name, members: members })
}
else {
None
}
}
unsafe fn members(groups: *const *const c_char) -> Vec<String> {
let mut i = 0;
let mut members = vec![];
loop {
match groups.offset(i).as_ref() {
Some(&username) => {
if !username.is_null() {
members.push(from_raw_buf(username as *const i8));
}
else {
return members;
}
i += 1;
},
None => return members,
}
}
}
impl Users for OSUsers {
fn get_user_by_uid(&mut self, uid: i32) -> Option<User> {
match self.users.entry(uid) {
Vacant(entry) => {
let user = unsafe { passwd_to_user(getpwuid(uid as i32)) };
match user {
Some(user) => {
entry.insert(Some(user.clone()));
self.users_back.insert(user.name.clone(), Some(user.uid));
Some(user)
},
None => {
entry.insert(None);
None
}
}
},
Occupied(entry) => entry.get().clone(),
}
}
fn get_user_by_name(&mut self, username: String) -> Option<User> {
match self.users_back.entry(username.clone()) {
Vacant(entry) => {
let user = unsafe { passwd_to_user(getpwnam(username.as_ptr() as *const i8)) };
match user {
Some(user) => {
entry.insert(Some(user.uid));
self.users.insert(user.uid, Some(user.clone()));
Some(user)
},
None => {
entry.insert(None);
None
}
}
},
Occupied(entry) => match entry.get() {
&Some(uid) => self.users[uid].clone(),
&None => None,
}
}
}
fn get_group_by_gid(&mut self, gid: u32) -> Option<Group> {
match self.groups.clone().entry(gid) {
Vacant(entry) => {
let group = unsafe { struct_to_group(getgrgid(gid)) };
match group {
Some(group) => {
entry.insert(Some(group.clone()));
self.groups_back.insert(group.name.clone(), Some(group.gid));
Some(group)
},
None => {
entry.insert(None);
None
}
}
},
Occupied(entry) => entry.get().clone(),
}
}
fn get_group_by_name(&mut self, group_name: String) -> Option<Group> {
match self.groups_back.clone().entry(group_name.clone()) {
Vacant(entry) => {
let user = unsafe { struct_to_group(getgrnam(group_name.as_ptr() as *const i8)) };
match user {
Some(group) => {
entry.insert(Some(group.gid));
self.groups.insert(group.gid, Some(group.clone()));
Some(group)
},
None => {
entry.insert(None);
None
}
}
},
Occupied(entry) => match entry.get() {
&Some(gid) => self.groups[gid].clone(),
&None => None,
}
}
}
fn get_current_uid(&mut self) -> i32 {
match self.uid {
Some(uid) => uid,
None => {
let uid = unsafe { getuid() };
self.uid = Some(uid);
uid
}
}
}
fn get_current_username(&mut self) -> Option<String> {
let uid = self.get_current_uid();
self.get_user_by_uid(uid).map(|u| u.name)
}
}
impl OSUsers {
pub fn empty_cache() -> OSUsers {
OSUsers {
users: HashMap::new(),
users_back: HashMap::new(),
groups: HashMap::new(),
groups_back: HashMap::new(),
uid: None,
}
}
}
pub fn get_user_by_uid(uid: i32) -> Option<User> {
OSUsers::empty_cache().get_user_by_uid(uid)
}
pub fn get_user_by_name(username: String) -> Option<User> {
OSUsers::empty_cache().get_user_by_name(username)
}
pub fn get_group_by_gid(gid: u32) -> Option<Group> {
OSUsers::empty_cache().get_group_by_gid(gid)
}
pub fn get_group_by_name(group_name: String) -> Option<Group> {
OSUsers::empty_cache().get_group_by_name(group_name)
}
pub fn get_current_uid() -> i32 {
OSUsers::empty_cache().get_current_uid()
}
pub fn get_current_username() -> Option<String> {
OSUsers::empty_cache().get_current_username()
}
#[cfg(test)]
mod test {
use super::{Users, OSUsers, get_current_username};
#[test]
fn uid() {
OSUsers::empty_cache().get_current_uid();
}
#[test]
fn username() {
let mut users = OSUsers::empty_cache();
let uid = users.get_current_uid();
assert_eq!(get_current_username().unwrap(), users.get_user_by_uid(uid).unwrap().name);
}
#[test]
fn uid_for_username() {
let mut users = OSUsers::empty_cache();
let uid = users.get_current_uid();
let user = users.get_user_by_uid(uid).unwrap();
assert_eq!(user.uid, uid);
}
#[test]
fn username_for_uid_for_username() {
let mut users = OSUsers::empty_cache();
let uid = users.get_current_uid();
let user = users.get_user_by_uid(uid).unwrap();
let user2 = users.get_user_by_uid(user.uid).unwrap();
assert_eq!(user2.uid, uid);
}
}