]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedatectl.c
timedatectl: update output format of status command
[thirdparty/systemd.git] / src / timedate / timedatectl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
6d0274f1
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
2f6a5907 6 Copyright 2013 Kay Sievers
6d0274f1
LP
7***/
8
6d0274f1 9#include <getopt.h>
a9cdc94f 10#include <locale.h>
3f6fd1ba
LP
11#include <stdbool.h>
12#include <stdlib.h>
6d0274f1 13
a281d9c7 14#include "sd-bus.h"
3f6fd1ba 15
a281d9c7 16#include "bus-error.h"
3f6fd1ba
LP
17#include "bus-util.h"
18#include "pager.h"
6bedfcbb 19#include "parse-util.h"
6d0274f1 20#include "spawn-polkit-agent.h"
6d0274f1 21#include "strv.h"
288a74cc 22#include "terminal-util.h"
3f6fd1ba 23#include "util.h"
be90a886 24#include "verbs.h"
6d0274f1 25
6d0274f1 26static bool arg_no_pager = false;
6d0274f1 27static bool arg_ask_password = true;
e1636421 28static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 29static char *arg_host = NULL;
e1636421 30static bool arg_adjust_system_clock = false;
6d0274f1 31
6d0274f1 32typedef struct StatusInfo {
2f6a5907 33 usec_t time;
f37f8a61 34 const char *timezone;
2f6a5907
KS
35
36 usec_t rtc_time;
f37f8a61 37 bool rtc_local;
2f6a5907 38
f37f8a61 39 bool ntp_capable;
f567f086 40 bool ntp_active;
f37f8a61 41 bool ntp_synced;
6d0274f1
LP
42} StatusInfo;
43
ffc06c35 44static void print_status_info(const StatusInfo *i) {
f567f086
YW
45 const char *old_tz = NULL, *tz;
46 bool have_time = false;
14ce0c25 47 char a[LINE_MAX];
6d0274f1
LP
48 struct tm tm;
49 time_t sec;
14ce0c25 50 size_t n;
f567f086 51 int r;
6d0274f1 52
59965986
LP
53 assert(i);
54
d95a74ed
LP
55 /* Save the old $TZ */
56 tz = getenv("TZ");
57 if (tz)
58 old_tz = strdupa(tz);
2311eb2f 59
d95a74ed 60 /* Set the new $TZ */
bdeb9e60 61 if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0)
d95a74ed
LP
62 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
63 else
64 tzset();
3e5e74d5 65
9ff09bcb
SL
66 if (i->time != 0) {
67 sec = (time_t) (i->time / USEC_PER_SEC);
68 have_time = true;
d95a74ed 69 } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
9ff09bcb
SL
70 sec = time(NULL);
71 have_time = true;
72 } else
d95a74ed 73 log_warning("Could not get time from timedated and not operating locally, ignoring.");
6d0274f1 74
9ff09bcb 75 if (have_time) {
14ce0c25 76 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
f567f086 77 printf(" Local time: %s\n", n > 0 ? a : "n/a");
5ffa8c81 78
14ce0c25 79 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
f567f086 80 printf(" Universal time: %s\n", n > 0 ? a : "n/a");
9ff09bcb 81 } else {
f567f086
YW
82 printf(" Local time: %s\n", "n/a");
83 printf(" Universal time: %s\n", "n/a");
9ff09bcb 84 }
6d0274f1 85
2f6a5907
KS
86 if (i->rtc_time > 0) {
87 time_t rtc_sec;
6d0274f1 88
d95a74ed 89 rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
14ce0c25 90 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
f567f086 91 printf(" RTC time: %s\n", n > 0 ? a : "n/a");
2f6a5907 92 } else
f567f086 93 printf(" RTC time: %s\n", "n/a");
6d0274f1 94
5ffa8c81 95 if (have_time)
14ce0c25 96 n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
2667cc25 97
d95a74ed
LP
98 /* Restore the $TZ */
99 if (old_tz)
100 r = setenv("TZ", old_tz, true);
101 else
102 r = unsetenv("TZ");
103 if (r < 0)
104 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
105 else
106 tzset();
107
f567f086
YW
108 printf(" Time zone: %s (%s)\n"
109 "System clock synchronized: %s\n"
110 " NTP service: %s\n"
111 " RTC in local TZ: %s\n",
14ce0c25 112 strna(i->timezone), have_time && n > 0 ? a : "n/a",
2f6a5907 113 yes_no(i->ntp_synced),
f567f086 114 i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
2f6a5907 115 yes_no(i->rtc_local));
6d0274f1 116
2f6a5907 117 if (i->rtc_local)
54f8c958
LP
118 printf("\n%s"
119 "Warning: The system is configured to read the RTC time in the local time zone.\n"
87ac8d99 120 " This mode cannot be fully supported. It will create various problems\n"
54f8c958
LP
121 " with time zone changes and daylight saving time adjustments. The RTC\n"
122 " time is never updated, it relies on external facilities to maintain it.\n"
123 " If at all possible, use RTC in UTC by calling\n"
124 " 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal());
6d0274f1
LP
125}
126
be90a886 127static int show_status(int argc, char **argv, void *userdata) {
f37f8a61 128 StatusInfo info = {};
9f6eb1cd 129 static const struct bus_properties_map map[] = {
f567f086
YW
130 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
131 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
132 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_active) },
9f6eb1cd 133 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
f567f086
YW
134 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
135 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
136 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
ffc06c35
KS
137 {}
138 };
f9e0eefc
LP
139
140 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 141 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
be90a886 142 sd_bus *bus = userdata;
ffc06c35 143 int r;
6d0274f1 144
a281d9c7 145 assert(bus);
6d0274f1 146
ffc06c35
KS
147 r = bus_map_all_properties(bus,
148 "org.freedesktop.timedate1",
149 "/org/freedesktop/timedate1",
9f6eb1cd 150 map,
a7e4861c 151 BUS_MAP_BOOLEAN_AS_BOOL,
f9e0eefc 152 &error,
f37f8a61 153 &m,
9f6eb1cd 154 &info);
e7e55dbd 155 if (r < 0)
f9e0eefc 156 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
6d0274f1
LP
157
158 print_status_info(&info);
ffc06c35 159
ffc06c35 160 return r;
6d0274f1
LP
161}
162
be90a886 163static int set_time(int argc, char **argv, void *userdata) {
4afd3348 164 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a281d9c7 165 bool relative = false, interactive = arg_ask_password;
be90a886 166 sd_bus *bus = userdata;
6d0274f1 167 usec_t t;
6d0274f1
LP
168 int r;
169
8a4b13c5 170 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 171
be90a886
YW
172 r = parse_timestamp(argv[1], &t);
173 if (r < 0)
174 return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]);
6d0274f1 175
a281d9c7
TA
176 r = sd_bus_call_method(bus,
177 "org.freedesktop.timedate1",
178 "/org/freedesktop/timedate1",
179 "org.freedesktop.timedate1",
180 "SetTime",
181 &error,
182 NULL,
be90a886 183 "xbb", (int64_t) t, relative, interactive);
a281d9c7 184 if (r < 0)
be90a886 185 log_error("Failed to set time: %s", bus_error_message(&error, r));
a281d9c7
TA
186
187 return r;
6d0274f1
LP
188}
189
be90a886 190static int set_timezone(int argc, char **argv, void *userdata) {
4afd3348 191 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
be90a886 192 sd_bus *bus = userdata;
a281d9c7 193 int r;
6d0274f1 194
8a4b13c5 195 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 196
a281d9c7
TA
197 r = sd_bus_call_method(bus,
198 "org.freedesktop.timedate1",
199 "/org/freedesktop/timedate1",
200 "org.freedesktop.timedate1",
201 "SetTimezone",
202 &error,
203 NULL,
be90a886 204 "sb", argv[1], arg_ask_password);
a281d9c7 205 if (r < 0)
be90a886 206 log_error("Failed to set time zone: %s", bus_error_message(&error, r));
a281d9c7
TA
207
208 return r;
6d0274f1
LP
209}
210
be90a886 211static int set_local_rtc(int argc, char **argv, void *userdata) {
4afd3348 212 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
be90a886 213 sd_bus *bus = userdata;
e5609878 214 int r, b;
6d0274f1 215
8a4b13c5 216 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 217
be90a886
YW
218 b = parse_boolean(argv[1]);
219 if (b < 0)
220 return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]);
6d0274f1 221
a281d9c7
TA
222 r = sd_bus_call_method(bus,
223 "org.freedesktop.timedate1",
224 "/org/freedesktop/timedate1",
225 "org.freedesktop.timedate1",
226 "SetLocalRTC",
227 &error,
228 NULL,
e5609878 229 "bbb", b, arg_adjust_system_clock, arg_ask_password);
a281d9c7 230 if (r < 0)
be90a886 231 log_error("Failed to set local RTC: %s", bus_error_message(&error, r));
a281d9c7
TA
232
233 return r;
6d0274f1
LP
234}
235
be90a886 236static int set_ntp(int argc, char **argv, void *userdata) {
4afd3348 237 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
be90a886 238 sd_bus *bus = userdata;
e5609878 239 int b, r;
6d0274f1 240
8a4b13c5 241 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 242
be90a886
YW
243 b = parse_boolean(argv[1]);
244 if (b < 0)
245 return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]);
6d0274f1 246
a281d9c7
TA
247 r = sd_bus_call_method(bus,
248 "org.freedesktop.timedate1",
249 "/org/freedesktop/timedate1",
250 "org.freedesktop.timedate1",
251 "SetNTP",
252 &error,
253 NULL,
e5609878 254 "bb", b, arg_ask_password);
a281d9c7 255 if (r < 0)
be90a886 256 log_error("Failed to set ntp: %s", bus_error_message(&error, r));
a281d9c7
TA
257
258 return r;
6d0274f1
LP
259}
260
be90a886 261static int list_timezones(int argc, char **argv, void *userdata) {
6d0274f1 262 _cleanup_strv_free_ char **zones = NULL;
75683450 263 int r;
6d0274f1 264
75683450 265 r = get_timezones(&zones);
f647962d
MS
266 if (r < 0)
267 return log_error_errno(r, "Failed to read list of time zones: %m");
6d0274f1 268
ee5324aa 269 (void) pager_open(arg_no_pager, false);
7c2d8094 270 strv_print(zones);
6d0274f1
LP
271
272 return 0;
273}
274
be90a886 275static int help(void) {
7591abd4
LP
276 printf("%s [OPTIONS...] COMMAND ...\n\n"
277 "Query or change system time and date settings.\n\n"
07a062a7 278 " -h --help Show this help message\n"
4f8f66cb
ZJS
279 " --version Show package version\n"
280 " --no-pager Do not pipe output into a pager\n"
281 " --no-ask-password Do not prompt for password\n"
282 " -H --host=[USER@]HOST Operate on remote host\n"
283 " -M --machine=CONTAINER Operate on local container\n"
284 " --adjust-system-clock Adjust system clock when changing local RTC mode\n\n"
6d0274f1 285 "Commands:\n"
4f8f66cb
ZJS
286 " status Show current time settings\n"
287 " set-time TIME Set system time\n"
07a062a7
JSJ
288 " set-timezone ZONE Set system time zone\n"
289 " list-timezones Show known time zones\n"
4f8f66cb 290 " set-local-rtc BOOL Control whether RTC is in local time\n"
3906ab4a 291 " set-ntp BOOL Enable or disable network time synchronization\n",
6d0274f1 292 program_invocation_short_name);
be90a886
YW
293
294 return 0;
295}
296
297static int verb_help(int argc, char **argv, void *userdata) {
298 return help();
6d0274f1
LP
299}
300
301static int parse_argv(int argc, char *argv[]) {
302
303 enum {
304 ARG_VERSION = 0x100,
305 ARG_NO_PAGER,
c9783430 306 ARG_ADJUST_SYSTEM_CLOCK,
6d0274f1
LP
307 ARG_NO_ASK_PASSWORD
308 };
309
310 static const struct option options[] = {
c9783430
LP
311 { "help", no_argument, NULL, 'h' },
312 { "version", no_argument, NULL, ARG_VERSION },
313 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
314 { "host", required_argument, NULL, 'H' },
a281d9c7 315 { "machine", required_argument, NULL, 'M' },
c9783430
LP
316 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
317 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
eb9da376 318 {}
6d0274f1
LP
319 };
320
321 int c;
322
323 assert(argc >= 0);
324 assert(argv);
325
601185b4 326 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
6d0274f1
LP
327
328 switch (c) {
329
330 case 'h':
be90a886 331 return help();
6d0274f1
LP
332
333 case ARG_VERSION:
3f6fd1ba 334 return version();
6d0274f1 335
a281d9c7
TA
336 case 'H':
337 arg_transport = BUS_TRANSPORT_REMOTE;
338 arg_host = optarg;
6d0274f1
LP
339 break;
340
a281d9c7 341 case 'M':
de33fc62 342 arg_transport = BUS_TRANSPORT_MACHINE;
a281d9c7 343 arg_host = optarg;
6d0274f1
LP
344 break;
345
546158bc
JJ
346 case ARG_NO_ASK_PASSWORD:
347 arg_ask_password = false;
348 break;
349
c9783430
LP
350 case ARG_ADJUST_SYSTEM_CLOCK:
351 arg_adjust_system_clock = true;
6d0274f1
LP
352 break;
353
354 case ARG_NO_PAGER:
355 arg_no_pager = true;
356 break;
357
358 case '?':
359 return -EINVAL;
360
361 default:
eb9da376 362 assert_not_reached("Unhandled option");
6d0274f1 363 }
6d0274f1
LP
364
365 return 1;
366}
367
a281d9c7 368static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
6d0274f1 369
be90a886
YW
370 static const Verb verbs[] = {
371 { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
372 { "set-time", 2, 2, 0, set_time },
373 { "set-timezone", 2, 2, 0, set_timezone },
374 { "list-timezones", VERB_ANY, 1, 0, list_timezones },
375 { "set-local-rtc", 2, 2, 0, set_local_rtc },
376 { "set-ntp", 2, 2, 0, set_ntp },
377 { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
378 {}
6d0274f1
LP
379 };
380
be90a886 381 return dispatch_verb(argc, argv, verbs, bus);
6d0274f1
LP
382}
383
384int main(int argc, char *argv[]) {
a3c56345 385 sd_bus *bus = NULL;
84f6181c 386 int r;
6d0274f1 387
a9cdc94f 388 setlocale(LC_ALL, "");
6d0274f1
LP
389 log_parse_environment();
390 log_open();
391
392 r = parse_argv(argc, argv);
84f6181c 393 if (r <= 0)
6d0274f1 394 goto finish;
6d0274f1 395
266f3e26 396 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
a281d9c7 397 if (r < 0) {
da927ba9 398 log_error_errno(r, "Failed to create bus connection: %m");
a281d9c7 399 goto finish;
6d0274f1
LP
400 }
401
a281d9c7 402 r = timedatectl_main(bus, argc, argv);
6d0274f1 403
a281d9c7 404finish:
0a84daa5
FB
405 /* make sure we terminate the bus connection first, and then close the
406 * pager, see issue #3543 for the details. */
a3c56345 407 sd_bus_flush_close_unref(bus);
6d0274f1
LP
408 pager_close();
409
84f6181c 410 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
6d0274f1 411}