On the following platforms: - Linux (that includes glibc-based systems, Alpine Linux, Raspbian), - NetBSD, - Cygwin, - Minix,
the "up" duration is inconsistent after the VM in which the OS is running has been 1. put into sleep / saved / pause mode (terminology depends on the hypervisor), 2. resumed, 3. a date bump has been done in the VM (either automatically or manually). VM sleep and a date adjustment after resume is a common operation, as can be seen - from the fact that it's automatic in VirtualBox machines with the "guest extensions" installed [1], - from the fact that it's automatic in KVM [2], - from the fact that it's automatic in Haiku as guest, - from a remark in the libvirt documentation [3], - from stackoverflow questions such as [4]. After such a VM sleep and a date adjustment, coreutils' "uptime" is inconsistent in three ways: * On Linux, the displayed "up" duration includes active time and hardware suspend time [5], but excludes VM sleep time. Such a figure is not useful for estimating the boot time, since (time now) - (that "up" time) is not the boot time. It is also not useful for estimating when e.g. a file system check would be necessary on file systems without a journal, or how much electricity was consumed — since the hardware suspend time was included. So this figure is useless. * The displayed "up" duration is inconsistent with the boot time displayed by "who -a". (Since the Gnulib module 'readutmp' makes efforts to find the boot time in a way that does not change when the date is adjusted.) * The behaviour is inconsistent among platforms: On platforms which have a /proc/uptime file, the displayed "up" time _excludes_ VM sleep time. On the other platforms it _includes_ VM sleep time. Seen on Linux (Fedora Rawhide, Alpine Linux 3.18, Raspbian), NetBSD 9.3, Cygwin 2.9.0, Minix 3.3. I see this as a bug. Find attached a fix for this bug. I'll provide a NEWS entry afterwards, that summarizes the changes in coreutils + gnulib on the various platforms. Bruno [1] https://docs.oracle.com/en/virtualization/virtualbox/6.1/user/guestadditions.html#4.1.-Introduction-to-Guest-Additions [2] https://bugzilla.redhat.com/show_bug.cgi?id=1115340 [3] https://libvirt.gitlab.io/libvirt-appdev-guide-python/libvirt_application_development_guide_using_python-Guest_Domains-Lifecycle-Save.html [4] https://serverfault.com/questions/334698/ [5] This is indicated by glibc's <bits/time.h>: /* Monotonic system-wide clock that includes time spent in suspension. */ # define CLOCK_BOOTTIME 7 I also verified this on a laptop.
>From ff6e92f38b560ade54df4c94e9d91657f740bb4b Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Sun, 13 Aug 2023 00:57:23 +0200 Subject: [PATCH] uptime: Include VM sleep time in the "up" duration * src/uptime.c: Don't include c-strtod.h. (print_uptime): Don't read /proc/uptime, because the value it provides does not change when a date adjustment occurs. * bootstrap.conf (gnulib_modules): Remove 'uptime'. --- bootstrap.conf | 1 - src/uptime.c | 36 ++++++------------------------------ 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index 6ce27d5dd..d758a9ff6 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -285,7 +285,6 @@ gnulib_modules=" unlocked-io unsetenv update-copyright - uptime useless-if-before-free userspec utimecmp diff --git a/src/uptime.c b/src/uptime.c index c29798d32..0cc1dedba 100644 --- a/src/uptime.c +++ b/src/uptime.c @@ -22,7 +22,6 @@ #include <sys/types.h> #include "system.h" -#include "c-strtod.h" #include "long-options.h" #include "quote.h" #include "readutmp.h" @@ -42,33 +41,13 @@ print_uptime (idx_t n, struct gl_utmp const *this) idx_t entries = 0; time_t boot_time = 0; time_t time_now; - time_t uptime = 0; + time_t uptime; intmax_t updays; int uphours; int upmins; struct tm *tmn; double avg[3]; int loads; -#ifdef HAVE_PROC_UPTIME - FILE *fp; - - fp = fopen ("/proc/uptime", "r"); - if (fp != nullptr) - { - char buf[BUFSIZ]; - char *b = fgets (buf, BUFSIZ, fp); - if (b == buf) - { - char *end_ptr; - double upsecs = c_strtod (buf, &end_ptr); - if (buf != end_ptr) - uptime = (0 <= upsecs && upsecs < TYPE_MAXIMUM (time_t) - ? upsecs : -1); - } - - fclose (fp); - } -#endif /* HAVE_PROC_UPTIME */ /* Loop through all the utmp entries we just read and count up the valid ones, also in the process possibly gleaning boottime. */ @@ -79,16 +58,13 @@ print_uptime (idx_t n, struct gl_utmp const *this) boot_time = this->ut_ts.tv_sec; ++this; } + /* The gnulib module 'readutmp' is supposed to provide a BOOT_TIME entry + on all platforms. */ + if (boot_time == 0) + error (EXIT_FAILURE, errno, _("couldn't get boot time")); time_now = time (nullptr); -#if defined HAVE_PROC_UPTIME - if (uptime == 0) -#endif - { - if (boot_time == 0) - error (EXIT_FAILURE, errno, _("couldn't get boot time")); - uptime = time_now - boot_time; - } + uptime = time_now - boot_time; updays = uptime / 86400; uphours = uptime % 86400 / 3600; upmins = uptime % 86400 % 3600 / 60; -- 2.34.1