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