Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 46 additions & 64 deletions src/uu/uptime/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
// 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;
use thiserror::Error;
use uucore::error::{UError, UResult};
Expand Down Expand Up @@ -44,14 +45,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 {
Expand All @@ -64,41 +61,22 @@ 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;
use uucore::error::set_exit_code;
use uucore::show_error;

let argument = matches.get_many::<OsString>(options::PATH);

// Switches to default uptime behaviour if there is no argument
if argument.is_none() {
return 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(());
}
let file_path = matches.get_one::<OsString>(options::PATH);
#[cfg(windows)]
let file_path = None;

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()
}
}

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))
Expand All @@ -109,18 +87,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::Append)
.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;
Expand Down Expand Up @@ -212,27 +192,29 @@ fn uptime_with_file(file_path: &std::ffi::OsString) -> UResult<()> {
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 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() -> UResult<()> {
print_time();
print_uptime(None)?;
print_nusers(None);
Expand All @@ -251,7 +233,7 @@ fn print_loadavg() {

#[cfg(unix)]
#[cfg(not(target_os = "openbsd"))]
fn process_utmpx(file: Option<&std::ffi::OsString>) -> (Option<time_t>, usize) {
fn process_utmpx(file: Option<&OsString>) -> (Option<time_t>, usize) {
let mut nusers = 0;
let mut boot_time = None;

Expand Down
2 changes: 1 addition & 1 deletion tests/by-util/test_uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Loading