From b78f78bedfdcf1df7bbc0a64123e690718521b24 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 6 May 2025 16:05:23 +0200 Subject: [PATCH 1/3] uptime: use clap to handle too many path args --- src/uu/uptime/src/uptime.rs | 33 +++++++-------------------------- tests/by-util/test_uptime.rs | 2 +- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/uu/uptime/src/uptime.rs b/src/uu/uptime/src/uptime.rs index 240a35371..001cf43bc 100644 --- a/src/uu/uptime/src/uptime.rs +++ b/src/uu/uptime/src/uptime.rs @@ -44,14 +44,10 @@ pub enum UptimeError { // io::Error wrapper #[error("couldn't get boot time: {0}")] IoErr(#[from] io::Error), - #[error("couldn't get boot time: Is a directory")] TargetIsDir, - #[error("couldn't get boot time: Illegal seek")] TargetIsFifo, - #[error("extra operand '{0}'")] - ExtraOperandError(String), } impl UError for UptimeError { @@ -70,30 +66,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { #[cfg(unix)] { use std::ffi::OsString; - use uucore::error::set_exit_code; - use uucore::show_error; - let argument = matches.get_many::(options::PATH); + let argument = matches.get_one::(options::PATH); - // Switches to default uptime behaviour if there is no argument - if argument.is_none() { - return default_uptime(&matches); + if let Some(file_path) = argument { + uptime_with_file(file_path) + } else { + default_uptime(&matches) } - let mut arg_iter = argument.unwrap(); - - let file_path = arg_iter.next().unwrap(); - if let Some(path) = arg_iter.next() { - // Uptime doesn't attempt to calculate boot time if there is extra arguments. - // Its a fatal error - show_error!( - "{}", - UptimeError::ExtraOperandError(path.to_owned().into_string().unwrap()) - ); - set_exit_code(1); - return Ok(()); - } - - uptime_with_file(file_path) } } @@ -113,7 +93,8 @@ pub fn uu_app() -> Command { .arg( Arg::new(options::PATH) .help("file to search boot time from") - .action(ArgAction::Append) + .action(ArgAction::Set) + .num_args(0..=1) .value_parser(ValueParser::os_string()) .value_hint(ValueHint::AnyPath), ) diff --git a/tests/by-util/test_uptime.rs b/tests/by-util/test_uptime.rs index 1fd65f9d6..7ec71ceba 100644 --- a/tests/by-util/test_uptime.rs +++ b/tests/by-util/test_uptime.rs @@ -251,7 +251,7 @@ fn test_uptime_with_extra_argument() { .arg("a") .arg("b") .fails() - .stderr_contains("extra operand 'b'"); + .stderr_contains("unexpected value 'b'"); } /// Checks whether uptime displays the correct stderr msg when its called with a directory #[test] From c6b12cfb96954f6965d49efba43bc326bf9e460b Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 6 May 2025 16:47:52 +0200 Subject: [PATCH 2/3] uptime: remove path arg under Windows --- src/uu/uptime/src/uptime.rs | 46 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/uu/uptime/src/uptime.rs b/src/uu/uptime/src/uptime.rs index 001cf43bc..f5b924bc9 100644 --- a/src/uu/uptime/src/uptime.rs +++ b/src/uu/uptime/src/uptime.rs @@ -7,6 +7,8 @@ use chrono::{Local, TimeZone, Utc}; use clap::ArgMatches; +#[cfg(unix)] +use std::ffi::OsString; use std::io; use thiserror::Error; use uucore::error::{UError, UResult}; @@ -60,25 +62,20 @@ impl UError for UptimeError { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; - #[cfg(windows)] - return default_uptime(&matches); - #[cfg(unix)] - { - use std::ffi::OsString; + let file_path = matches.get_one::(options::PATH); + #[cfg(windows)] + let file_path = None; - let argument = matches.get_one::(options::PATH); - - if let Some(file_path) = argument { - uptime_with_file(file_path) - } else { - default_uptime(&matches) - } + if let Some(file_path) = file_path { + uptime_with_file(file_path) + } else { + default_uptime(&matches) } } pub fn uu_app() -> Command { - Command::new(uucore::util_name()) + let cmd = Command::new(uucore::util_name()) .version(uucore::crate_version!()) .about(ABOUT) .override_usage(format_usage(USAGE)) @@ -89,19 +86,20 @@ pub fn uu_app() -> Command { .long(options::SINCE) .help("system up since") .action(ArgAction::SetTrue), - ) - .arg( - Arg::new(options::PATH) - .help("file to search boot time from") - .action(ArgAction::Set) - .num_args(0..=1) - .value_parser(ValueParser::os_string()) - .value_hint(ValueHint::AnyPath), - ) + ); + #[cfg(unix)] + cmd.arg( + Arg::new(options::PATH) + .help("file to search boot time from") + .action(ArgAction::Set) + .num_args(0..=1) + .value_parser(ValueParser::os_string()) + .value_hint(ValueHint::AnyPath), + ) } #[cfg(unix)] -fn uptime_with_file(file_path: &std::ffi::OsString) -> UResult<()> { +fn uptime_with_file(file_path: &OsString) -> UResult<()> { use std::fs; use std::os::unix::fs::FileTypeExt; use uucore::error::set_exit_code; @@ -232,7 +230,7 @@ fn print_loadavg() { #[cfg(unix)] #[cfg(not(target_os = "openbsd"))] -fn process_utmpx(file: Option<&std::ffi::OsString>) -> (Option, usize) { +fn process_utmpx(file: Option<&OsString>) -> (Option, usize) { let mut nusers = 0; let mut boot_time = None; From 26e175757dbd08446dcc8f05f4cab5b48c775274 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 6 May 2025 16:59:23 +0200 Subject: [PATCH 3/3] uptime: extract uptime_since fn --- src/uu/uptime/src/uptime.rs | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/uu/uptime/src/uptime.rs b/src/uu/uptime/src/uptime.rs index f5b924bc9..e001a64a8 100644 --- a/src/uu/uptime/src/uptime.rs +++ b/src/uu/uptime/src/uptime.rs @@ -6,7 +6,6 @@ // spell-checker:ignore getloadavg behaviour loadavg uptime upsecs updays upmins uphours boottime nusers utmpxname gettime clockid use chrono::{Local, TimeZone, Utc}; -use clap::ArgMatches; #[cfg(unix)] use std::ffi::OsString; use std::io; @@ -67,10 +66,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { #[cfg(windows)] let file_path = None; - if let Some(file_path) = file_path { - uptime_with_file(file_path) + if matches.get_flag(options::SINCE) { + uptime_since() + } else if let Some(path) = file_path { + uptime_with_file(path) } else { - default_uptime(&matches) + default_uptime() } } @@ -191,27 +192,29 @@ fn uptime_with_file(file_path: &OsString) -> UResult<()> { Ok(()) } +fn uptime_since() -> UResult<()> { + #[cfg(unix)] + #[cfg(not(target_os = "openbsd"))] + let (boot_time, _) = process_utmpx(None); + + #[cfg(target_os = "openbsd")] + let uptime = get_uptime(None)?; + #[cfg(unix)] + #[cfg(not(target_os = "openbsd"))] + let uptime = get_uptime(boot_time)?; + #[cfg(target_os = "windows")] + let uptime = get_uptime(None)?; + + let initial_date = Local + .timestamp_opt(Utc::now().timestamp() - uptime, 0) + .unwrap(); + println!("{}", initial_date.format("%Y-%m-%d %H:%M:%S")); + + Ok(()) +} + /// Default uptime behaviour i.e. when no file argument is given. -fn default_uptime(matches: &ArgMatches) -> UResult<()> { - if matches.get_flag(options::SINCE) { - #[cfg(unix)] - #[cfg(not(target_os = "openbsd"))] - let (boot_time, _) = process_utmpx(None); - - #[cfg(target_os = "openbsd")] - let uptime = get_uptime(None)?; - #[cfg(unix)] - #[cfg(not(target_os = "openbsd"))] - let uptime = get_uptime(boot_time)?; - #[cfg(target_os = "windows")] - let uptime = get_uptime(None)?; - let initial_date = Local - .timestamp_opt(Utc::now().timestamp() - uptime, 0) - .unwrap(); - println!("{}", initial_date.format("%Y-%m-%d %H:%M:%S")); - return Ok(()); - } - +fn default_uptime() -> UResult<()> { print_time(); print_uptime(None)?; print_nusers(None);