use libc::{c_char, endpwent, getpwent, getpwnam, getpwuid, getuid, passwd, setpwent};
use std::ffi::{CStr, CString};
use crate::errors::{PwdError, Result};
#[derive(Debug, Clone, PartialEq)]
pub struct Passwd {
pub name: String,
pub passwd: Option<String>,
pub uid: u32,
pub gid: u32,
pub gecos: Option<String>,
pub dir: String,
pub shell: String,
}
#[derive(Debug, Clone, PartialEq)]
#[doc(hidden)]
pub struct PasswdIter {
inited: bool,
}
impl PasswdIter {
fn new() -> PasswdIter {
PasswdIter { inited: false }
}
}
impl Iterator for PasswdIter {
type Item = Passwd;
fn next(&mut self) -> Option<Self::Item> {
if !self.inited {
unsafe {
setpwent();
};
self.inited = true;
}
let next = unsafe { getpwent() };
if next.is_null() {
unsafe {
endpwent();
};
return None;
}
if let Ok(passwd) = Passwd::from_unsafe(next) {
Some(passwd)
} else {
None
}
}
}
fn cstr_to_string(cstr: *const c_char) -> Result<String> {
let cstr = unsafe { CStr::from_ptr(cstr) };
Ok(cstr
.to_str()
.map_err(|e| PwdError::StringConvError(format!("{:?}", e)))?
.to_string())
}
impl Passwd {
fn from_unsafe(pwd: *mut passwd) -> Result<Passwd> {
if pwd.is_null() {
return Err(PwdError::NullPtr);
}
let pwd = unsafe { *pwd };
let password = if pwd.pw_passwd.is_null() {
None
} else {
Some(cstr_to_string(pwd.pw_passwd)?)
};
let gecos = if pwd.pw_gecos.is_null() {
None
} else {
Some(cstr_to_string(pwd.pw_gecos)?)
};
Ok(Passwd {
name: cstr_to_string(pwd.pw_name)?,
passwd: password,
uid: pwd.pw_uid as u32,
gid: pwd.pw_gid as u32,
gecos,
dir: cstr_to_string(pwd.pw_dir)?,
shell: cstr_to_string(pwd.pw_shell)?,
})
}
pub fn from_name(name: &str) -> Result<Option<Passwd>> {
let cname =
CString::new(name).map_err(|e| PwdError::StringConvError(format!("{:?}", e)))?;
let pwd = unsafe { getpwnam(cname.as_ptr()) };
if pwd.is_null() {
Ok(None)
} else {
Ok(Some(Passwd::from_unsafe(pwd)?))
}
}
pub fn from_uid(uid: u32) -> Option<Passwd> {
let pwd = unsafe { getpwuid(uid) };
Passwd::from_unsafe(pwd).ok()
}
pub fn current_user() -> Option<Passwd> {
let uid = unsafe { getuid() };
Passwd::from_uid(uid as u32)
}
pub fn iter() -> PasswdIter {
PasswdIter::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::u32;
#[test]
fn test_null_pwd_from_uid() {
let should_be_none = Passwd::from_uid(u32::MAX);
assert_eq!(should_be_none, None);
}
}