From 1ea34cbf6a235f2436a3265ab9ded6f04748051e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 15 Aug 2023 14:00:54 -0700 Subject: [PATCH] uptime: be more generous about read_utmp failure * src/uptime.c (print_uptime): Check for overflow when computing uptime. Use C99-style decl after statements. Do not let an idx_t value go negative. (print_uptime, uptime): Be more generous about read_utmp failures, or when read_utmp does not report the boot time. Instead of failing, warn but keep going, printing the information that we did get, and then exit with nonzero status. (print_uptime): Return the desired exit status. Caller changed. --- src/uptime.c | 77 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/src/uptime.c b/src/uptime.c index 813d29430a..928829dfc1 100644 --- a/src/uptime.c +++ b/src/uptime.c @@ -17,9 +17,11 @@ /* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu. */ #include -#include +#include +#include #include + #include "system.h" #if HAVE_SYSCTL && HAVE_SYS_SYSCTL_H && ! defined __GLIBC__ @@ -43,19 +45,11 @@ proper_name ("David MacKenzie"), \ proper_name ("Kaveh Ghazi") -static void -print_uptime (idx_t n, struct gl_utmp const *this) +static int +print_uptime (idx_t n, struct gl_utmp const *utmp_buf) { - idx_t entries = 0; + int status = EXIT_SUCCESS; time_t boot_time = 0; - time_t time_now; - time_t uptime; - intmax_t updays; - int uphours; - int upmins; - struct tm *tmn; - double avg[3]; - int loads; #if HAVE_SYSCTL && ! defined __GLIBC__ \ && defined CTL_KERN && defined KERN_BOOTTIME @@ -81,35 +75,47 @@ print_uptime (idx_t n, struct gl_utmp const *this) /* Loop through all the utmp entries we just read and count up the valid ones, also in the process possibly gleaning boottime. */ - while (n--) + idx_t entries = 0; + for (idx_t i = 0; i < n; i++) { + struct gl_utmp const *this = &utmp_buf[i]; entries += IS_USER_PROCESS (this); if (UT_TYPE_BOOT_TIME (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); - uptime = time_now - boot_time; - updays = uptime / 86400; - uphours = uptime % 86400 / 3600; - upmins = uptime % 86400 % 3600 / 60; - tmn = localtime (&time_now); + { + error (0, errno, _("couldn't get boot time")); + status = EXIT_FAILURE; + } + + time_t time_now = time (nullptr); + struct tm *tmn = time_now == (time_t) -1 ? nullptr : localtime (&time_now); /* procps' version of uptime also prints the seconds field, but previous versions of coreutils don't. */ if (tmn) /* TRANSLATORS: This prints the current clock time. */ fprintftime (stdout, _(" %H:%M:%S "), tmn, 0, 0); else - printf (_(" ??:???? ")); - if (uptime == (time_t) -1) - printf (_("up ???? days ??:??, ")); + { + printf (_(" ??:???? ")); + status = EXIT_FAILURE; + } + + intmax_t uptime; + if (time_now == (time_t) -1 || boot_time == 0 + || ckd_sub (&uptime, time_now, boot_time) || uptime < 0) + { + printf (_("up ???? days ??:??, ")); + status = EXIT_FAILURE; + } else { + intmax_t updays = uptime / 86400; + int uphours = uptime % 86400 / 3600; + int upmins = uptime % 86400 % 3600 / 60; if (0 < updays) printf (ngettext ("up %"PRIdMAX" day %2d:%02d, ", "up %"PRIdMAX" days %2d:%02d, ", @@ -118,10 +124,12 @@ print_uptime (idx_t n, struct gl_utmp const *this) else printf (_("up %2d:%02d, "), uphours, upmins); } + printf (ngettext ("%td user", "%td users", select_plural (entries)), entries); - loads = getloadavg (avg, 3); + double avg[3]; + int loads = getloadavg (avg, 3); if (loads == -1) putchar ('\n'); @@ -136,6 +144,8 @@ print_uptime (idx_t n, struct gl_utmp const *this) if (loads > 0) putchar ('\n'); } + + return status; } /* Display the system uptime and the number of users on the system, @@ -147,12 +157,17 @@ uptime (char const *filename, int options) { idx_t n_users; struct gl_utmp *utmp_buf; - if (read_utmp (filename, &n_users, &utmp_buf, options) != 0) - error (EXIT_FAILURE, errno, "%s", quotef (filename)); - - print_uptime (n_users, utmp_buf); + int read_utmp_status = (read_utmp (filename, &n_users, &utmp_buf, options) < 0 + ? EXIT_FAILURE : EXIT_SUCCESS); + if (read_utmp_status != EXIT_SUCCESS) + { + error (0, errno, "%s", quotef (filename)); + n_users = 0; + utmp_buf = nullptr; + } - exit (EXIT_SUCCESS); + int print_uptime_status = print_uptime (n_users, utmp_buf); + exit (MAX (read_utmp_status, print_uptime_status)); } void -- 2.47.2