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