Skip to content
Closed
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ feat_common_core = [
"unexpand",
"uniq",
"unlink",
"uptime",
"vdir",
"wc",
"yes",
Expand Down Expand Up @@ -229,7 +230,7 @@ feat_require_unix_core = [
]
# "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support
# * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>
feat_require_unix_utmpx = ["pinky", "uptime", "users", "who"]
feat_require_unix_utmpx = ["pinky", "users", "who"]
# "feat_require_unix_hostid" == set of utilities requiring gethostid in libc (only some unixes provide)
feat_require_unix_hostid = ["hostid"]
# "feat_require_selinux" == set of utilities depending on SELinux.
Expand Down
47 changes: 31 additions & 16 deletions src/uu/uptime/src/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
// spell-checker:ignore getloadavg behaviour loadavg uptime upsecs updays upmins uphours boottime nusers utmpxname gettime clockid couldnt

use chrono::{Local, TimeZone, Utc};
#[cfg(unix)]
use std::ffi::OsString;
use std::io;
use thiserror::Error;
Expand All @@ -15,12 +14,12 @@ use uucore::libc::time_t;
use uucore::translate;
use uucore::uptime::*;

use clap::{Arg, ArgAction, Command, ValueHint, builder::ValueParser};
use clap::{Arg, ArgAction, Command};

use uucore::format_usage;

#[cfg(unix)]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "android")))]
use uucore::utmpx::*;

pub mod options {
Expand Down Expand Up @@ -82,15 +81,26 @@ pub fn uu_app() -> Command {
.help(translate!("uptime-help-since"))
.action(ArgAction::SetTrue),
);

#[cfg(unix)]
cmd.arg(
Arg::new(options::PATH)
.help(translate!("uptime-help-path"))
.action(ArgAction::Set)
.num_args(0..=1)
.value_parser(ValueParser::os_string())
.value_hint(ValueHint::AnyPath),
)
let cmd = {
use clap::{ValueHint, builder::ValueParser};
cmd.arg(
Arg::new(options::PATH)
.help(translate!("uptime-help-path"))
.action(ArgAction::Set)
.num_args(0..=1)
.value_parser(ValueParser::os_string())
.value_hint(ValueHint::AnyPath),
)
};

cmd
}

#[cfg(windows)]
fn uptime_with_file(_: &OsString) -> UResult<()> {
unreachable!("The function should never be called on Windows")
}

#[cfg(unix)]
Expand Down Expand Up @@ -152,7 +162,7 @@ fn uptime_with_file(file_path: &OsString) -> UResult<()> {
print_time();
let user_count;

#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "android")))]
{
let (boot_time, count) = process_utmpx(Some(file_path));
if let Some(time) = boot_time {
Expand All @@ -166,7 +176,7 @@ fn uptime_with_file(file_path: &OsString) -> UResult<()> {
user_count = count;
}

#[cfg(target_os = "openbsd")]
#[cfg(any(target_os = "openbsd", target_os = "redox", target_os = "android"))]
{
let upsecs = get_uptime(None)?;
if upsecs >= 0 {
Expand All @@ -188,12 +198,17 @@ fn uptime_with_file(file_path: &OsString) -> UResult<()> {

fn uptime_since() -> UResult<()> {
#[cfg(unix)]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "android")))]
let uptime = {
let (boot_time, _) = process_utmpx(None);
get_uptime(boot_time)?
};
#[cfg(any(windows, target_os = "openbsd"))]
#[cfg(any(
windows,
target_os = "openbsd",
target_os = "redox",
target_os = "android"
))]
let uptime = get_uptime(None)?;

let since_date = Local
Expand Down Expand Up @@ -223,7 +238,7 @@ fn print_loadavg() {
}

#[cfg(unix)]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "android")))]
fn process_utmpx(file: Option<&OsString>) -> (Option<time_t>, usize) {
let mut nusers = 0;
let mut boot_time = None;
Expand Down
41 changes: 30 additions & 11 deletions src/uucore/src/lib/features/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ pub fn get_uptime(_boot_time: Option<time_t>) -> UResult<i64> {
}
}

// TODO implement functionality
#[cfg(any(target_os = "android", target_os = "redox"))]
pub fn get_uptime(_boot_time: Option<time_t>) -> UResult<i64> {
Err(UptimeError::SystemUptime)?
}

/// Get the system uptime
///
/// # Arguments
Expand All @@ -130,7 +136,7 @@ pub fn get_uptime(_boot_time: Option<time_t>) -> UResult<i64> {
///
/// Returns a UResult with the uptime in seconds if successful, otherwise an UptimeError.
#[cfg(unix)]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "android", target_os = "redox")))]
pub fn get_uptime(boot_time: Option<time_t>) -> UResult<i64> {
use crate::utmpx::Utmpx;
use libc::BOOT_TIME;
Expand Down Expand Up @@ -251,7 +257,7 @@ pub fn get_formatted_uptime(boot_time: Option<time_t>) -> UResult<String> {
///
/// Returns the number of users currently logged in if successful, otherwise 0.
#[cfg(unix)]
#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "openbsd", target_os = "android", target_os = "redox")))]
// see: https://gitlab.com/procps-ng/procps/-/blob/4740a0efa79cade867cfc7b32955fe0f75bf5173/library/uptime.c#L63-L115
pub fn get_nusers() -> usize {
use crate::utmpx::Utmpx;
Expand Down Expand Up @@ -293,6 +299,12 @@ pub fn get_nusers(file: &str) -> usize {
.count()
}

// TODO implement functionality
#[cfg(any(target_os = "android", target_os = "redox"))]
pub fn get_nusers() -> usize {
0
}

/// Get the number of users currently logged in
///
/// # Returns
Expand All @@ -314,8 +326,8 @@ pub fn get_nusers() -> usize {
WTS_CURRENT_SERVER_HANDLE,
0,
1,
&mut session_info_ptr,
&mut session_count,
&raw mut session_info_ptr,
&raw mut session_count,
);
if result == 0 {
return 0;
Expand All @@ -331,27 +343,27 @@ pub fn get_nusers() -> usize {
WTS_CURRENT_SERVER_HANDLE,
session.SessionId,
5,
&mut buffer,
&mut bytes_returned,
&raw mut buffer,
&raw mut bytes_returned,
);
if result == 0 || buffer.is_null() {
continue;
}

let username = if !buffer.is_null() {
let username = if buffer.is_null() {
String::new()
} else {
let cstr = std::ffi::CStr::from_ptr(buffer as *const i8);
cstr.to_string_lossy().to_string()
} else {
String::new()
};
if !username.is_empty() {
num_user += 1;
}

WTSFreeMemory(buffer as _);
WTSFreeMemory(buffer.cast());
}

WTSFreeMemory(session_info_ptr as _);
WTSFreeMemory(session_info_ptr.cast());
}

num_user
Expand Down Expand Up @@ -391,6 +403,7 @@ pub fn get_formatted_nusers() -> String {
/// Returns a UResult with the load average if successful, otherwise an UptimeError.
/// The load average is a tuple of three floating point numbers representing the 1-minute, 5-minute, and 15-minute load averages.
#[cfg(unix)]
#[cfg(not(any(target_os = "android", target_os = "redox")))]
pub fn get_loadavg() -> UResult<(f64, f64, f64)> {
use crate::libc::c_double;
use libc::getloadavg;
Expand All @@ -406,6 +419,12 @@ pub fn get_loadavg() -> UResult<(f64, f64, f64)> {
}
}

// TODO implement functionality
#[cfg(any(target_os = "android", target_os = "redox"))]
pub fn get_loadavg() -> UResult<(f64, f64, f64)> {
Err(UptimeError::SystemLoadavg)?
}

/// Get the system load average
/// Windows does not have an equivalent to the load average on Unix-like systems.
///
Expand Down
26 changes: 17 additions & 9 deletions tests/by-util/test_uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
// spell-checker:ignore bincode serde utmp runlevel testusr testx boottime
#![allow(clippy::cast_possible_wrap, clippy::unreadable_literal)]

#[cfg(not(any(windows, target_os = "openbsd", target_os = "freebsd")))]
use uutests::at_and_ucmd;
use uutests::util::TestScenario;
use uutests::{new_ucmd, util_name};
use uutests::new_ucmd;

use regex::Regex;

Expand All @@ -19,17 +19,21 @@ fn test_invalid_arg() {

#[test]
fn test_uptime() {
new_ucmd!()
.succeeds()
.stdout_contains("load average:")
.stdout_contains(" up ");
let result = new_ucmd!().succeeds();

result.stdout_contains(" up ");

#[cfg(not(windows))]
result.stdout_contains("load average:");
#[cfg(windows)]
result.stdout_does_not_contain("load average:");

// Don't check for users as it doesn't show in some CI
}

/// Checks for files without utmpx records for which boot time cannot be calculated
#[test]
#[cfg(not(any(target_os = "openbsd", target_os = "freebsd")))]
#[cfg(not(any(windows, target_os = "openbsd", target_os = "freebsd")))]
// Disabled for freebsd, since it doesn't use the utmpxname() sys call to change the default utmpx
// file that is accessed using getutxent()
fn test_uptime_for_file_without_utmpx_records() {
Expand All @@ -47,6 +51,7 @@ fn test_uptime_for_file_without_utmpx_records() {
#[test]
#[cfg(all(unix, feature = "cp"))]
fn test_uptime_with_fifo() {
use uutests::{util::TestScenario, util_name};
// This test can go on forever in the CI in some cases, might need aborting
// Sometimes writing to the pipe is broken
let ts = TestScenario::new(util_name!());
Expand All @@ -73,7 +78,7 @@ fn test_uptime_with_fifo() {
}

#[test]
#[cfg(not(target_os = "freebsd"))]
#[cfg(not(any(windows, target_os = "freebsd")))]
fn test_uptime_with_non_existent_file() {
// Disabled for freebsd, since it doesn't use the utmpxname() sys call to change the default utmpx
// file that is accessed using getutxent()
Expand All @@ -87,7 +92,7 @@ fn test_uptime_with_non_existent_file() {
// TODO create a similar test for macos
// This will pass
#[test]
#[cfg(not(any(target_os = "openbsd", target_os = "macos")))]
#[cfg(not(any(windows, target_os = "openbsd", target_os = "macos")))]
#[cfg(not(target_env = "musl"))]
#[cfg_attr(
all(target_arch = "aarch64", target_os = "linux"),
Expand Down Expand Up @@ -234,15 +239,18 @@ fn test_uptime_with_file_containing_valid_boot_time_utmpx_record() {
}

#[test]
#[cfg(not(windows))]
fn test_uptime_with_extra_argument() {
new_ucmd!()
.arg("a")
.arg("b")
.fails()
.stderr_contains("unexpected value 'b'");
}

/// Checks whether uptime displays the correct stderr msg when its called with a directory
#[test]
#[cfg(not(windows))]
fn test_uptime_with_dir() {
let (at, mut ucmd) = at_and_ucmd!();

Expand Down
Loading