rpsutil 0.0.3

System monitoring
Documentation
use std::default::Default;
use std::ffi::CStr;
use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};

/// Struct to represent values in /proc/uptime.
#[derive(Debug, Default)]
pub struct Uptime {
    /// The uptime of the system (seconds).
    pub system: f64,

    /// The amount of time spent in idle process (seconds).
    pub idle: f64,
}

impl Uptime {
    // Parse /proc/uptime and initialize a new Uptime with the results.
    //
    // Availability: Linux
    fn new() -> Uptime {
        let mut result = Uptime {
            ..Default::default()
        };

        let contents =
            fs::read_to_string("/proc/uptime").expect("Something went wrong reading /proc/uptime");
        let vals: Vec<&str> = contents.trim().split(" ").collect();

        result.system = Self::parse(vals[0]);
        result.idle = Self::parse(vals[1]);

        result
    }

    fn parse(val: &str) -> f64 {
        val.parse::<f64>().unwrap()
    }
}

/// Return an instance of Uptime
///
/// Availability: Linux
///
/// # Examples
/// ```
/// let sys_uptime = rpsutil::system::uptime();
/// assert!(sys_uptime.system > 0.0);
/// ```
pub fn uptime() -> Uptime {
    Uptime::new()
}

/// Return the system boot time expressed in seconds since the epoch.
///
/// Availability: Linux
///
/// # Examples
/// ```
/// let boot = rpsutil::system::boot_time();
/// assert!(boot > 0);
/// ```
pub fn boot_time() -> u64 {
    match SystemTime::now().duration_since(UNIX_EPOCH) {
        Ok(n) => {
            let result = n.as_secs() as f64 - uptime().system;
            return result.round() as u64;
        }
        Err(_) => {
            return 0;
        }
    }
}

/// Struct to represent a system user.
#[derive(Debug)]
pub struct User {
    /// The name of the user.
    pub name: String,

    /// The tty or pseudo-tty associated with the user, if any.
    pub terminal: Option<String>,

    /// The hostname associated with the entry, if any.
    pub host: Option<String>,

    /// The creation time as seconds since the epoch.
    pub started: i32,

    /// The PID of the logged in process.
    pub pid: Option<i32>,
}

/// Return a list of system users currently logged in.
///
/// Availability: Linux
///
/// # Examples
/// ```
/// for user in rpsutil::system::users() {
///     println!("user: {:?}", user);
/// }
/// ```
pub fn users() -> Vec<User> {
    let mut result: Vec<User> = vec![];

    loop {
        let utmpx = unsafe { libc::getutxent() };
        if utmpx.is_null() {
            break;
        }

        if unsafe { (*utmpx).ut_type } == libc::USER_PROCESS {
            let name = cstr_to_string(unsafe { (*utmpx).ut_user.as_ptr() });
            let terminal = cstr_to_string(unsafe { (*utmpx).ut_line.as_ptr() });
            let host = cstr_to_string(unsafe { (*utmpx).ut_host.as_ptr() });

            let user = User {
                name: name,
                terminal: Some(terminal),
                host: Some(host),
                started: unsafe { (*utmpx).ut_tv.tv_sec },
                pid: unsafe { Some((*utmpx).ut_pid) },
            };

            result.push(user);
        }
    }

    result
}

/// Convert a C string to Rust string.
/// Input needs to be a pointer to C char.
fn cstr_to_string(str_ptr: *const libc::c_char) -> String {
    let cstr = unsafe { CStr::from_ptr(str_ptr) };
    cstr.to_string_lossy().into_owned()
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_uptime_struct() {
        let uptime = super::Uptime::new();
        assert!(uptime.system > 0.0);
        assert!(uptime.idle > 0.0);
    }
}