]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/timedate/timedatectl.c
shared: split out code that maps properties to local structs
[thirdparty/systemd.git] / src / timedate / timedatectl.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <getopt.h>
4#include <locale.h>
5#include <math.h>
6#include <stdbool.h>
7#include <stdlib.h>
8
9#include "sd-bus.h"
10
11#include "bus-error.h"
12#include "bus-locator.h"
13#include "bus-map-properties.h"
14#include "format-table.h"
15#include "in-addr-util.h"
16#include "main-func.h"
17#include "pager.h"
18#include "parse-util.h"
19#include "pretty-print.h"
20#include "sparse-endian.h"
21#include "spawn-polkit-agent.h"
22#include "string-table.h"
23#include "strv.h"
24#include "terminal-util.h"
25#include "util.h"
26#include "verbs.h"
27
28static PagerFlags arg_pager_flags = 0;
29static bool arg_ask_password = true;
30static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
31static char *arg_host = NULL;
32static bool arg_adjust_system_clock = false;
33static bool arg_monitor = false;
34static char **arg_property = NULL;
35static bool arg_value = false;
36static bool arg_all = false;
37
38typedef struct StatusInfo {
39 usec_t time;
40 const char *timezone;
41
42 usec_t rtc_time;
43 bool rtc_local;
44
45 bool ntp_capable;
46 bool ntp_active;
47 bool ntp_synced;
48} StatusInfo;
49
50static int print_status_info(const StatusInfo *i) {
51 _cleanup_(table_unrefp) Table *table = NULL;
52 const char *old_tz = NULL, *tz, *tz_colon;
53 bool have_time = false;
54 char a[LINE_MAX];
55 TableCell *cell;
56 struct tm tm;
57 time_t sec;
58 size_t n;
59 int r;
60
61 assert(i);
62
63 table = table_new("key", "value");
64 if (!table)
65 return log_oom();
66
67 table_set_header(table, false);
68
69 assert_se(cell = table_get_cell(table, 0, 0));
70 (void) table_set_ellipsize_percent(table, cell, 100);
71 (void) table_set_align_percent(table, cell, 100);
72
73 assert_se(cell = table_get_cell(table, 0, 1));
74 (void) table_set_ellipsize_percent(table, cell, 100);
75
76 /* Save the old $TZ */
77 tz = getenv("TZ");
78 if (tz)
79 old_tz = strdupa(tz);
80
81 /* Set the new $TZ */
82 tz_colon = strjoina(":", isempty(i->timezone) ? "UTC" : i->timezone);
83 if (setenv("TZ", tz_colon, true) < 0)
84 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
85 else
86 tzset();
87
88 if (i->time != 0) {
89 sec = (time_t) (i->time / USEC_PER_SEC);
90 have_time = true;
91 } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
92 sec = time(NULL);
93 have_time = true;
94 } else
95 log_warning("Could not get time from timedated and not operating locally, ignoring.");
96
97 if (have_time)
98 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
99
100 r = table_add_many(table,
101 TABLE_STRING, "Local time:",
102 TABLE_STRING, have_time && n > 0 ? a : "n/a");
103 if (r < 0)
104 return table_log_add_error(r);
105
106 if (have_time)
107 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
108
109 r = table_add_many(table,
110 TABLE_STRING, "Universal time:",
111 TABLE_STRING, have_time && n > 0 ? a : "n/a");
112 if (r < 0)
113 return table_log_add_error(r);
114
115 if (i->rtc_time > 0) {
116 time_t rtc_sec;
117
118 rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
119 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
120 }
121
122 r = table_add_many(table,
123 TABLE_STRING, "RTC time:",
124 TABLE_STRING, i->rtc_time > 0 && n > 0 ? a : "n/a");
125 if (r < 0)
126 return table_log_add_error(r);
127
128 if (have_time)
129 n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
130
131 r = table_add_cell(table, NULL, TABLE_STRING, "Time zone:");
132 if (r < 0)
133 return table_log_add_error(r);
134
135 r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->timezone), have_time && n > 0 ? a : "n/a");
136 if (r < 0)
137 return table_log_add_error(r);
138
139
140 /* Restore the $TZ */
141 if (old_tz)
142 r = setenv("TZ", old_tz, true);
143 else
144 r = unsetenv("TZ");
145 if (r < 0)
146 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
147 else
148 tzset();
149
150 r = table_add_many(table,
151 TABLE_STRING, "System clock synchronized:",
152 TABLE_BOOLEAN, i->ntp_synced,
153 TABLE_STRING, "NTP service:",
154 TABLE_STRING, i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
155 TABLE_STRING, "RTC in local TZ:",
156 TABLE_BOOLEAN, i->rtc_local);
157 if (r < 0)
158 return table_log_add_error(r);
159
160 r = table_print(table, NULL);
161 if (r < 0)
162 return log_error_errno(r, "Failed to show table: %m");
163
164 if (i->rtc_local)
165 printf("\n%s"
166 "Warning: The system is configured to read the RTC time in the local time zone.\n"
167 " This mode cannot be fully supported. It will create various problems\n"
168 " with time zone changes and daylight saving time adjustments. The RTC\n"
169 " time is never updated, it relies on external facilities to maintain it.\n"
170 " If at all possible, use RTC in UTC by calling\n"
171 " 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal());
172
173 return 0;
174}
175
176static int show_status(int argc, char **argv, void *userdata) {
177 StatusInfo info = {};
178 static const struct bus_properties_map map[] = {
179 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
180 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
181 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_active) },
182 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
183 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
184 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
185 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
186 {}
187 };
188
189 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
190 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
191 sd_bus *bus = userdata;
192 int r;
193
194 assert(bus);
195
196 r = bus_map_all_properties(bus,
197 "org.freedesktop.timedate1",
198 "/org/freedesktop/timedate1",
199 map,
200 BUS_MAP_BOOLEAN_AS_BOOL,
201 &error,
202 &m,
203 &info);
204 if (r < 0)
205 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
206
207 return print_status_info(&info);
208}
209
210static int show_properties(int argc, char **argv, void *userdata) {
211 sd_bus *bus = userdata;
212 int r;
213
214 assert(bus);
215
216 r = bus_print_all_properties(bus,
217 "org.freedesktop.timedate1",
218 "/org/freedesktop/timedate1",
219 NULL,
220 arg_property,
221 arg_value,
222 arg_all,
223 NULL);
224 if (r < 0)
225 return bus_log_parse_error(r);
226
227 return 0;
228}
229
230static int set_time(int argc, char **argv, void *userdata) {
231 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
232 bool relative = false, interactive = arg_ask_password;
233 sd_bus *bus = userdata;
234 usec_t t;
235 int r;
236
237 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
238
239 r = parse_timestamp(argv[1], &t);
240 if (r < 0)
241 return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]);
242
243 r = bus_call_method(
244 bus,
245 bus_timedate,
246 "SetTime",
247 &error,
248 NULL,
249 "xbb", (int64_t) t, relative, interactive);
250 if (r < 0)
251 return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r));
252
253 return 0;
254}
255
256static int set_timezone(int argc, char **argv, void *userdata) {
257 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
258 sd_bus *bus = userdata;
259 int r;
260
261 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
262
263 r = bus_call_method(bus, bus_timedate, "SetTimezone", &error, NULL, "sb", argv[1], arg_ask_password);
264 if (r < 0)
265 return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r));
266
267 return 0;
268}
269
270static int set_local_rtc(int argc, char **argv, void *userdata) {
271 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
272 sd_bus *bus = userdata;
273 int r, b;
274
275 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
276
277 b = parse_boolean(argv[1]);
278 if (b < 0)
279 return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]);
280
281 r = bus_call_method(
282 bus,
283 bus_timedate,
284 "SetLocalRTC",
285 &error,
286 NULL,
287 "bbb", b, arg_adjust_system_clock, arg_ask_password);
288 if (r < 0)
289 return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r));
290
291 return 0;
292}
293
294static int set_ntp(int argc, char **argv, void *userdata) {
295 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
296 sd_bus *bus = userdata;
297 int b, r;
298
299 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
300
301 b = parse_boolean(argv[1]);
302 if (b < 0)
303 return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]);
304
305 r = bus_call_method(bus, bus_timedate, "SetNTP", &error, NULL, "bb", b, arg_ask_password);
306 if (r < 0)
307 return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r));
308
309 return 0;
310}
311
312static int list_timezones(int argc, char **argv, void *userdata) {
313 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
314 sd_bus *bus = userdata;
315 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
316 int r;
317 char** zones;
318
319 r = bus_call_method(bus, bus_timedate, "ListTimezones", &error, &reply, NULL);
320 if (r < 0)
321 return log_error_errno(r, "Failed to request list of time zones: %s",
322 bus_error_message(&error, r));
323
324 r = sd_bus_message_read_strv(reply, &zones);
325 if (r < 0)
326 return bus_log_parse_error(r);
327
328 (void) pager_open(arg_pager_flags);
329 strv_print(zones);
330
331 return 0;
332}
333
334typedef struct NTPStatusInfo {
335 const char *server_name;
336 char *server_address;
337 usec_t poll_interval, poll_max, poll_min;
338 usec_t root_distance_max;
339
340 uint32_t leap, version, mode, stratum;
341 int32_t precision;
342 usec_t root_delay, root_dispersion;
343 union {
344 char str[5];
345 uint32_t val;
346 } reference;
347 usec_t origin, recv, trans, dest;
348
349 bool spike;
350 uint64_t packet_count;
351 usec_t jitter;
352
353 int64_t freq;
354} NTPStatusInfo;
355
356static void ntp_status_info_clear(NTPStatusInfo *p) {
357 p->server_address = mfree(p->server_address);
358}
359
360static const char * const ntp_leap_table[4] = {
361 [0] = "normal",
362 [1] = "last minute of the day has 61 seconds",
363 [2] = "last minute of the day has 59 seconds",
364 [3] = "not synchronized",
365};
366
367DISABLE_WARNING_TYPE_LIMITS;
368DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ntp_leap, uint32_t);
369REENABLE_WARNING;
370
371static int print_ntp_status_info(NTPStatusInfo *i) {
372 char ts[FORMAT_TIMESPAN_MAX], jitter[FORMAT_TIMESPAN_MAX],
373 tmin[FORMAT_TIMESPAN_MAX], tmax[FORMAT_TIMESPAN_MAX];
374 usec_t delay, t14, t23, offset, root_distance;
375 _cleanup_(table_unrefp) Table *table = NULL;
376 bool offset_sign;
377 TableCell *cell;
378 int r;
379
380 assert(i);
381
382 table = table_new("key", "value");
383 if (!table)
384 return log_oom();
385
386 table_set_header(table, false);
387
388 assert_se(cell = table_get_cell(table, 0, 0));
389 (void) table_set_ellipsize_percent(table, cell, 100);
390 (void) table_set_align_percent(table, cell, 100);
391
392 assert_se(cell = table_get_cell(table, 0, 1));
393 (void) table_set_ellipsize_percent(table, cell, 100);
394
395 /*
396 * "Timestamp Name ID When Generated
397 * ------------------------------------------------------------
398 * Originate Timestamp T1 time request sent by client
399 * Receive Timestamp T2 time request received by server
400 * Transmit Timestamp T3 time reply sent by server
401 * Destination Timestamp T4 time reply received by client
402 *
403 * The round-trip delay, d, and system clock offset, t, are defined as:
404 * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2"
405 */
406
407 r = table_add_cell(table, NULL, TABLE_STRING, "Server:");
408 if (r < 0)
409 return table_log_add_error(r);
410
411 r = table_add_cell_stringf(table, NULL, "%s (%s)", i->server_address, i->server_name);
412 if (r < 0)
413 return table_log_add_error(r);
414
415 r = table_add_cell(table, NULL, TABLE_STRING, "Poll interval:");
416 if (r < 0)
417 return table_log_add_error(r);
418
419 r = table_add_cell_stringf(table, NULL, "%s (min: %s; max %s)",
420 format_timespan(ts, sizeof(ts), i->poll_interval, 0),
421 format_timespan(tmin, sizeof(tmin), i->poll_min, 0),
422 format_timespan(tmax, sizeof(tmax), i->poll_max, 0));
423 if (r < 0)
424 return table_log_add_error(r);
425
426 if (i->packet_count == 0) {
427 r = table_add_many(table,
428 TABLE_STRING, "Packet count:",
429 TABLE_STRING, "0");
430 if (r < 0)
431 return table_log_add_error(r);
432
433 r = table_print(table, NULL);
434 if (r < 0)
435 return log_error_errno(r, "Failed to show table: %m");
436
437 return 0;
438 }
439
440 if (i->dest < i->origin || i->trans < i->recv || i->dest - i->origin < i->trans - i->recv) {
441 log_error("Invalid NTP response");
442 r = table_print(table, NULL);
443 if (r < 0)
444 return log_error_errno(r, "Failed to show table: %m");
445
446 return 0;
447 }
448
449 delay = (i->dest - i->origin) - (i->trans - i->recv);
450
451 t14 = i->origin + i->dest;
452 t23 = i->recv + i->trans;
453 offset_sign = t14 < t23;
454 offset = (offset_sign ? t23 - t14 : t14 - t23) / 2;
455
456 root_distance = i->root_delay / 2 + i->root_dispersion;
457
458 r = table_add_many(table,
459 TABLE_STRING, "Leap:",
460 TABLE_STRING, ntp_leap_to_string(i->leap),
461 TABLE_STRING, "Version:",
462 TABLE_UINT32, i->version,
463 TABLE_STRING, "Stratum:",
464 TABLE_UINT32, i->stratum,
465 TABLE_STRING, "Reference:");
466 if (r < 0)
467 return table_log_add_error(r);
468
469 if (i->stratum <= 1)
470 r = table_add_cell(table, NULL, TABLE_STRING, i->reference.str);
471 else
472 r = table_add_cell_stringf(table, NULL, "%" PRIX32, be32toh(i->reference.val));
473 if (r < 0)
474 return table_log_add_error(r);
475
476 r = table_add_cell(table, NULL, TABLE_STRING, "Precision:");
477 if (r < 0)
478 return table_log_add_error(r);
479
480 r = table_add_cell_stringf(table, NULL, "%s (%" PRIi32 ")",
481 format_timespan(ts, sizeof(ts), DIV_ROUND_UP((nsec_t) (exp2(i->precision) * NSEC_PER_SEC), NSEC_PER_USEC), 0),
482 i->precision);
483 if (r < 0)
484 return table_log_add_error(r);
485
486 r = table_add_cell(table, NULL, TABLE_STRING, "Root distance:");
487 if (r < 0)
488 return table_log_add_error(r);
489
490 r = table_add_cell_stringf(table, NULL, "%s (max: %s)",
491 format_timespan(ts, sizeof(ts), root_distance, 0),
492 format_timespan(tmax, sizeof(tmax), i->root_distance_max, 0));
493 if (r < 0)
494 return table_log_add_error(r);
495
496 r = table_add_cell(table, NULL, TABLE_STRING, "Offset:");
497 if (r < 0)
498 return table_log_add_error(r);
499
500 r = table_add_cell_stringf(table, NULL, "%s%s",
501 offset_sign ? "+" : "-",
502 format_timespan(ts, sizeof(ts), offset, 0));
503 if (r < 0)
504 return table_log_add_error(r);
505
506 r = table_add_many(table,
507 TABLE_STRING, "Delay:",
508 TABLE_STRING, format_timespan(ts, sizeof(ts), delay, 0),
509 TABLE_STRING, "Jitter:",
510 TABLE_STRING, format_timespan(jitter, sizeof(jitter), i->jitter, 0),
511 TABLE_STRING, "Packet count:",
512 TABLE_UINT64, i->packet_count);
513 if (r < 0)
514 return table_log_add_error(r);
515
516 if (!i->spike) {
517 r = table_add_cell(table, NULL, TABLE_STRING, "Frequency:");
518 if (r < 0)
519 return table_log_add_error(r);
520
521 r = table_add_cell_stringf(table, NULL, "%+.3fppm", (double) i->freq / 0x10000);
522 if (r < 0)
523 return table_log_add_error(r);
524 }
525
526 r = table_print(table, NULL);
527 if (r < 0)
528 log_error_errno(r, "Failed to show table: %m");
529
530 return 0;
531}
532
533static int map_server_address(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
534 char **p = (char **) userdata;
535 const void *d;
536 int family, r;
537 size_t sz;
538
539 assert(p);
540
541 r = sd_bus_message_enter_container(m, 'r', "iay");
542 if (r < 0)
543 return r;
544
545 r = sd_bus_message_read(m, "i", &family);
546 if (r < 0)
547 return r;
548
549 r = sd_bus_message_read_array(m, 'y', &d, &sz);
550 if (r < 0)
551 return r;
552
553 r = sd_bus_message_exit_container(m);
554 if (r < 0)
555 return r;
556
557 if (sz == 0 && family == AF_UNSPEC) {
558 *p = mfree(*p);
559 return 0;
560 }
561
562 if (!IN_SET(family, AF_INET, AF_INET6))
563 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
564 "Unknown address family %i", family);
565
566 if (sz != FAMILY_ADDRESS_SIZE(family))
567 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
568 "Invalid address size");
569
570 r = in_addr_to_string(family, d, p);
571 if (r < 0)
572 return r;
573
574 return 0;
575}
576
577static int map_ntp_message(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
578 NTPStatusInfo *p = userdata;
579 const void *d;
580 size_t sz;
581 int32_t b;
582 int r;
583
584 assert(p);
585
586 r = sd_bus_message_enter_container(m, 'r', "uuuuittayttttbtt");
587 if (r < 0)
588 return r;
589
590 r = sd_bus_message_read(m, "uuuuitt",
591 &p->leap, &p->version, &p->mode, &p->stratum, &p->precision,
592 &p->root_delay, &p->root_dispersion);
593 if (r < 0)
594 return r;
595
596 r = sd_bus_message_read_array(m, 'y', &d, &sz);
597 if (r < 0)
598 return r;
599
600 r = sd_bus_message_read(m, "ttttbtt",
601 &p->origin, &p->recv, &p->trans, &p->dest,
602 &b, &p->packet_count, &p->jitter);
603 if (r < 0)
604 return r;
605
606 r = sd_bus_message_exit_container(m);
607 if (r < 0)
608 return r;
609
610 if (sz != 4)
611 return -EINVAL;
612
613 memcpy(p->reference.str, d, sz);
614
615 p->spike = b;
616
617 return 0;
618}
619
620static int show_timesync_status_once(sd_bus *bus) {
621 static const struct bus_properties_map map_timesync[] = {
622 { "ServerName", "s", NULL, offsetof(NTPStatusInfo, server_name) },
623 { "ServerAddress", "(iay)", map_server_address, offsetof(NTPStatusInfo, server_address) },
624 { "PollIntervalUSec", "t", NULL, offsetof(NTPStatusInfo, poll_interval) },
625 { "PollIntervalMinUSec", "t", NULL, offsetof(NTPStatusInfo, poll_min) },
626 { "PollIntervalMaxUSec", "t", NULL, offsetof(NTPStatusInfo, poll_max) },
627 { "RootDistanceMaxUSec", "t", NULL, offsetof(NTPStatusInfo, root_distance_max) },
628 { "NTPMessage", "(uuuuittayttttbtt)", map_ntp_message, 0 },
629 { "Frequency", "x", NULL, offsetof(NTPStatusInfo, freq) },
630 {}
631 };
632 _cleanup_(ntp_status_info_clear) NTPStatusInfo info = {};
633 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
634 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
635 int r;
636
637 assert(bus);
638
639 r = bus_map_all_properties(bus,
640 "org.freedesktop.timesync1",
641 "/org/freedesktop/timesync1",
642 map_timesync,
643 BUS_MAP_BOOLEAN_AS_BOOL,
644 &error,
645 &m,
646 &info);
647 if (r < 0)
648 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
649
650 if (arg_monitor && !terminal_is_dumb())
651 fputs(ANSI_HOME_CLEAR, stdout);
652
653 print_ntp_status_info(&info);
654
655 return 0;
656}
657
658static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
659 const char *name;
660 int r;
661
662 assert(m);
663
664 r = sd_bus_message_read(m, "s", &name);
665 if (r < 0)
666 return log_error_errno(r, "Failed to read interface name: %m");
667
668 if (!streq_ptr(name, "org.freedesktop.timesync1.Manager"))
669 return 0;
670
671 return show_timesync_status_once(sd_bus_message_get_bus(m));
672}
673
674static int show_timesync_status(int argc, char **argv, void *userdata) {
675 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
676 sd_bus *bus = userdata;
677 int r;
678
679 assert(bus);
680
681 r = show_timesync_status_once(bus);
682 if (r < 0)
683 return r;
684
685 if (!arg_monitor)
686 return 0;
687
688 r = sd_event_default(&event);
689 if (r < 0)
690 return log_error_errno(r, "Failed to get event loop: %m");
691
692 r = sd_bus_match_signal(bus,
693 NULL,
694 "org.freedesktop.timesync1",
695 "/org/freedesktop/timesync1",
696 "org.freedesktop.DBus.Properties",
697 "PropertiesChanged",
698 on_properties_changed, NULL);
699 if (r < 0)
700 return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
701
702 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
703 if (r < 0)
704 return log_error_errno(r, "Failed to attach bus to event loop: %m");
705
706 r = sd_event_loop(event);
707 if (r < 0)
708 return log_error_errno(r, "Failed to run event loop: %m");
709
710 return 0;
711}
712
713static int print_timesync_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
714 char type;
715 const char *contents;
716 int r;
717
718 assert(name);
719 assert(m);
720
721 r = sd_bus_message_peek_type(m, &type, &contents);
722 if (r < 0)
723 return r;
724
725 switch (type) {
726
727 case SD_BUS_TYPE_STRUCT:
728 if (streq(name, "NTPMessage")) {
729 _cleanup_(ntp_status_info_clear) NTPStatusInfo i = {};
730 char ts[FORMAT_TIMESPAN_MAX], stamp[FORMAT_TIMESTAMP_MAX];
731
732 r = map_ntp_message(NULL, NULL, m, NULL, &i);
733 if (r < 0)
734 return r;
735
736 if (i.packet_count == 0)
737 return 1;
738
739 if (!value) {
740 fputs(name, stdout);
741 fputc('=', stdout);
742 }
743
744 printf("{ Leap=%u, Version=%u, Mode=%u, Stratum=%u, Precision=%i,",
745 i.leap, i.version, i.mode, i.stratum, i.precision);
746 printf(" RootDelay=%s,",
747 format_timespan(ts, sizeof(ts), i.root_delay, 0));
748 printf(" RootDispersion=%s,",
749 format_timespan(ts, sizeof(ts), i.root_dispersion, 0));
750
751 if (i.stratum == 1)
752 printf(" Reference=%s,", i.reference.str);
753 else
754 printf(" Reference=%" PRIX32 ",", be32toh(i.reference.val));
755
756 printf(" OriginateTimestamp=%s,",
757 format_timestamp(stamp, sizeof(stamp), i.origin));
758 printf(" ReceiveTimestamp=%s,",
759 format_timestamp(stamp, sizeof(stamp), i.recv));
760 printf(" TransmitTimestamp=%s,",
761 format_timestamp(stamp, sizeof(stamp), i.trans));
762 printf(" DestinationTimestamp=%s,",
763 format_timestamp(stamp, sizeof(stamp), i.dest));
764 printf(" Ignored=%s PacketCount=%" PRIu64 ",",
765 yes_no(i.spike), i.packet_count);
766 printf(" Jitter=%s }\n",
767 format_timespan(ts, sizeof(ts), i.jitter, 0));
768
769 return 1;
770
771 } else if (streq(name, "ServerAddress")) {
772 _cleanup_free_ char *str = NULL;
773
774 r = map_server_address(NULL, NULL, m, NULL, &str);
775 if (r < 0)
776 return r;
777
778 if (arg_all || !isempty(str))
779 bus_print_property_value(name, expected_value, value, str);
780
781 return 1;
782 }
783 break;
784 }
785
786 return 0;
787}
788
789static int show_timesync(int argc, char **argv, void *userdata) {
790 sd_bus *bus = userdata;
791 int r;
792
793 assert(bus);
794
795 r = bus_print_all_properties(bus,
796 "org.freedesktop.timesync1",
797 "/org/freedesktop/timesync1",
798 print_timesync_property,
799 arg_property,
800 arg_value,
801 arg_all,
802 NULL);
803 if (r < 0)
804 return bus_log_parse_error(r);
805
806 return 0;
807}
808
809static int parse_ifindex_bus(sd_bus *bus, const char *str) {
810 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
811 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
812 int32_t i;
813 int r;
814
815 assert(bus);
816 assert(str);
817
818 r = parse_ifindex(str);
819 if (r > 0)
820 return r;
821 assert(r < 0);
822
823 r = bus_call_method(bus, bus_network_mgr, "GetLinkByName", &error, &reply, "s", str);
824 if (r < 0)
825 return log_error_errno(r, "Failed to get ifindex of interfaces %s: %s", str, bus_error_message(&error, r));
826
827 r = sd_bus_message_read(reply, "io", &i, NULL);
828 if (r < 0)
829 return bus_log_create_error(r);
830
831 return i;
832}
833
834static int verb_ntp_servers(int argc, char **argv, void *userdata) {
835 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
836 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
837 sd_bus *bus = userdata;
838 int ifindex, r;
839
840 assert(bus);
841
842 ifindex = parse_ifindex_bus(bus, argv[1]);
843 if (ifindex < 0)
844 return ifindex;
845
846 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
847
848 r = bus_message_new_method_call(bus, &req, bus_network_mgr, "SetLinkNTP");
849 if (r < 0)
850 return bus_log_create_error(r);
851
852 r = sd_bus_message_append(req, "i", ifindex);
853 if (r < 0)
854 return bus_log_create_error(r);
855
856 r = sd_bus_message_append_strv(req, argv + 2);
857 if (r < 0)
858 return bus_log_create_error(r);
859
860 r = sd_bus_call(bus, req, 0, &error, NULL);
861 if (r < 0)
862 return log_error_errno(r, "Failed to set NTP servers: %s", bus_error_message(&error, r));
863
864 return 0;
865}
866
867static int verb_revert(int argc, char **argv, void *userdata) {
868 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
869 sd_bus *bus = userdata;
870 int ifindex, r;
871
872 assert(bus);
873
874 ifindex = parse_ifindex_bus(bus, argv[1]);
875 if (ifindex < 0)
876 return ifindex;
877
878 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
879
880 r = bus_call_method(bus, bus_network_mgr, "RevertLinkNTP", &error, NULL, "i", ifindex);
881 if (r < 0)
882 return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
883
884 return 0;
885}
886
887static int help(void) {
888 _cleanup_free_ char *link = NULL;
889 int r;
890
891 r = terminal_urlify_man("timedatectl", "1", &link);
892 if (r < 0)
893 return log_oom();
894
895 printf("%s [OPTIONS...] COMMAND ...\n"
896 "\n%sQuery or change system time and date settings.%s\n"
897 "\nCommands:\n"
898 " status Show current time settings\n"
899 " show Show properties of systemd-timedated\n"
900 " set-time TIME Set system time\n"
901 " set-timezone ZONE Set system time zone\n"
902 " list-timezones Show known time zones\n"
903 " set-local-rtc BOOL Control whether RTC is in local time\n"
904 " set-ntp BOOL Enable or disable network time synchronization\n"
905 "\nsystemd-timesyncd Commands:\n"
906 " timesync-status Show status of systemd-timesyncd\n"
907 " show-timesync Show properties of systemd-timesyncd\n"
908 "\nOptions:\n"
909 " -h --help Show this help message\n"
910 " --version Show package version\n"
911 " --no-pager Do not pipe output into a pager\n"
912 " --no-ask-password Do not prompt for password\n"
913 " -H --host=[USER@]HOST Operate on remote host\n"
914 " -M --machine=CONTAINER Operate on local container\n"
915 " --adjust-system-clock Adjust system clock when changing local RTC mode\n"
916 " --monitor Monitor status of systemd-timesyncd\n"
917 " -p --property=NAME Show only properties by this name\n"
918 " -a --all Show all properties, including empty ones\n"
919 " --value When showing properties, only print the value\n"
920 "\nSee the %s for details.\n"
921 , program_invocation_short_name
922 , ansi_highlight()
923 , ansi_normal()
924 , link
925 );
926
927 return 0;
928}
929
930static int verb_help(int argc, char **argv, void *userdata) {
931 return help();
932}
933
934static int parse_argv(int argc, char *argv[]) {
935
936 enum {
937 ARG_VERSION = 0x100,
938 ARG_NO_PAGER,
939 ARG_ADJUST_SYSTEM_CLOCK,
940 ARG_NO_ASK_PASSWORD,
941 ARG_MONITOR,
942 ARG_VALUE,
943 };
944
945 static const struct option options[] = {
946 { "help", no_argument, NULL, 'h' },
947 { "version", no_argument, NULL, ARG_VERSION },
948 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
949 { "host", required_argument, NULL, 'H' },
950 { "machine", required_argument, NULL, 'M' },
951 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
952 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
953 { "monitor", no_argument, NULL, ARG_MONITOR },
954 { "property", required_argument, NULL, 'p' },
955 { "all", no_argument, NULL, 'a' },
956 { "value", no_argument, NULL, ARG_VALUE },
957 {}
958 };
959
960 int c, r;
961
962 assert(argc >= 0);
963 assert(argv);
964
965 while ((c = getopt_long(argc, argv, "hH:M:p:a", options, NULL)) >= 0)
966
967 switch (c) {
968
969 case 'h':
970 return help();
971
972 case ARG_VERSION:
973 return version();
974
975 case 'H':
976 arg_transport = BUS_TRANSPORT_REMOTE;
977 arg_host = optarg;
978 break;
979
980 case 'M':
981 arg_transport = BUS_TRANSPORT_MACHINE;
982 arg_host = optarg;
983 break;
984
985 case ARG_NO_ASK_PASSWORD:
986 arg_ask_password = false;
987 break;
988
989 case ARG_ADJUST_SYSTEM_CLOCK:
990 arg_adjust_system_clock = true;
991 break;
992
993 case ARG_NO_PAGER:
994 arg_pager_flags |= PAGER_DISABLE;
995 break;
996
997 case ARG_MONITOR:
998 arg_monitor = true;
999 break;
1000
1001 case 'p': {
1002 r = strv_extend(&arg_property, optarg);
1003 if (r < 0)
1004 return log_oom();
1005
1006 /* If the user asked for a particular
1007 * property, show it to him, even if it is
1008 * empty. */
1009 arg_all = true;
1010 break;
1011 }
1012
1013 case 'a':
1014 arg_all = true;
1015 break;
1016
1017 case ARG_VALUE:
1018 arg_value = true;
1019 break;
1020
1021 case '?':
1022 return -EINVAL;
1023
1024 default:
1025 assert_not_reached("Unhandled option");
1026 }
1027
1028 return 1;
1029}
1030
1031static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
1032 static const Verb verbs[] = {
1033 { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
1034 { "show", VERB_ANY, 1, 0, show_properties },
1035 { "set-time", 2, 2, 0, set_time },
1036 { "set-timezone", 2, 2, 0, set_timezone },
1037 { "list-timezones", VERB_ANY, 1, 0, list_timezones },
1038 { "set-local-rtc", 2, 2, 0, set_local_rtc },
1039 { "set-ntp", 2, 2, 0, set_ntp },
1040 { "timesync-status", VERB_ANY, 1, 0, show_timesync_status },
1041 { "show-timesync", VERB_ANY, 1, 0, show_timesync },
1042 { "ntp-servers", 3, VERB_ANY, 0, verb_ntp_servers },
1043 { "revert", 2, 2, 0, verb_revert },
1044 { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
1045 {}
1046 };
1047
1048 return dispatch_verb(argc, argv, verbs, bus);
1049}
1050
1051static int run(int argc, char *argv[]) {
1052 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1053 int r;
1054
1055 setlocale(LC_ALL, "");
1056 log_setup_cli();
1057
1058 r = parse_argv(argc, argv);
1059 if (r <= 0)
1060 return r;
1061
1062 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
1063 if (r < 0)
1064 return log_error_errno(r, "Failed to create bus connection: %m");
1065
1066 return timedatectl_main(bus, argc, argv);
1067}
1068
1069DEFINE_MAIN_FUNCTION(run);