From 8c6d72291638bfac7a33c3bdd708a07fb4f74d1e Mon Sep 17 00:00:00 2001 From: Laurent Cheylus Date: Sat, 9 Dec 2023 18:39:10 +0100 Subject: [PATCH] uptime: tool unsupported on OpenBSD - utmpx not supported on OpenBSD - add src/uu/uptime/src/platform directory and platform/mod.rs for conditional compilation according to target_os - platform/openbsd.rs: implementation on OpenBSD (unsupported tool) - platform/unix.rs: implementation on other OS - src/uu/uptime/src/uptime.rs: use platform module for uucore::main function --- src/uu/uptime/src/platform/mod.rs | 14 +++ src/uu/uptime/src/platform/openbsd.rs | 17 +++ src/uu/uptime/src/platform/unix.rs | 161 ++++++++++++++++++++++++++ src/uu/uptime/src/uptime.rs | 152 +----------------------- 4 files changed, 194 insertions(+), 150 deletions(-) create mode 100644 src/uu/uptime/src/platform/mod.rs create mode 100644 src/uu/uptime/src/platform/openbsd.rs create mode 100644 src/uu/uptime/src/platform/unix.rs diff --git a/src/uu/uptime/src/platform/mod.rs b/src/uu/uptime/src/platform/mod.rs new file mode 100644 index 000000000..e0e87dca1 --- /dev/null +++ b/src/uu/uptime/src/platform/mod.rs @@ -0,0 +1,14 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +#[cfg(not(target_os = "openbsd"))] +mod unix; +#[cfg(not(target_os = "openbsd"))] +pub use self::unix::*; + +#[cfg(target_os = "openbsd")] +mod openbsd; +#[cfg(target_os = "openbsd")] +pub use self::openbsd::*; diff --git a/src/uu/uptime/src/platform/openbsd.rs b/src/uu/uptime/src/platform/openbsd.rs new file mode 100644 index 000000000..7e6970c1f --- /dev/null +++ b/src/uu/uptime/src/platform/openbsd.rs @@ -0,0 +1,17 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// Specific implementation for OpenBSD: tool unsupported (utmpx not supported) + +use crate::uu_app; + +use uucore::error::UResult; + +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let _matches = uu_app().try_get_matches_from(args)?; + + println!("unsupported command on OpenBSD"); + Ok(()) +} diff --git a/src/uu/uptime/src/platform/unix.rs b/src/uu/uptime/src/platform/unix.rs new file mode 100644 index 000000000..df3e5e653 --- /dev/null +++ b/src/uu/uptime/src/platform/unix.rs @@ -0,0 +1,161 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// spell-checker:ignore (ToDO) getloadavg upsecs updays nusers loadavg boottime uphours upmins + +use crate::options; +use crate::uu_app; + +use chrono::{Local, TimeZone, Utc}; + +use uucore::libc::time_t; + +use uucore::error::{UResult, USimpleError}; + +#[cfg(unix)] +use uucore::libc::getloadavg; + +#[cfg(windows)] +extern "C" { + fn GetTickCount() -> uucore::libc::uint32_t; +} + +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let matches = uu_app().try_get_matches_from(args)?; + + let (boot_time, user_count) = process_utmpx(); + let uptime = get_uptime(boot_time); + if uptime < 0 { + Err(USimpleError::new(1, "could not retrieve system uptime")) + } else { + if matches.get_flag(options::SINCE) { + let initial_date = Local + .timestamp_opt(Utc::now().timestamp() - uptime, 0) + .unwrap(); + println!("{}", initial_date.format("%Y-%m-%d %H:%M:%S")); + return Ok(()); + } + + print_time(); + let upsecs = uptime; + print_uptime(upsecs); + print_nusers(user_count); + print_loadavg(); + + Ok(()) + } +} + +#[cfg(unix)] +fn print_loadavg() { + use uucore::libc::c_double; + + let mut avg: [c_double; 3] = [0.0; 3]; + let loads: i32 = unsafe { getloadavg(avg.as_mut_ptr(), 3) }; + + if loads == -1 { + println!(); + } else { + print!("load average: "); + for n in 0..loads { + print!( + "{:.2}{}", + avg[n as usize], + if n == loads - 1 { "\n" } else { ", " } + ); + } + } +} + +#[cfg(windows)] +fn print_loadavg() { + // XXX: currently this is a noop as Windows does not seem to have anything comparable to + // getloadavg() +} + +#[cfg(unix)] +fn process_utmpx() -> (Option, usize) { + use uucore::utmpx::*; + + let mut nusers = 0; + let mut boot_time = None; + + for line in Utmpx::iter_all_records() { + match line.record_type() { + USER_PROCESS => nusers += 1, + BOOT_TIME => { + let dt = line.login_time(); + if dt.unix_timestamp() > 0 { + boot_time = Some(dt.unix_timestamp() as time_t); + } + } + _ => continue, + } + } + (boot_time, nusers) +} + +#[cfg(windows)] +fn process_utmpx() -> (Option, usize) { + (None, 0) // TODO: change 0 to number of users +} + +fn print_nusers(nusers: usize) { + match nusers.cmp(&1) { + std::cmp::Ordering::Equal => print!("1 user, "), + std::cmp::Ordering::Greater => print!("{nusers} users, "), + _ => {} + }; +} + +fn print_time() { + let local_time = Local::now().time(); + + print!(" {} ", local_time.format("%H:%M:%S")); +} + +#[cfg(unix)] +fn get_uptime(boot_time: Option) -> i64 { + use std::fs::File; + use std::io::Read; + + let mut proc_uptime_s = String::new(); + + let proc_uptime = File::open("/proc/uptime") + .ok() + .and_then(|mut f| f.read_to_string(&mut proc_uptime_s).ok()) + .and_then(|_| proc_uptime_s.split_whitespace().next()) + .and_then(|s| s.split('.').next().unwrap_or("0").parse().ok()); + + proc_uptime.unwrap_or_else(|| match boot_time { + Some(t) => { + let now = Local::now().timestamp(); + #[cfg(target_pointer_width = "64")] + let boottime: i64 = t; + #[cfg(not(target_pointer_width = "64"))] + let boottime: i64 = t.into(); + now - boottime + } + None => -1, + }) +} + +#[cfg(windows)] +fn get_uptime(_boot_time: Option) -> i64 { + unsafe { GetTickCount() as i64 } +} + +fn print_uptime(upsecs: i64) { + let updays = upsecs / 86400; + let uphours = (upsecs - (updays * 86400)) / 3600; + let upmins = (upsecs - (updays * 86400) - (uphours * 3600)) / 60; + match updays.cmp(&1) { + std::cmp::Ordering::Equal => print!("up {updays:1} day, {uphours:2}:{upmins:02}, "), + std::cmp::Ordering::Greater => { + print!("up {updays:1} days, {uphours:2}:{upmins:02}, "); + } + _ => print!("up {uphours:2}:{upmins:02}, "), + }; +} diff --git a/src/uu/uptime/src/uptime.rs b/src/uu/uptime/src/uptime.rs index 778fbc920..196ae60ba 100644 --- a/src/uu/uptime/src/uptime.rs +++ b/src/uu/uptime/src/uptime.rs @@ -3,15 +3,11 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) getloadavg upsecs updays nusers loadavg boottime uphours upmins - -use chrono::{Local, TimeZone, Utc}; use clap::{crate_version, Arg, ArgAction, Command}; -use uucore::libc::time_t; use uucore::{format_usage, help_about, help_usage}; -use uucore::error::{UResult, USimpleError}; +mod platform; const ABOUT: &str = help_about!("uptime.md"); const USAGE: &str = help_usage!("uptime.md"); @@ -19,40 +15,8 @@ pub mod options { pub static SINCE: &str = "since"; } -#[cfg(unix)] -use uucore::libc::getloadavg; - -#[cfg(windows)] -extern "C" { - fn GetTickCount() -> uucore::libc::uint32_t; -} - #[uucore::main] -pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let matches = uu_app().try_get_matches_from(args)?; - - let (boot_time, user_count) = process_utmpx(); - let uptime = get_uptime(boot_time); - if uptime < 0 { - Err(USimpleError::new(1, "could not retrieve system uptime")) - } else { - if matches.get_flag(options::SINCE) { - let initial_date = Local - .timestamp_opt(Utc::now().timestamp() - uptime, 0) - .unwrap(); - println!("{}", initial_date.format("%Y-%m-%d %H:%M:%S")); - return Ok(()); - } - - print_time(); - let upsecs = uptime; - print_uptime(upsecs); - print_nusers(user_count); - print_loadavg(); - - Ok(()) - } -} +use platform::uumain; pub fn uu_app() -> Command { Command::new(uucore::util_name()) @@ -68,115 +32,3 @@ pub fn uu_app() -> Command { .action(ArgAction::SetTrue), ) } - -#[cfg(unix)] -fn print_loadavg() { - use uucore::libc::c_double; - - let mut avg: [c_double; 3] = [0.0; 3]; - let loads: i32 = unsafe { getloadavg(avg.as_mut_ptr(), 3) }; - - if loads == -1 { - println!(); - } else { - print!("load average: "); - for n in 0..loads { - print!( - "{:.2}{}", - avg[n as usize], - if n == loads - 1 { "\n" } else { ", " } - ); - } - } -} - -#[cfg(windows)] -fn print_loadavg() { - // XXX: currently this is a noop as Windows does not seem to have anything comparable to - // getloadavg() -} - -#[cfg(unix)] -fn process_utmpx() -> (Option, usize) { - use uucore::utmpx::*; - - let mut nusers = 0; - let mut boot_time = None; - - for line in Utmpx::iter_all_records() { - match line.record_type() { - USER_PROCESS => nusers += 1, - BOOT_TIME => { - let dt = line.login_time(); - if dt.unix_timestamp() > 0 { - boot_time = Some(dt.unix_timestamp() as time_t); - } - } - _ => continue, - } - } - (boot_time, nusers) -} - -#[cfg(windows)] -fn process_utmpx() -> (Option, usize) { - (None, 0) // TODO: change 0 to number of users -} - -fn print_nusers(nusers: usize) { - match nusers.cmp(&1) { - std::cmp::Ordering::Equal => print!("1 user, "), - std::cmp::Ordering::Greater => print!("{nusers} users, "), - _ => {} - }; -} - -fn print_time() { - let local_time = Local::now().time(); - - print!(" {} ", local_time.format("%H:%M:%S")); -} - -#[cfg(unix)] -fn get_uptime(boot_time: Option) -> i64 { - use std::fs::File; - use std::io::Read; - - let mut proc_uptime_s = String::new(); - - let proc_uptime = File::open("/proc/uptime") - .ok() - .and_then(|mut f| f.read_to_string(&mut proc_uptime_s).ok()) - .and_then(|_| proc_uptime_s.split_whitespace().next()) - .and_then(|s| s.split('.').next().unwrap_or("0").parse().ok()); - - proc_uptime.unwrap_or_else(|| match boot_time { - Some(t) => { - let now = Local::now().timestamp(); - #[cfg(target_pointer_width = "64")] - let boottime: i64 = t; - #[cfg(not(target_pointer_width = "64"))] - let boottime: i64 = t.into(); - now - boottime - } - None => -1, - }) -} - -#[cfg(windows)] -fn get_uptime(_boot_time: Option) -> i64 { - unsafe { GetTickCount() as i64 } -} - -fn print_uptime(upsecs: i64) { - let updays = upsecs / 86400; - let uphours = (upsecs - (updays * 86400)) / 3600; - let upmins = (upsecs - (updays * 86400) - (uphours * 3600)) / 60; - match updays.cmp(&1) { - std::cmp::Ordering::Equal => print!("up {updays:1} day, {uphours:2}:{upmins:02}, "), - std::cmp::Ordering::Greater => { - print!("up {updays:1} days, {uphours:2}:{upmins:02}, "); - } - _ => print!("up {uphours:2}:{upmins:02}, "), - }; -}