]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedatectl.c
tree-wide: add clickable man page link to all --help texts
[thirdparty/systemd.git] / src / timedate / timedatectl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
6d0274f1 2
6d0274f1 3#include <getopt.h>
a9cdc94f 4#include <locale.h>
6129ec85 5#include <math.h>
3f6fd1ba
LP
6#include <stdbool.h>
7#include <stdlib.h>
6d0274f1 8
a281d9c7 9#include "sd-bus.h"
3f6fd1ba 10
a281d9c7 11#include "bus-error.h"
3f6fd1ba 12#include "bus-util.h"
6129ec85 13#include "in-addr-util.h"
3f6fd1ba 14#include "pager.h"
6bedfcbb 15#include "parse-util.h"
6d0274f1 16#include "spawn-polkit-agent.h"
6129ec85
YW
17#include "sparse-endian.h"
18#include "string-table.h"
6d0274f1 19#include "strv.h"
288a74cc 20#include "terminal-util.h"
3f6fd1ba 21#include "util.h"
be90a886 22#include "verbs.h"
6d0274f1 23
6d0274f1 24static bool arg_no_pager = false;
6d0274f1 25static bool arg_ask_password = true;
e1636421 26static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 27static char *arg_host = NULL;
e1636421 28static bool arg_adjust_system_clock = false;
6129ec85
YW
29static bool arg_monitor = false;
30static char **arg_property = NULL;
31static bool arg_value = false;
32static bool arg_all = false;
6d0274f1 33
6d0274f1 34typedef struct StatusInfo {
2f6a5907 35 usec_t time;
f37f8a61 36 const char *timezone;
2f6a5907
KS
37
38 usec_t rtc_time;
f37f8a61 39 bool rtc_local;
2f6a5907 40
f37f8a61 41 bool ntp_capable;
f567f086 42 bool ntp_active;
f37f8a61 43 bool ntp_synced;
6d0274f1
LP
44} StatusInfo;
45
ffc06c35 46static void print_status_info(const StatusInfo *i) {
f567f086
YW
47 const char *old_tz = NULL, *tz;
48 bool have_time = false;
14ce0c25 49 char a[LINE_MAX];
6d0274f1
LP
50 struct tm tm;
51 time_t sec;
14ce0c25 52 size_t n;
f567f086 53 int r;
6d0274f1 54
59965986
LP
55 assert(i);
56
d95a74ed
LP
57 /* Save the old $TZ */
58 tz = getenv("TZ");
59 if (tz)
60 old_tz = strdupa(tz);
2311eb2f 61
d95a74ed 62 /* Set the new $TZ */
bdeb9e60 63 if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0)
d95a74ed
LP
64 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
65 else
66 tzset();
3e5e74d5 67
9ff09bcb
SL
68 if (i->time != 0) {
69 sec = (time_t) (i->time / USEC_PER_SEC);
70 have_time = true;
d95a74ed 71 } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
9ff09bcb
SL
72 sec = time(NULL);
73 have_time = true;
74 } else
d95a74ed 75 log_warning("Could not get time from timedated and not operating locally, ignoring.");
6d0274f1 76
9ff09bcb 77 if (have_time) {
14ce0c25 78 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
f567f086 79 printf(" Local time: %s\n", n > 0 ? a : "n/a");
5ffa8c81 80
14ce0c25 81 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
f567f086 82 printf(" Universal time: %s\n", n > 0 ? a : "n/a");
9ff09bcb 83 } else {
f567f086
YW
84 printf(" Local time: %s\n", "n/a");
85 printf(" Universal time: %s\n", "n/a");
9ff09bcb 86 }
6d0274f1 87
2f6a5907
KS
88 if (i->rtc_time > 0) {
89 time_t rtc_sec;
6d0274f1 90
d95a74ed 91 rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
14ce0c25 92 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
f567f086 93 printf(" RTC time: %s\n", n > 0 ? a : "n/a");
2f6a5907 94 } else
f567f086 95 printf(" RTC time: %s\n", "n/a");
6d0274f1 96
5ffa8c81 97 if (have_time)
14ce0c25 98 n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
2667cc25 99
d95a74ed
LP
100 /* Restore the $TZ */
101 if (old_tz)
102 r = setenv("TZ", old_tz, true);
103 else
104 r = unsetenv("TZ");
105 if (r < 0)
106 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
107 else
108 tzset();
109
f567f086
YW
110 printf(" Time zone: %s (%s)\n"
111 "System clock synchronized: %s\n"
112 " NTP service: %s\n"
113 " RTC in local TZ: %s\n",
14ce0c25 114 strna(i->timezone), have_time && n > 0 ? a : "n/a",
2f6a5907 115 yes_no(i->ntp_synced),
f567f086 116 i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
2f6a5907 117 yes_no(i->rtc_local));
6d0274f1 118
2f6a5907 119 if (i->rtc_local)
54f8c958
LP
120 printf("\n%s"
121 "Warning: The system is configured to read the RTC time in the local time zone.\n"
87ac8d99 122 " This mode cannot be fully supported. It will create various problems\n"
54f8c958
LP
123 " with time zone changes and daylight saving time adjustments. The RTC\n"
124 " time is never updated, it relies on external facilities to maintain it.\n"
125 " If at all possible, use RTC in UTC by calling\n"
126 " 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal());
6d0274f1
LP
127}
128
be90a886 129static int show_status(int argc, char **argv, void *userdata) {
f37f8a61 130 StatusInfo info = {};
9f6eb1cd 131 static const struct bus_properties_map map[] = {
f567f086
YW
132 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
133 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
134 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_active) },
9f6eb1cd 135 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
f567f086
YW
136 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
137 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
138 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
ffc06c35
KS
139 {}
140 };
f9e0eefc
LP
141
142 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 143 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
be90a886 144 sd_bus *bus = userdata;
ffc06c35 145 int r;
6d0274f1 146
a281d9c7 147 assert(bus);
6d0274f1 148
ffc06c35
KS
149 r = bus_map_all_properties(bus,
150 "org.freedesktop.timedate1",
151 "/org/freedesktop/timedate1",
9f6eb1cd 152 map,
a7e4861c 153 BUS_MAP_BOOLEAN_AS_BOOL,
f9e0eefc 154 &error,
f37f8a61 155 &m,
9f6eb1cd 156 &info);
e7e55dbd 157 if (r < 0)
f9e0eefc 158 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
6d0274f1
LP
159
160 print_status_info(&info);
ffc06c35 161
ffc06c35 162 return r;
6d0274f1
LP
163}
164
ead0adb1
YW
165static int show_properties(int argc, char **argv, void *userdata) {
166 sd_bus *bus = userdata;
167 int r;
168
169 assert(bus);
170
171 r = bus_print_all_properties(bus,
172 "org.freedesktop.timedate1",
173 "/org/freedesktop/timedate1",
174 NULL,
175 arg_property,
176 arg_value,
177 arg_all,
178 NULL);
179 if (r < 0)
180 return bus_log_parse_error(r);
181
182 return 0;
183}
184
be90a886 185static int set_time(int argc, char **argv, void *userdata) {
4afd3348 186 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a281d9c7 187 bool relative = false, interactive = arg_ask_password;
be90a886 188 sd_bus *bus = userdata;
6d0274f1 189 usec_t t;
6d0274f1
LP
190 int r;
191
8a4b13c5 192 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 193
be90a886
YW
194 r = parse_timestamp(argv[1], &t);
195 if (r < 0)
196 return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]);
6d0274f1 197
a281d9c7
TA
198 r = sd_bus_call_method(bus,
199 "org.freedesktop.timedate1",
200 "/org/freedesktop/timedate1",
201 "org.freedesktop.timedate1",
202 "SetTime",
203 &error,
204 NULL,
be90a886 205 "xbb", (int64_t) t, relative, interactive);
a281d9c7 206 if (r < 0)
4ae25393 207 return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r));
a281d9c7 208
4ae25393 209 return 0;
6d0274f1
LP
210}
211
be90a886 212static int set_timezone(int argc, char **argv, void *userdata) {
4afd3348 213 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
be90a886 214 sd_bus *bus = userdata;
a281d9c7 215 int r;
6d0274f1 216
8a4b13c5 217 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 218
a281d9c7
TA
219 r = sd_bus_call_method(bus,
220 "org.freedesktop.timedate1",
221 "/org/freedesktop/timedate1",
222 "org.freedesktop.timedate1",
223 "SetTimezone",
224 &error,
225 NULL,
be90a886 226 "sb", argv[1], arg_ask_password);
a281d9c7 227 if (r < 0)
4ae25393 228 return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r));
a281d9c7 229
4ae25393 230 return 0;
6d0274f1
LP
231}
232
be90a886 233static int set_local_rtc(int argc, char **argv, void *userdata) {
4afd3348 234 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
be90a886 235 sd_bus *bus = userdata;
e5609878 236 int r, b;
6d0274f1 237
8a4b13c5 238 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 239
be90a886
YW
240 b = parse_boolean(argv[1]);
241 if (b < 0)
242 return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]);
6d0274f1 243
a281d9c7
TA
244 r = sd_bus_call_method(bus,
245 "org.freedesktop.timedate1",
246 "/org/freedesktop/timedate1",
247 "org.freedesktop.timedate1",
248 "SetLocalRTC",
249 &error,
250 NULL,
e5609878 251 "bbb", b, arg_adjust_system_clock, arg_ask_password);
a281d9c7 252 if (r < 0)
4ae25393 253 return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r));
a281d9c7 254
4ae25393 255 return 0;
6d0274f1
LP
256}
257
be90a886 258static int set_ntp(int argc, char **argv, void *userdata) {
4afd3348 259 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
be90a886 260 sd_bus *bus = userdata;
e5609878 261 int b, r;
6d0274f1 262
8a4b13c5 263 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6d0274f1 264
be90a886
YW
265 b = parse_boolean(argv[1]);
266 if (b < 0)
267 return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]);
6d0274f1 268
a281d9c7
TA
269 r = sd_bus_call_method(bus,
270 "org.freedesktop.timedate1",
271 "/org/freedesktop/timedate1",
272 "org.freedesktop.timedate1",
273 "SetNTP",
274 &error,
275 NULL,
e5609878 276 "bb", b, arg_ask_password);
a281d9c7 277 if (r < 0)
4ae25393 278 return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r));
a281d9c7 279
4ae25393 280 return 0;
6d0274f1
LP
281}
282
be90a886 283static int list_timezones(int argc, char **argv, void *userdata) {
6d0274f1 284 _cleanup_strv_free_ char **zones = NULL;
75683450 285 int r;
6d0274f1 286
75683450 287 r = get_timezones(&zones);
f647962d
MS
288 if (r < 0)
289 return log_error_errno(r, "Failed to read list of time zones: %m");
6d0274f1 290
ee5324aa 291 (void) pager_open(arg_no_pager, false);
7c2d8094 292 strv_print(zones);
6d0274f1
LP
293
294 return 0;
295}
296
6129ec85
YW
297typedef struct NTPStatusInfo {
298 const char *server_name;
299 char *server_address;
300 usec_t poll_interval, poll_max, poll_min;
301 usec_t root_distance_max;
302
303 uint32_t leap, version, mode, stratum;
304 int32_t precision;
305 usec_t root_delay, root_dispersion;
306 union {
307 char str[5];
308 uint32_t val;
309 } reference;
310 usec_t origin, recv, trans, dest;
311
312 bool spike;
313 uint64_t packet_count;
314 usec_t jitter;
315
316 int64_t freq;
317} NTPStatusInfo;
318
319static void ntp_status_info_clear(NTPStatusInfo *p) {
320 p->server_address = mfree(p->server_address);
321}
322
323static const char * const ntp_leap_table[4] = {
324 [0] = "normal",
325 [1] = "last minute of the day has 61 seconds",
326 [2] = "last minute of the day has 59 seconds",
327 [3] = "not synchronized",
328};
329
330#pragma GCC diagnostic push
331#pragma GCC diagnostic ignored "-Wtype-limits"
332DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ntp_leap, uint32_t);
333#pragma GCC diagnostic pop
334
335static void print_ntp_status_info(NTPStatusInfo *i) {
336 char ts[FORMAT_TIMESPAN_MAX], tmin[FORMAT_TIMESPAN_MAX], tmax[FORMAT_TIMESPAN_MAX];
337 usec_t delay, t14, t23, offset, root_distance;
338 bool offset_sign;
339
340 assert(i);
341
342 /*
343 * "Timestamp Name ID When Generated
344 * ------------------------------------------------------------
345 * Originate Timestamp T1 time request sent by client
346 * Receive Timestamp T2 time request received by server
347 * Transmit Timestamp T3 time reply sent by server
348 * Destination Timestamp T4 time reply received by client
349 *
350 * The round-trip delay, d, and system clock offset, t, are defined as:
351 * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2"
352 */
353
354 printf(" Server: %s (%s)\n",
355 i->server_address, i->server_name);
356 printf("Poll interval: %s (min: %s; max %s)\n",
357 format_timespan(ts, sizeof(ts), i->poll_interval, 0),
358 format_timespan(tmin, sizeof(tmin), i->poll_min, 0),
359 format_timespan(tmax, sizeof(tmax), i->poll_max, 0));
360
361 if (i->packet_count == 0) {
362 printf(" Packet count: 0\n");
363 return;
364 }
365
366 if (i->dest < i->origin || i->trans < i->recv || i->dest - i->origin < i->trans - i->recv) {
367 log_error("Invalid NTP response");
368 return;
369 }
370
371 delay = (i->dest - i->origin) - (i->trans - i->recv);
372
373 t14 = i->origin + i->dest;
374 t23 = i->recv + i->trans;
375 offset_sign = t14 < t23;
376 offset = (offset_sign ? t23 - t14 : t14 - t23) / 2;
377
378 root_distance = i->root_delay / 2 + i->root_dispersion;
379
380 printf(" Leap: %s\n"
381 " Version: %" PRIu32 "\n"
382 " Stratum: %" PRIu32 "\n",
383 ntp_leap_to_string(i->leap),
384 i->version,
385 i->stratum);
386 if (i->stratum <= 1)
387 printf(" Reference: %s\n", i->reference.str);
388 else
389 printf(" Reference: %" PRIX32 "\n", be32toh(i->reference.val));
390 printf(" Precision: %s (%" PRIi32 ")\n",
391 format_timespan(ts, sizeof(ts), DIV_ROUND_UP((nsec_t) (exp2(i->precision) * NSEC_PER_SEC), NSEC_PER_USEC), 0),
392 i->precision);
393 printf("Root distance: %s (max: %s)\n",
394 format_timespan(ts, sizeof(ts), root_distance, 0),
395 format_timespan(tmax, sizeof(tmax), i->root_distance_max, 0));
396 printf(" Offset: %s%s\n",
397 offset_sign ? "+" : "-",
398 format_timespan(ts, sizeof(ts), offset, 0));
399 printf(" Delay: %s\n",
400 format_timespan(ts, sizeof(ts), delay, 0));
401 printf(" Jitter: %s\n",
402 format_timespan(ts, sizeof(ts), i->jitter, 0));
403 printf(" Packet count: %" PRIu64 "\n", i->packet_count);
404
405 if (!i->spike)
406 printf(" Frequency: %+.3fppm\n",
407 (double) i->freq / 0x10000);
408}
409
410static int map_server_address(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
411 char **p = (char **) userdata;
412 const void *d;
413 int family, r;
414 size_t sz;
415
416 assert(p);
417
418 r = sd_bus_message_enter_container(m, 'r', "iay");
419 if (r < 0)
420 return r;
421
422 r = sd_bus_message_read(m, "i", &family);
423 if (r < 0)
424 return r;
425
426 r = sd_bus_message_read_array(m, 'y', &d, &sz);
427 if (r < 0)
428 return r;
429
430 r = sd_bus_message_exit_container(m);
431 if (r < 0)
432 return r;
433
434 if (sz == 0 && family == AF_UNSPEC) {
435 *p = mfree(*p);
436 return 0;
437 }
438
439 if (!IN_SET(family, AF_INET, AF_INET6)) {
440 log_error("Unknown address family %i", family);
441 return -EINVAL;
442 }
443
444 if (sz != FAMILY_ADDRESS_SIZE(family)) {
445 log_error("Invalid address size");
446 return -EINVAL;
447 }
448
449 r = in_addr_to_string(family, d, p);
450 if (r < 0)
451 return r;
452
453 return 0;
454}
455
456static int map_ntp_message(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
457 NTPStatusInfo *p = userdata;
458 const void *d;
459 size_t sz;
460 int32_t b;
461 int r;
462
463 assert(p);
464
465 r = sd_bus_message_enter_container(m, 'r', "uuuuittayttttbtt");
466 if (r < 0)
467 return r;
468
469 r = sd_bus_message_read(m, "uuuuitt",
470 &p->leap, &p->version, &p->mode, &p->stratum, &p->precision,
471 &p->root_delay, &p->root_dispersion);
472 if (r < 0)
473 return r;
474
475 r = sd_bus_message_read_array(m, 'y', &d, &sz);
476 if (r < 0)
477 return r;
478
479 r = sd_bus_message_read(m, "ttttbtt",
480 &p->origin, &p->recv, &p->trans, &p->dest,
481 &b, &p->packet_count, &p->jitter);
482 if (r < 0)
483 return r;
484
485 r = sd_bus_message_exit_container(m);
486 if (r < 0)
487 return r;
488
489 if (sz != 4)
490 return -EINVAL;
491
492 memcpy(p->reference.str, d, sz);
493
5d904a6a 494 p->spike = b;
6129ec85
YW
495
496 return 0;
497}
498
499static int show_timesync_status_once(sd_bus *bus) {
500 static const struct bus_properties_map map_timesync[] = {
501 { "ServerName", "s", NULL, offsetof(NTPStatusInfo, server_name) },
502 { "ServerAddress", "(iay)", map_server_address, offsetof(NTPStatusInfo, server_address) },
503 { "PollIntervalUSec", "t", NULL, offsetof(NTPStatusInfo, poll_interval) },
504 { "PollIntervalMinUSec", "t", NULL, offsetof(NTPStatusInfo, poll_min) },
505 { "PollIntervalMaxUSec", "t", NULL, offsetof(NTPStatusInfo, poll_max) },
506 { "RootDistanceMaxUSec", "t", NULL, offsetof(NTPStatusInfo, root_distance_max) },
507 { "NTPMessage", "(uuuuittayttttbtt)", map_ntp_message, 0 },
508 { "Frequency", "x", NULL, offsetof(NTPStatusInfo, freq) },
509 {}
510 };
511 _cleanup_(ntp_status_info_clear) NTPStatusInfo info = {};
512 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
513 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
514 int r;
515
516 assert(bus);
517
518 r = bus_map_all_properties(bus,
519 "org.freedesktop.timesync1",
520 "/org/freedesktop/timesync1",
521 map_timesync,
522 BUS_MAP_BOOLEAN_AS_BOOL,
523 &error,
524 &m,
525 &info);
526 if (r < 0)
527 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
528
529 if (arg_monitor && !terminal_is_dumb())
530 fputs(ANSI_HOME_CLEAR, stdout);
531
532 print_ntp_status_info(&info);
533
534 return 0;
535}
536
537static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
538 const char *name;
539 int r;
540
541 assert(m);
542
543 r = sd_bus_message_read(m, "s", &name);
544 if (r < 0)
545 return log_error_errno(r, "Failed to read interface name: %m");
546
547 if (!streq_ptr(name, "org.freedesktop.timesync1.Manager"))
548 return 0;
549
550 return show_timesync_status_once(sd_bus_message_get_bus(m));
551}
552
553static int show_timesync_status(int argc, char **argv, void *userdata) {
554 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
555 sd_bus *bus = userdata;
556 int r;
557
558 assert(bus);
559
560 r = show_timesync_status_once(bus);
561 if (r < 0)
562 return r;
563
564 if (!arg_monitor)
565 return 0;
566
567 r = sd_event_default(&event);
568 if (r < 0)
569 return log_error_errno(r, "Failed to get event loop: %m");
570
571 r = sd_bus_match_signal(bus,
572 NULL,
573 "org.freedesktop.timesync1",
574 "/org/freedesktop/timesync1",
575 "org.freedesktop.DBus.Properties",
576 "PropertiesChanged",
577 on_properties_changed, NULL);
578 if (r < 0)
579 return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
580
581 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
582 if (r < 0)
583 return log_error_errno(r, "Failed to attach bus to event loop: %m");
584
585 r = sd_event_loop(event);
586 if (r < 0)
587 return log_error_errno(r, "Failed to run event loop: %m");
588
589 return 0;
590}
591
592#define property(name, fmt, ...) \
593 do { \
594 if (value) \
595 printf(fmt "\n", __VA_ARGS__); \
596 else \
597 printf("%s=" fmt "\n", name, __VA_ARGS__); \
598 } while (0)
599
600static int print_timesync_property(const char *name, sd_bus_message *m, bool value, bool all) {
601 char type;
602 const char *contents;
603 int r;
604
605 assert(name);
606 assert(m);
607
608 r = sd_bus_message_peek_type(m, &type, &contents);
609 if (r < 0)
610 return r;
611
612 switch (type) {
613
614 case SD_BUS_TYPE_STRUCT:
615 if (streq(name, "NTPMessage")) {
616 _cleanup_(ntp_status_info_clear) NTPStatusInfo i = {};
617 char ts[FORMAT_TIMESPAN_MAX], stamp[FORMAT_TIMESTAMP_MAX];
618
619 r = map_ntp_message(NULL, NULL, m, NULL, &i);
620 if (r < 0)
621 return r;
622
623 if (i.packet_count == 0)
624 return 1;
625
626 if (!value) {
627 fputs(name, stdout);
628 fputc('=', stdout);
629 }
630
631 printf("{ Leap=%u, Version=%u, Mode=%u, Stratum=%u, Precision=%i,",
632 i.leap, i.version, i.mode, i.stratum, i.precision);
633 printf(" RootDelay=%s,",
634 format_timespan(ts, sizeof(ts), i.root_delay, 0));
635 printf(" RootDispersion=%s,",
636 format_timespan(ts, sizeof(ts), i.root_dispersion, 0));
637
638 if (i.stratum == 1)
639 printf(" Reference=%s,", i.reference.str);
640 else
641 printf(" Reference=%" PRIX32 ",", be32toh(i.reference.val));
642
643 printf(" OriginateTimestamp=%s,",
644 format_timestamp(stamp, sizeof(stamp), i.origin));
645 printf(" ReceiveTimestamp=%s,",
646 format_timestamp(stamp, sizeof(stamp), i.recv));
647 printf(" TransmitTimestamp=%s,",
648 format_timestamp(stamp, sizeof(stamp), i.trans));
649 printf(" DestinationTimestamp=%s,",
650 format_timestamp(stamp, sizeof(stamp), i.dest));
651 printf(" Ignored=%s PacketCount=%" PRIu64 ",",
652 yes_no(i.spike), i.packet_count);
653 printf(" Jitter=%s }\n",
654 format_timespan(ts, sizeof(ts), i.jitter, 0));
655
656 return 1;
657
658 } else if (streq(name, "ServerAddress")) {
659 _cleanup_free_ char *str = NULL;
660
661 r = map_server_address(NULL, NULL, m, NULL, &str);
662 if (r < 0)
663 return r;
664
665 if (arg_all || !isempty(str))
666 property(name, "%s", str);
667
668 return 1;
669 }
670 break;
671 }
672
673 return 0;
674}
675
676static int show_timesync(int argc, char **argv, void *userdata) {
677 sd_bus *bus = userdata;
678 int r;
679
680 assert(bus);
681
682 r = bus_print_all_properties(bus,
683 "org.freedesktop.timesync1",
684 "/org/freedesktop/timesync1",
685 print_timesync_property,
686 arg_property,
687 arg_value,
688 arg_all,
689 NULL);
690 if (r < 0)
691 return bus_log_parse_error(r);
692
693 return 0;
694}
695
be90a886 696static int help(void) {
37ec0fdd
LP
697 _cleanup_free_ char *link = NULL;
698 int r;
699
700 r = terminal_urlify_man("timedatectl", "1", &link);
701 if (r < 0)
702 return log_oom();
703
7591abd4
LP
704 printf("%s [OPTIONS...] COMMAND ...\n\n"
705 "Query or change system time and date settings.\n\n"
07a062a7 706 " -h --help Show this help message\n"
4f8f66cb
ZJS
707 " --version Show package version\n"
708 " --no-pager Do not pipe output into a pager\n"
709 " --no-ask-password Do not prompt for password\n"
710 " -H --host=[USER@]HOST Operate on remote host\n"
711 " -M --machine=CONTAINER Operate on local container\n"
6129ec85
YW
712 " --adjust-system-clock Adjust system clock when changing local RTC mode\n"
713 " --monitor Monitor status of systemd-timesyncd\n"
714 " -p --property=NAME Show only properties by this name\n"
715 " -a --all Show all properties, including empty ones\n"
716 " --value When showing properties, only print the value\n"
717 "\n"
6d0274f1 718 "Commands:\n"
4f8f66cb 719 " status Show current time settings\n"
ead0adb1 720 " show Show properties of systemd-timedated\n"
4f8f66cb 721 " set-time TIME Set system time\n"
07a062a7
JSJ
722 " set-timezone ZONE Set system time zone\n"
723 " list-timezones Show known time zones\n"
4f8f66cb 724 " set-local-rtc BOOL Control whether RTC is in local time\n"
6129ec85
YW
725 " set-ntp BOOL Enable or disable network time synchronization\n"
726 "\n"
727 "systemd-timesyncd Commands:\n"
728 " timesync-status Show status of systemd-timesyncd\n"
729 " show-timesync Show properties of systemd-timesyncd\n"
37ec0fdd
LP
730 "\nSee the %s for details.\n"
731 , program_invocation_short_name
732 , link
733 );
be90a886
YW
734
735 return 0;
736}
737
738static int verb_help(int argc, char **argv, void *userdata) {
739 return help();
6d0274f1
LP
740}
741
742static int parse_argv(int argc, char *argv[]) {
743
744 enum {
745 ARG_VERSION = 0x100,
746 ARG_NO_PAGER,
c9783430 747 ARG_ADJUST_SYSTEM_CLOCK,
6129ec85
YW
748 ARG_NO_ASK_PASSWORD,
749 ARG_MONITOR,
750 ARG_VALUE,
6d0274f1
LP
751 };
752
753 static const struct option options[] = {
c9783430
LP
754 { "help", no_argument, NULL, 'h' },
755 { "version", no_argument, NULL, ARG_VERSION },
756 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
757 { "host", required_argument, NULL, 'H' },
a281d9c7 758 { "machine", required_argument, NULL, 'M' },
c9783430
LP
759 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
760 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
6129ec85
YW
761 { "monitor", no_argument, NULL, ARG_MONITOR },
762 { "property", required_argument, NULL, 'p' },
763 { "all", no_argument, NULL, 'a' },
764 { "value", no_argument, NULL, ARG_VALUE },
eb9da376 765 {}
6d0274f1
LP
766 };
767
6129ec85 768 int c, r;
6d0274f1
LP
769
770 assert(argc >= 0);
771 assert(argv);
772
6129ec85 773 while ((c = getopt_long(argc, argv, "hH:M:p:a", options, NULL)) >= 0)
6d0274f1
LP
774
775 switch (c) {
776
777 case 'h':
be90a886 778 return help();
6d0274f1
LP
779
780 case ARG_VERSION:
3f6fd1ba 781 return version();
6d0274f1 782
a281d9c7
TA
783 case 'H':
784 arg_transport = BUS_TRANSPORT_REMOTE;
785 arg_host = optarg;
6d0274f1
LP
786 break;
787
a281d9c7 788 case 'M':
de33fc62 789 arg_transport = BUS_TRANSPORT_MACHINE;
a281d9c7 790 arg_host = optarg;
6d0274f1
LP
791 break;
792
546158bc
JJ
793 case ARG_NO_ASK_PASSWORD:
794 arg_ask_password = false;
795 break;
796
c9783430
LP
797 case ARG_ADJUST_SYSTEM_CLOCK:
798 arg_adjust_system_clock = true;
6d0274f1
LP
799 break;
800
801 case ARG_NO_PAGER:
802 arg_no_pager = true;
803 break;
804
6129ec85
YW
805 case ARG_MONITOR:
806 arg_monitor = true;
807 break;
808
809 case 'p': {
810 r = strv_extend(&arg_property, optarg);
811 if (r < 0)
812 return log_oom();
813
814 /* If the user asked for a particular
815 * property, show it to him, even if it is
816 * empty. */
817 arg_all = true;
818 break;
819 }
820
821 case 'a':
822 arg_all = true;
823 break;
824
825 case ARG_VALUE:
826 arg_value = true;
827 break;
828
6d0274f1
LP
829 case '?':
830 return -EINVAL;
831
832 default:
eb9da376 833 assert_not_reached("Unhandled option");
6d0274f1 834 }
6d0274f1
LP
835
836 return 1;
837}
838
a281d9c7 839static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
6d0274f1 840
be90a886 841 static const Verb verbs[] = {
6129ec85 842 { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
ead0adb1 843 { "show", VERB_ANY, 1, 0, show_properties },
6129ec85
YW
844 { "set-time", 2, 2, 0, set_time },
845 { "set-timezone", 2, 2, 0, set_timezone },
846 { "list-timezones", VERB_ANY, 1, 0, list_timezones },
847 { "set-local-rtc", 2, 2, 0, set_local_rtc },
848 { "set-ntp", 2, 2, 0, set_ntp },
849 { "timesync-status", VERB_ANY, 1, 0, show_timesync_status },
850 { "show-timesync", VERB_ANY, 1, 0, show_timesync },
851 { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
be90a886 852 {}
6d0274f1
LP
853 };
854
be90a886 855 return dispatch_verb(argc, argv, verbs, bus);
6d0274f1
LP
856}
857
858int main(int argc, char *argv[]) {
a3c56345 859 sd_bus *bus = NULL;
84f6181c 860 int r;
6d0274f1 861
a9cdc94f 862 setlocale(LC_ALL, "");
6d0274f1
LP
863 log_parse_environment();
864 log_open();
865
866 r = parse_argv(argc, argv);
84f6181c 867 if (r <= 0)
6d0274f1 868 goto finish;
6d0274f1 869
266f3e26 870 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
a281d9c7 871 if (r < 0) {
da927ba9 872 log_error_errno(r, "Failed to create bus connection: %m");
a281d9c7 873 goto finish;
6d0274f1
LP
874 }
875
a281d9c7 876 r = timedatectl_main(bus, argc, argv);
6d0274f1 877
a281d9c7 878finish:
0a84daa5
FB
879 /* make sure we terminate the bus connection first, and then close the
880 * pager, see issue #3543 for the details. */
a3c56345 881 sd_bus_flush_close_unref(bus);
6d0274f1
LP
882 pager_close();
883
84f6181c 884 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
6d0274f1 885}