diff --git a/src/uucore/utmpx.rs b/src/uucore/utmpx.rs index c249e2a94..67b959541 100644 --- a/src/uucore/utmpx.rs +++ b/src/uucore/utmpx.rs @@ -1,125 +1,87 @@ -#![allow(dead_code, non_camel_case_types)] +//! Aim to provide platform-independent methods to obtain login records +//! +//! **ONLY** support linux, macos and freebsd for the time being -extern crate libc; +use super::libc; +pub extern crate time; +use self::time::{Tm, Timespec}; -pub use self::utmpx::{UT_NAMESIZE, UT_LINESIZE, DEFAULT_FILE, USER_PROCESS, BOOT_TIME}; -pub use self::utmpx::c_utmp; - -extern "C" { - pub fn getutxent() -> *const c_utmp; - pub fn getutxid(ut: *const c_utmp) -> *const c_utmp; - pub fn getutxline(ut: *const c_utmp) -> *const c_utmp; - pub fn pututxline(ut: *const c_utmp) -> *const c_utmp; - pub fn setutxent(); - pub fn endutxent(); - - #[cfg(any(target_os = "macos", target_os = "linux"))] - pub fn utmpxname(file: *const libc::c_char) -> libc::c_int; -} +use ::std::io::Result as IOResult; +use ::std::io::Error as IOError; +use ::std::ptr; +use ::std::borrow::Cow; +use ::std::ffi::CStr; +use ::std::ffi::CString; +pub use self::ut::*; +use libc::utmpx; +// pub use libc::getutxid; +// pub use libc::getutxline; +// pub use libc::pututxline; +pub use libc::getutxent; +pub use libc::setutxent; +pub use libc::endutxent; +#[cfg(any(target_os = "macos", target_os = "linux"))] +pub use libc::utmpxname; #[cfg(target_os = "freebsd")] -pub unsafe extern fn utmpxname(_file: *const libc::c_char) -> libc::c_int { +pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int { 0 } -#[cfg(target_os = "linux")] -mod utmpx { - use super::libc; +macro_rules! bytes2cow { + ($name:expr) => ( + unsafe { + CStr::from_ptr($name.as_ref().as_ptr()).to_string_lossy() + } + ) +} +#[cfg(target_os = "linux")] +mod ut { pub static DEFAULT_FILE: &'static str = "/var/run/utmp"; - pub const UT_LINESIZE: usize = 32; - pub const UT_NAMESIZE: usize = 32; + pub use libc::__UT_LINESIZE as UT_LINESIZE; + pub use libc::__UT_NAMESIZE as UT_NAMESIZE; + pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE; pub const UT_IDSIZE: usize = 4; - pub const UT_HOSTSIZE: usize = 256; - pub const EMPTY: libc::c_short = 0; - pub const RUN_LVL: libc::c_short = 1; - pub const BOOT_TIME: libc::c_short = 2; - pub const NEW_TIME: libc::c_short = 3; - pub const OLD_TIME: libc::c_short = 4; - pub const INIT_PROCESS: libc::c_short = 5; - pub const LOGIN_PROCESS: libc::c_short = 6; - pub const USER_PROCESS: libc::c_short = 7; - pub const DEAD_PROCESS: libc::c_short = 8; - pub const ACCOUNTING: libc::c_short = 9; - - #[repr(C)] - pub struct __exit_status { - pub e_termination: libc::c_short, - pub e_exit: libc::c_short, - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct __timeval { - pub tv_sec: libc::int32_t, - pub tv_usec: libc::int32_t, - } - - #[repr(C)] - pub struct c_utmp { - pub ut_type: libc::c_short, - pub ut_pid: libc::pid_t, - pub ut_line: [libc::c_char; UT_LINESIZE], - pub ut_id: [libc::c_char; UT_IDSIZE], - - pub ut_user: [libc::c_char; UT_NAMESIZE], - pub ut_host: [libc::c_char; UT_HOSTSIZE], - pub ut_exit: __exit_status, - - #[cfg(target_pointer_width = "32")] - pub ut_session: libc::c_long, - #[cfg(target_pointer_width = "32")] - pub ut_tv: libc::timeval, - - #[cfg(target_pointer_width = "64")] - pub ut_session: libc::int32_t, - #[cfg(target_pointer_width = "64")] - pub ut_tv: __timeval, - - pub ut_addr_v6: [libc::int32_t; 4], - __glibc_reserved: [libc::c_char; 20], - } + pub use libc::EMPTY; + pub use libc::RUN_LVL; + pub use libc::BOOT_TIME; + pub use libc::NEW_TIME; + pub use libc::OLD_TIME; + pub use libc::INIT_PROCESS; + pub use libc::LOGIN_PROCESS; + pub use libc::USER_PROCESS; + pub use libc::DEAD_PROCESS; + pub use libc::ACCOUNTING; } #[cfg(target_os = "macos")] -mod utmpx { - use super::libc; - +mod ut { pub static DEFAULT_FILE: &'static str = "/var/run/utmpx"; - pub const UT_LINESIZE: usize = 32; - pub const UT_NAMESIZE: usize = 256; - pub const UT_IDSIZE: usize = 4; - pub const UT_HOSTSIZE: usize = 256; + pub use libc::_UTX_LINESIZE as UT_LINESIZE; + pub use libc::_UTX_USERSIZE as UT_NAMESIZE; + pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE; + pub use libc::_UTX_IDSIZE as UT_IDSIZE; - pub const EMPTY: libc::c_short = 0; - pub const RUN_LVL: libc::c_short = 1; - pub const BOOT_TIME: libc::c_short = 2; - pub const OLD_TIME: libc::c_short = 3; - pub const NEW_TIME: libc::c_short = 4; - pub const INIT_PROCESS: libc::c_short = 5; - pub const LOGIN_PROCESS: libc::c_short = 6; - pub const USER_PROCESS: libc::c_short = 7; - pub const DEAD_PROCESS: libc::c_short = 8; - pub const ACCOUNTING: libc::c_short = 9; - - #[repr(C)] - pub struct c_utmp { - pub ut_user: [libc::c_char; UT_NAMESIZE], - pub ut_id: [libc::c_char; UT_IDSIZE], - pub ut_line: [libc::c_char; UT_LINESIZE], - pub ut_pid: libc::pid_t, - pub ut_type: libc::c_short, - pub ut_tv: libc::timeval, - pub ut_host: [libc::c_char; UT_HOSTSIZE], - pub __unused: [libc::uint32_t; 16], - } + pub use libc::EMPTY; + pub use libc::RUN_LVL; + pub use libc::BOOT_TIME; + pub use libc::NEW_TIME; + pub use libc::OLD_TIME; + pub use libc::INIT_PROCESS; + pub use libc::LOGIN_PROCESS; + pub use libc::USER_PROCESS; + pub use libc::DEAD_PROCESS; + pub use libc::ACCOUNTING; + pub use libc::SIGNATURE; + pub use libc::SHUTDOWN_TIME; } #[cfg(target_os = "freebsd")] -mod utmpx { +mod ut { use super::libc; pub static DEFAULT_FILE: &'static str = ""; @@ -129,25 +91,158 @@ mod utmpx { pub const UT_IDSIZE: usize = 8; pub const UT_HOSTSIZE: usize = 128; - pub const EMPTY: libc::c_short = 0; - pub const BOOT_TIME: libc::c_short = 1; - pub const OLD_TIME: libc::c_short = 2; - pub const NEW_TIME: libc::c_short = 3; - pub const USER_PROCESS: libc::c_short = 4; - pub const INIT_PROCESS: libc::c_short = 5; - pub const LOGIN_PROCESS: libc::c_short = 6; - pub const DEAD_PROCESS: libc::c_short = 7; - pub const SHUTDOWN_TIME: libc::c_short = 8; + pub use libc::EMPTY; + pub use libc::BOOT_TIME; + pub use libc::OLD_TIME; + pub use libc::NEW_TIME; + pub use libc::USER_PROCESS; + pub use libc::INIT_PROCESS; + pub use libc::LOGIN_PROCESS; + pub use libc::DEAD_PROCESS; + pub use libc::SHUTDOWN_TIME; +} - #[repr(C)] - pub struct c_utmp { - pub ut_type: libc::c_short, - pub ut_tv: libc::timeval, - pub ut_id: [libc::c_char; UT_IDSIZE], - pub ut_pid: libc::pid_t, - pub ut_user: [libc::c_char; UT_NAMESIZE], - pub ut_line: [libc::c_char; UT_LINESIZE], - pub ut_host: [libc::c_char; UT_HOSTSIZE], - pub ut_spare: [libc::c_char; 64], +/// Login records +/// +/// Examples: +/// --------- +/// +/// ``` +/// for ut in Utmpx::iter_all_records() { +/// if ut.is_user_process() { +/// println!("{}: {}", ut.host(), ut.user()) +/// } +/// } +/// ``` +/// +/// Specifying the path to login record: +/// +/// ``` +/// for ut in Utmpx::iter_all_records().read_from("/some/where/else") { +/// if ut.is_user_process() { +/// println!("{}: {}", ut.host(), ut.user()) +/// } +/// } +/// ``` +pub struct Utmpx { + inner: utmpx, +} + +impl Utmpx { + /// A.K.A. ut.ut_type + pub fn record_type(&self) -> i16 { + self.inner.ut_type as i16 + } + /// A.K.A. ut.ut_pid + pub fn pid(&self) -> i32 { + self.inner.ut_pid as i32 + } + /// A.K.A. ut.ut_id + pub fn terminal_suffix(&self) -> Cow { + bytes2cow!(self.inner.ut_id) + } + /// A.K.A. ut.ut_user + pub fn user(&self) -> Cow { + bytes2cow!(self.inner.ut_user) + } + /// A.K.A. ut.ut_host + pub fn host(&self) -> Cow { + bytes2cow!(self.inner.ut_host) + } + /// A.K.A. ut.ut_line + pub fn tty_device(&self) -> Cow { + bytes2cow!(self.inner.ut_line) + } + /// A.K.A. ut.ut_tv + pub fn login_time(&self) -> Tm { + time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64, + self.inner.ut_tv.tv_usec as i32)) + } + /// Consumes the `Utmpx`, returning the underlying C struct utmpx + pub fn into_inner(self) -> utmpx { + self.inner + } + pub fn is_user_process(&self) -> bool { + !self.user().is_empty() && self.record_type() == USER_PROCESS + } + + /// Canonicalize host name using DNS + pub fn canon_host(&self) -> IOResult { + const AI_CANONNAME: libc::c_int = 0x2; + let host = self.host(); + let hints = libc::addrinfo { + ai_flags: AI_CANONNAME, + ai_family: 0, + ai_socktype: 0, + ai_protocol: 0, + ai_addrlen: 0, + ai_addr: ptr::null_mut(), + ai_canonname: ptr::null_mut(), + ai_next: ptr::null_mut(), + }; + let c_host = CString::new(host.as_ref()).unwrap(); + let mut res = ptr::null_mut(); + let status = unsafe { + libc::getaddrinfo(c_host.as_ptr(), + ptr::null(), + &hints as *const _, + &mut res as *mut _) + }; + if status == 0 { + let info: libc::addrinfo = unsafe { ptr::read(res as *const _) }; + // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html + // says Darwin 7.9.0 getaddrinfo returns 0 but sets + // res->ai_canonname to NULL. + let ret = if info.ai_canonname.is_null() { + Ok(String::from(host.as_ref())) + } else { + Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() }) + }; + unsafe { + libc::freeaddrinfo(res); + } + ret + } else { + Err(IOError::last_os_error()) + } + } + pub fn iter_all_records() -> UtmpxIter { + UtmpxIter + } +} + +/// Iterator of login records +pub struct UtmpxIter; + +impl UtmpxIter { + /// Sets the name of the utmpx-format file for the other utmpx functions to access. + /// + /// If not set, default record file will be used(file path depends on the target OS) + pub fn read_from(self, f: &str) -> Self { + let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) }; + if res != 0 { + println!("Warning: {}", IOError::last_os_error()); + } + unsafe { + setutxent(); + } + self + } +} + +impl Iterator for UtmpxIter { + type Item = Utmpx; + fn next(&mut self) -> Option { + unsafe { + let res = getutxent(); + if !res.is_null() { + Some(Utmpx { + inner: ptr::read(res as *const _) + }) + } else { + endutxent(); + None + } + } } }