diff --git a/src/main.rs b/src/main.rs index 021303f..ecd2948 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,8 @@ use util::*; mod log; +mod sys; + mod interval; mod config; diff --git a/src/sys/user.rs b/src/sys/user.rs index ea43067..26d836a 100644 --- a/src/sys/user.rs +++ b/src/sys/user.rs @@ -1 +1,85 @@ //! passwd queries +use super::*; +use std::sync::Mutex; +use libc::{ + getpwent, + endpwent +}; + +/// Safe wrapper around C's passwd struct. Doesn't implement the +/// `passwd', `gecos' or `expire' fields because we don't use them. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Passwd { + name: String, + uid: u32, + gid: u32, + home: String, + shell: String, +} + +/// Returns a vector of the system's users +pub fn get_users() -> std::vec::Vec { + lazy_static::lazy_static! { + static ref MUTEX: Mutex<()> = Mutex::new(()); + } + + let mut users = vec!(); + + unsafe { + // This could maybe be simplified but there's no point + macro_rules! from_cstr { + ($x:expr, $fmt:literal) => { + match std::ffi::CStr::from_ptr($x).to_owned().into_string() { + Ok(v) => v, + Err(v) => { + warn!($fmt, v); + continue; + } + } + }; + ($x:expr, $fmt:literal, $usr:expr) => { + match std::ffi::CStr::from_ptr($x).to_owned().into_string() { + Ok(v) => v, + Err(v) => { + warn!($fmt, $usr, v); + continue; + } + } + } + } + + let _lock = MUTEX.lock().expect("poisoned"); + loop { + let passwd = getpwent(); + + // FFI returns NULL when no more entries + if passwd.is_null() { + break; + } + + let my_passwd = *passwd; + let name = from_cstr!( + my_passwd.pw_name, + "User {} has invalid UTF-8 characters, skipping" + ); + + users.push(Passwd{ + gid: 1, + uid: 2, + home: from_cstr!( + my_passwd.pw_dir, + "User {}'s home ({}) has invalid UTF-8 characters, skipping", + name + ), + shell: from_cstr!( + my_passwd.pw_shell, + "User {}'s shell ({}) has invalid UTF-8 characters, skipping", + name + ), + name + }); + } + endpwent(); + } + return users; +}