From ef4a5e965d2e37e5c2aeb23a12f2d99dd24525b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Neder?= Date: Tue, 18 Mar 2014 09:17:32 -0300 Subject: [PATCH] Implement uptime Move utmp struct from users to common/utmpx.rs --- Makefile | 3 +- common/c_types.rs | 12 ++++ common/utmpx.rs | 86 ++++++++++++++++++++++++ uptime/uptime.rs | 168 ++++++++++++++++++++++++++++++++++++++++++++++ users/users.rs | 84 +---------------------- 5 files changed, 270 insertions(+), 83 deletions(-) create mode 100644 common/utmpx.rs create mode 100644 uptime/uptime.rs diff --git a/Makefile b/Makefile index 991eeb617..0b998facf 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,8 @@ UNIX_PROGS := \ whoami \ tty \ groups \ - id + id \ + uptime ifneq ($(OS),Windows_NT) PROGS := $(PROGS) $(UNIX_PROGS) diff --git a/common/c_types.rs b/common/c_types.rs index c6d1bc16f..a7d412e2b 100644 --- a/common/c_types.rs +++ b/common/c_types.rs @@ -32,6 +32,18 @@ pub struct c_group { gr_name: *c_char /* group name */ } +pub struct c_tm { + tm_sec: c_int, /* seconds */ + tm_min: c_int, /* minutes */ + tm_hour: c_int, /* hours */ + tm_mday: c_int, /* day of the month */ + tm_mon: c_int, /* month */ + tm_year: c_int, /* year */ + tm_wday: c_int, /* day of the week */ + tm_yday: c_int, /* day in the year */ + tm_isdst: c_int /* daylight saving time */ +} + extern { pub fn getpwuid(uid: c_int) -> *c_passwd; pub fn getpwnam(login: *c_char) -> *c_passwd; diff --git a/common/utmpx.rs b/common/utmpx.rs new file mode 100644 index 000000000..7d6dc3458 --- /dev/null +++ b/common/utmpx.rs @@ -0,0 +1,86 @@ +#[allow(non_camel_case_types)]; + +pub use self::utmpx::{DEFAULT_FILE,USER_PROCESS,c_utmp}; +#[cfg(target_os = "linux")] +mod utmpx { + use std::libc; + + pub static DEFAULT_FILE: &'static str = "/var/run/utmp"; + + pub static UT_LINESIZE: uint = 32; + pub static UT_NAMESIZE: uint = 32; + pub static UT_IDSIZE: uint = 4; + pub static UT_HOSTSIZE: uint = 256; + + pub static EMPTY: libc::c_short = 0; + pub static RUN_LVL: libc::c_short = 1; + pub static BOOT_TIME: libc::c_short = 2; + pub static NEW_TIME: libc::c_short = 3; + pub static OLD_TIME: libc::c_short = 4; + pub static INIT_PROCESS: libc::c_short = 5; + pub static LOGIN_PROCESS: libc::c_short = 6; + pub static USER_PROCESS: libc::c_short = 7; + pub static DEAD_PROCESS: libc::c_short = 8; + pub static ACCOUNTING: libc::c_short = 9; + + pub struct c_exit_status { + e_termination: libc::c_short, + e_exit: libc::c_short, + } + + pub struct c_utmp { + ut_type: libc::c_short, + ut_pid: libc::pid_t, + ut_line: [libc::c_char, ..UT_LINESIZE], + ut_id: [libc::c_char, ..UT_IDSIZE], + + ut_user: [libc::c_char, ..UT_NAMESIZE], + ut_host: [libc::c_char, ..UT_HOSTSIZE], + ut_exit: c_exit_status, + ut_session: libc::c_long, + ut_tv: libc::timeval, + + ut_addr_v6: [libc::int32_t, ..4], + __unused: [libc::c_char, ..20], + } +} + +#[cfg(target_os = "macos")] +mod utmpx { + use std::libc; + + pub static DEFAULT_FILE: &'static str = "/var/run/utmpx"; + + pub static UT_LINESIZE: uint = 32; + pub static UT_NAMESIZE: uint = 256; + pub static UT_IDSIZE: uint = 4; + pub static UT_HOSTSIZE: uint = 256; + + pub static EMPTY: libc::c_short = 0; + pub static RUN_LVL: libc::c_short = 1; + pub static BOOT_TIME: libc::c_short = 2; + pub static OLD_TIME: libc::c_short = 3; + pub static NEW_TIME: libc::c_short = 4; + pub static INIT_PROCESS: libc::c_short = 5; + pub static LOGIN_PROCESS: libc::c_short = 6; + pub static USER_PROCESS: libc::c_short = 7; + pub static DEAD_PROCESS: libc::c_short = 8; + pub static ACCOUNTING: libc::c_short = 9; + + pub struct c_exit_status { + e_termination: libc::c_short, + e_exit: libc::c_short, + } + + pub struct c_utmp { + ut_user: [libc::c_char, ..UT_NAMESIZE], + ut_id: [libc::c_char, ..UT_IDSIZE], + ut_line: [libc::c_char, ..UT_LINESIZE], + ut_pid: libc::pid_t, + ut_type: libc::c_short, + ut_tv: libc::timeval, + ut_host: [libc::c_char, ..UT_HOSTSIZE], + __unused: [libc::c_char, ..16] + } +} + diff --git a/uptime/uptime.rs b/uptime/uptime.rs new file mode 100644 index 000000000..bf64e030c --- /dev/null +++ b/uptime/uptime.rs @@ -0,0 +1,168 @@ +#[crate_id(name="uptime", vers="1.0.0", author="José Neder")]; + +/* + * This file is part of the uutils coreutils package. + * + * (c) Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* last synced with: cat (GNU coreutils) 8.13 */ + +#[allow(non_camel_case_types)]; +#[feature(macro_rules, globs)]; + +extern crate getopts; + +use std::os; +use std::cast::transmute; +use std::io::{print,File}; +use std::libc::{time_t,c_double,c_int,size_t,c_char}; +use std::ptr::null; +use std::from_str::from_str; +use c_types::c_tm; +use utmpx::*; + +#[path = "../common/util.rs"] mod util; + +#[path = "../common/c_types.rs"] mod c_types; + +#[path = "../common/utmpx.rs"] mod utmpx; + +static NAME: &'static str = "uptime"; + +extern { + fn time(timep: *time_t) -> time_t; + fn localtime(timep: *time_t) -> *c_tm; + + fn getloadavg(loadavg: *c_double, nelem: c_int) -> c_int; + + fn getutxent() -> *c_utmp; + fn setutxent(); + fn endutxent(); + + fn utmpxname(file: *c_char) -> c_int; +} + +fn main() { + let args = os::args(); + let program = args[0].clone(); + let opts = ~[ + getopts::optflag("v", "version", "output version information and exit"), + getopts::optflag("h", "help", "display this help and exit"), + ]; + let matches = match getopts::getopts(args.tail(), opts) { + Ok(m) => m, + Err(f) => crash!(1, "Invalid options\n{}", f.to_err_msg()) + }; + if matches.opt_present("version") { + println!("uptime 1.0.0"); + return; + } + if matches.opt_present("help") || matches.free.len() > 0 { + println!("Usage:"); + println!(" {0:s} [OPTION]", program); + println!(""); + print(getopts::usage("Print the current time, the length of time the system has been up,\n\ + the number of users on the system, and the average number of jobs\n\ + in the run queue over the last 1, 5 and 15 minutes.", opts)); + return; + } + + print_time(); + print_uptime(); + print_nusers(); + print_loadavg(); +} + +fn print_loadavg() { + let avg: [c_double, ..3] = [0.0, ..3]; + let loads: i32 = unsafe { transmute(getloadavg(avg.as_ptr(), 3)) }; + + if loads == -1 { + print!("\n"); + } + else { + print!("load average: ") + for n in range(0, loads) { + print!("{}{}", avg[n], if n == loads - 1 { "\n" } + else { ", " } ); + } + } +} + +fn print_nusers() { + DEFAULT_FILE.with_c_str(|filename| { + unsafe { + utmpxname(filename); + } + }); + + let mut nusers = 0; + + unsafe { + setutxent(); + + loop { + let line = getutxent(); + + if line == null() { + break; + } + + if (*line).ut_type == USER_PROCESS { + nusers += 1; + } + } + + endutxent(); + } + + if nusers == 1 { + print!("1 user, "); + } else if nusers > 1 { + print!("{} users, ", nusers); + } +} + + +fn print_time() { + let local_time = unsafe { *localtime(&time(null())) }; + + if local_time.tm_hour >= 0 && local_time.tm_min >= 0 && + local_time.tm_sec >= 0 { + print!(" {:02d}:{:02d}:{:02d} ", local_time.tm_hour, + local_time.tm_min, local_time.tm_sec); + } +} + +fn get_uptime() -> int { + let uptime_text = File::open(&Path::new("/proc/uptime")) + .read_to_str().unwrap(); + + return match uptime_text.words().next() { + Some(s) => match from_str(s.replace(".","")) { + Some(n) => n, + None => -1 + }, + None => -1 + }; +} + +fn print_uptime() { + let uptime = get_uptime() / 100; + let updays = uptime / 86400; + let uphours = (uptime - (updays * 86400)) / 3600; + let upmins = (uptime - (updays * 86400) - (uphours * 3600)) / 60; + if updays == 1 { + print!("up {:1d} day, {:2d}:{:02d}, ", updays, uphours, upmins); + } + else if updays > 1 { + print!("up {:1d} days, {:2d}:{:02d}, ", updays, uphours, upmins); + } + else { + print!("up {:2d}:{:02d}, ", uphours, upmins); + } +} diff --git a/users/users.rs b/users/users.rs index 7211f3307..51dd6d0df 100644 --- a/users/users.rs +++ b/users/users.rs @@ -29,88 +29,8 @@ use utmpx::*; #[path = "../common/util.rs"] mod util; -#[cfg(target_os = "linux")] -mod utmpx { - use std::libc; - - pub static DEFAULT_FILE: &'static str = "/var/run/utmp"; - - pub static UT_LINESIZE: uint = 32; - pub static UT_NAMESIZE: uint = 32; - pub static UT_IDSIZE: uint = 4; - pub static UT_HOSTSIZE: uint = 256; - - pub static EMPTY: libc::c_short = 0; - pub static RUN_LVL: libc::c_short = 1; - pub static BOOT_TIME: libc::c_short = 2; - pub static NEW_TIME: libc::c_short = 3; - pub static OLD_TIME: libc::c_short = 4; - pub static INIT_PROCESS: libc::c_short = 5; - pub static LOGIN_PROCESS: libc::c_short = 6; - pub static USER_PROCESS: libc::c_short = 7; - pub static DEAD_PROCESS: libc::c_short = 8; - pub static ACCOUNTING: libc::c_short = 9; - - pub struct c_exit_status { - e_termination: libc::c_short, - e_exit: libc::c_short, - } - - pub struct c_utmp { - ut_type: libc::c_short, - ut_pid: libc::pid_t, - ut_line: [libc::c_char, ..UT_LINESIZE], - ut_id: [libc::c_char, ..UT_IDSIZE], - - ut_user: [libc::c_char, ..UT_NAMESIZE], - ut_host: [libc::c_char, ..UT_HOSTSIZE], - ut_exit: c_exit_status, - ut_session: libc::c_long, - ut_tv: libc::timeval, - - ut_addr_v6: [libc::int32_t, ..4], - __unused: [libc::c_char, ..20], - } -} - -#[cfg(target_os = "macos")] -mod utmpx { - use std::libc; - - pub static DEFAULT_FILE: &'static str = "/var/run/utmpx"; - - pub static UT_LINESIZE: uint = 32; - pub static UT_NAMESIZE: uint = 256; - pub static UT_IDSIZE: uint = 4; - pub static UT_HOSTSIZE: uint = 256; - - pub static EMPTY: libc::c_short = 0; - pub static RUN_LVL: libc::c_short = 1; - pub static BOOT_TIME: libc::c_short = 2; - pub static OLD_TIME: libc::c_short = 3; - pub static NEW_TIME: libc::c_short = 4; - pub static INIT_PROCESS: libc::c_short = 5; - pub static LOGIN_PROCESS: libc::c_short = 6; - pub static USER_PROCESS: libc::c_short = 7; - pub static DEAD_PROCESS: libc::c_short = 8; - pub static ACCOUNTING: libc::c_short = 9; - - pub struct c_exit_status { - e_termination: libc::c_short, - e_exit: libc::c_short, - } - - pub struct c_utmp { - ut_user: [libc::c_char, ..UT_NAMESIZE], - ut_id: [libc::c_char, ..UT_IDSIZE], - ut_line: [libc::c_char, ..UT_LINESIZE], - ut_pid: libc::pid_t, - ut_type: libc::c_short, - ut_tv: libc::timeval, - ut_host: [libc::c_char, ..UT_HOSTSIZE], - __unused: [libc::c_char, ..16] - } -} +#[path = "../common/utmpx.rs"] +mod utmpx; extern { fn getutxent() -> *c_utmp;