1 /* GNU's uptime.
2    Copyright (C) 1992-2023 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu.  */
18 
19 #include <config.h>
20 
21 #include <stdckdint.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 
25 #include "system.h"
26 
27 #include "long-options.h"
28 #include "quote.h"
29 #include "readutmp.h"
30 #include "fprintftime.h"
31 
32 /* The official name of this program (e.g., no 'g' prefix).  */
33 #define PROGRAM_NAME "uptime"
34 
35 #define AUTHORS \
36   proper_name ("Joseph Arceneaux"), \
37   proper_name ("David MacKenzie"), \
38   proper_name ("Kaveh Ghazi")
39 
40 static int
print_uptime(idx_t n,struct gl_utmp const * utmp_buf)41 print_uptime (idx_t n, struct gl_utmp const *utmp_buf)
42 {
43   int status = EXIT_SUCCESS;
44   time_t boot_time = 0;
45 
46   /* Loop through all the utmp entries we just read and count up the valid
47      ones, also in the process possibly gleaning boottime. */
48   idx_t entries = 0;
49   for (idx_t i = 0; i < n; i++)
50     {
51       struct gl_utmp const *this = &utmp_buf[i];
52       entries += IS_USER_PROCESS (this);
53       if (UT_TYPE_BOOT_TIME (this))
54         boot_time = this->ut_ts.tv_sec;
55     }
56   /* The gnulib module 'readutmp' is supposed to provide a BOOT_TIME entry
57      on all platforms.  */
58   if (boot_time == 0)
59     {
60       error (0, errno, _("couldn't get boot time"));
61       status = EXIT_FAILURE;
62     }
63 
64   time_t time_now = time (nullptr);
65   struct tm *tmn = time_now == (time_t) -1 ? nullptr : localtime (&time_now);
66   /* procps' version of uptime also prints the seconds field, but
67      previous versions of coreutils don't. */
68   if (tmn)
69     /* TRANSLATORS: This prints the current clock time. */
70     fprintftime (stdout, _(" %H:%M:%S  "), tmn, 0, 0);
71   else
72     {
73       printf (_(" ??:????  "));
74       status = EXIT_FAILURE;
75     }
76 
77   intmax_t uptime;
78   if (time_now == (time_t) -1 || boot_time == 0
79       || ckd_sub (&uptime, time_now, boot_time) || uptime < 0)
80     {
81       printf (_("up ???? days ??:??,  "));
82       status = EXIT_FAILURE;
83     }
84   else
85     {
86       intmax_t updays = uptime / 86400;
87       int uphours = uptime % 86400 / 3600;
88       int upmins = uptime % 86400 % 3600 / 60;
89       if (0 < updays)
90         printf (ngettext ("up %jd day %2d:%02d,  ",
91                           "up %jd days %2d:%02d,  ",
92                           select_plural (updays)),
93                 updays, uphours, upmins);
94       else
95         printf (_("up  %2d:%02d,  "), uphours, upmins);
96     }
97 
98   printf (ngettext ("%td user", "%td users", select_plural (entries)),
99           entries);
100 
101   double avg[3];
102   int loads = getloadavg (avg, 3);
103 
104   if (loads == -1)
105     putchar ('\n');
106   else
107     {
108       if (loads > 0)
109         printf (_(",  load average: %.2f"), avg[0]);
110       if (loads > 1)
111         printf (", %.2f", avg[1]);
112       if (loads > 2)
113         printf (", %.2f", avg[2]);
114       if (loads > 0)
115         putchar ('\n');
116     }
117 
118   return status;
119 }
120 
121 /* Display the system uptime and the number of users on the system,
122    according to utmp file FILENAME.  Use read_utmp OPTIONS to read the
123    utmp file.  */
124 
125 static _Noreturn void
uptime(char const * filename,int options)126 uptime (char const *filename, int options)
127 {
128   idx_t n_users;
129   struct gl_utmp *utmp_buf;
130   int read_utmp_status = (read_utmp (filename, &n_users, &utmp_buf, options) < 0
131                           ? EXIT_FAILURE : EXIT_SUCCESS);
132   if (read_utmp_status != EXIT_SUCCESS)
133     {
134       error (0, errno, "%s", quotef (filename));
135       n_users = 0;
136       utmp_buf = nullptr;
137     }
138 
139   int print_uptime_status = print_uptime (n_users, utmp_buf);
140   exit (MAX (read_utmp_status, print_uptime_status));
141 }
142 
143 void
usage(int status)144 usage (int status)
145 {
146   if (status != EXIT_SUCCESS)
147     emit_try_help ();
148   else
149     {
150       printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
151       printf (_("\
152 Print the current time, the length of time the system has been up,\n\
153 the number of users on the system, and the average number of jobs\n\
154 in the run queue over the last 1, 5 and 15 minutes."));
155 #ifdef __linux__
156       /* It would be better to introduce a configure test for this,
157          but such a test is hard to write.  For the moment then, we
158          have a hack which depends on the preprocessor used at compile
159          time to tell us what the running kernel is.  Ugh.  */
160       printf (_("  \
161 Processes in\n\
162 an uninterruptible sleep state also contribute to the load average.\n"));
163 #else
164       printf (_("\n"));
165 #endif
166       printf (_("\
167 If FILE is not specified, use %s.  %s as FILE is common.\n\
168 \n"),
169               UTMP_FILE, WTMP_FILE);
170       fputs (HELP_OPTION_DESCRIPTION, stdout);
171       fputs (VERSION_OPTION_DESCRIPTION, stdout);
172       emit_ancillary_info (PROGRAM_NAME);
173     }
174   exit (status);
175 }
176 
177 int
main(int argc,char ** argv)178 main (int argc, char **argv)
179 {
180   initialize_main (&argc, &argv);
181   set_program_name (argv[0]);
182   setlocale (LC_ALL, "");
183   bindtextdomain (PACKAGE, LOCALEDIR);
184   textdomain (PACKAGE);
185 
186   atexit (close_stdout);
187 
188   parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
189                                    Version, true, usage, AUTHORS,
190                                    (char const *) nullptr);
191 
192   switch (argc - optind)
193     {
194     case 0:			/* uptime */
195       uptime (UTMP_FILE, READ_UTMP_CHECK_PIDS);
196       break;
197 
198     case 1:			/* uptime <utmp file> */
199       uptime (argv[optind], 0);
200       break;
201 
202     default:			/* lose */
203       error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
204       usage (EXIT_FAILURE);
205     }
206 }
207