]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timedate/timedatectl.c
tree-wide: shorten error logging a bit
[thirdparty/systemd.git] / src / timedate / timedatectl.c
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-util.h"
13 #include "in-addr-util.h"
14 #include "pager.h"
15 #include "parse-util.h"
16 #include "spawn-polkit-agent.h"
17 #include "sparse-endian.h"
18 #include "string-table.h"
19 #include "strv.h"
20 #include "terminal-util.h"
21 #include "util.h"
22 #include "verbs.h"
23
24 static bool arg_no_pager = false;
25 static bool arg_ask_password = true;
26 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
27 static char *arg_host = NULL;
28 static bool arg_adjust_system_clock = false;
29 static bool arg_monitor = false;
30 static char **arg_property = NULL;
31 static bool arg_value = false;
32 static bool arg_all = false;
33
34 typedef struct StatusInfo {
35 usec_t time;
36 const char *timezone;
37
38 usec_t rtc_time;
39 bool rtc_local;
40
41 bool ntp_capable;
42 bool ntp_active;
43 bool ntp_synced;
44 } StatusInfo;
45
46 static void print_status_info(const StatusInfo *i) {
47 const char *old_tz = NULL, *tz;
48 bool have_time = false;
49 char a[LINE_MAX];
50 struct tm tm;
51 time_t sec;
52 size_t n;
53 int r;
54
55 assert(i);
56
57 /* Save the old $TZ */
58 tz = getenv("TZ");
59 if (tz)
60 old_tz = strdupa(tz);
61
62 /* Set the new $TZ */
63 if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0)
64 log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m");
65 else
66 tzset();
67
68 if (i->time != 0) {
69 sec = (time_t) (i->time / USEC_PER_SEC);
70 have_time = true;
71 } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) {
72 sec = time(NULL);
73 have_time = true;
74 } else
75 log_warning("Could not get time from timedated and not operating locally, ignoring.");
76
77 if (have_time) {
78 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
79 printf(" Local time: %s\n", n > 0 ? a : "n/a");
80
81 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
82 printf(" Universal time: %s\n", n > 0 ? a : "n/a");
83 } else {
84 printf(" Local time: %s\n", "n/a");
85 printf(" Universal time: %s\n", "n/a");
86 }
87
88 if (i->rtc_time > 0) {
89 time_t rtc_sec;
90
91 rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
92 n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
93 printf(" RTC time: %s\n", n > 0 ? a : "n/a");
94 } else
95 printf(" RTC time: %s\n", "n/a");
96
97 if (have_time)
98 n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
99
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
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",
114 strna(i->timezone), have_time && n > 0 ? a : "n/a",
115 yes_no(i->ntp_synced),
116 i->ntp_capable ? (i->ntp_active ? "active" : "inactive") : "n/a",
117 yes_no(i->rtc_local));
118
119 if (i->rtc_local)
120 printf("\n%s"
121 "Warning: The system is configured to read the RTC time in the local time zone.\n"
122 " This mode cannot be fully supported. It will create various problems\n"
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());
127 }
128
129 static int show_status(int argc, char **argv, void *userdata) {
130 StatusInfo info = {};
131 static const struct bus_properties_map map[] = {
132 { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) },
133 { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) },
134 { "NTP", "b", NULL, offsetof(StatusInfo, ntp_active) },
135 { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) },
136 { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) },
137 { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) },
138 { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) },
139 {}
140 };
141
142 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
143 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
144 sd_bus *bus = userdata;
145 int r;
146
147 assert(bus);
148
149 r = bus_map_all_properties(bus,
150 "org.freedesktop.timedate1",
151 "/org/freedesktop/timedate1",
152 map,
153 BUS_MAP_BOOLEAN_AS_BOOL,
154 &error,
155 &m,
156 &info);
157 if (r < 0)
158 return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r));
159
160 print_status_info(&info);
161
162 return r;
163 }
164
165 static 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
185 static int set_time(int argc, char **argv, void *userdata) {
186 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
187 bool relative = false, interactive = arg_ask_password;
188 sd_bus *bus = userdata;
189 usec_t t;
190 int r;
191
192 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
193
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]);
197
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,
205 "xbb", (int64_t) t, relative, interactive);
206 if (r < 0)
207 return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r));
208
209 return 0;
210 }
211
212 static int set_timezone(int argc, char **argv, void *userdata) {
213 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
214 sd_bus *bus = userdata;
215 int r;
216
217 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
218
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,
226 "sb", argv[1], arg_ask_password);
227 if (r < 0)
228 return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r));
229
230 return 0;
231 }
232
233 static int set_local_rtc(int argc, char **argv, void *userdata) {
234 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
235 sd_bus *bus = userdata;
236 int r, b;
237
238 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
239
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]);
243
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,
251 "bbb", b, arg_adjust_system_clock, arg_ask_password);
252 if (r < 0)
253 return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r));
254
255 return 0;
256 }
257
258 static int set_ntp(int argc, char **argv, void *userdata) {
259 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
260 sd_bus *bus = userdata;
261 int b, r;
262
263 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
264
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]);
268
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,
276 "bb", b, arg_ask_password);
277 if (r < 0)
278 return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r));
279
280 return 0;
281 }
282
283 static int list_timezones(int argc, char **argv, void *userdata) {
284 _cleanup_strv_free_ char **zones = NULL;
285 int r;
286
287 r = get_timezones(&zones);
288 if (r < 0)
289 return log_error_errno(r, "Failed to read list of time zones: %m");
290
291 (void) pager_open(arg_no_pager, false);
292 strv_print(zones);
293
294 return 0;
295 }
296
297 typedef 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
319 static void ntp_status_info_clear(NTPStatusInfo *p) {
320 p->server_address = mfree(p->server_address);
321 }
322
323 static 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"
332 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ntp_leap, uint32_t);
333 #pragma GCC diagnostic pop
334
335 static 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
410 static 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
456 static 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
494 p->spike = b;
495
496 return 0;
497 }
498
499 static 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
537 static 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
553 static 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
600 static 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
676 static 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
696 static int help(void) {
697 printf("%s [OPTIONS...] COMMAND ...\n\n"
698 "Query or change system time and date settings.\n\n"
699 " -h --help Show this help message\n"
700 " --version Show package version\n"
701 " --no-pager Do not pipe output into a pager\n"
702 " --no-ask-password Do not prompt for password\n"
703 " -H --host=[USER@]HOST Operate on remote host\n"
704 " -M --machine=CONTAINER Operate on local container\n"
705 " --adjust-system-clock Adjust system clock when changing local RTC mode\n"
706 " --monitor Monitor status of systemd-timesyncd\n"
707 " -p --property=NAME Show only properties by this name\n"
708 " -a --all Show all properties, including empty ones\n"
709 " --value When showing properties, only print the value\n"
710 "\n"
711 "Commands:\n"
712 " status Show current time settings\n"
713 " show Show properties of systemd-timedated\n"
714 " set-time TIME Set system time\n"
715 " set-timezone ZONE Set system time zone\n"
716 " list-timezones Show known time zones\n"
717 " set-local-rtc BOOL Control whether RTC is in local time\n"
718 " set-ntp BOOL Enable or disable network time synchronization\n"
719 "\n"
720 "systemd-timesyncd Commands:\n"
721 " timesync-status Show status of systemd-timesyncd\n"
722 " show-timesync Show properties of systemd-timesyncd\n"
723 , program_invocation_short_name);
724
725 return 0;
726 }
727
728 static int verb_help(int argc, char **argv, void *userdata) {
729 return help();
730 }
731
732 static int parse_argv(int argc, char *argv[]) {
733
734 enum {
735 ARG_VERSION = 0x100,
736 ARG_NO_PAGER,
737 ARG_ADJUST_SYSTEM_CLOCK,
738 ARG_NO_ASK_PASSWORD,
739 ARG_MONITOR,
740 ARG_VALUE,
741 };
742
743 static const struct option options[] = {
744 { "help", no_argument, NULL, 'h' },
745 { "version", no_argument, NULL, ARG_VERSION },
746 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
747 { "host", required_argument, NULL, 'H' },
748 { "machine", required_argument, NULL, 'M' },
749 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
750 { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK },
751 { "monitor", no_argument, NULL, ARG_MONITOR },
752 { "property", required_argument, NULL, 'p' },
753 { "all", no_argument, NULL, 'a' },
754 { "value", no_argument, NULL, ARG_VALUE },
755 {}
756 };
757
758 int c, r;
759
760 assert(argc >= 0);
761 assert(argv);
762
763 while ((c = getopt_long(argc, argv, "hH:M:p:a", options, NULL)) >= 0)
764
765 switch (c) {
766
767 case 'h':
768 return help();
769
770 case ARG_VERSION:
771 return version();
772
773 case 'H':
774 arg_transport = BUS_TRANSPORT_REMOTE;
775 arg_host = optarg;
776 break;
777
778 case 'M':
779 arg_transport = BUS_TRANSPORT_MACHINE;
780 arg_host = optarg;
781 break;
782
783 case ARG_NO_ASK_PASSWORD:
784 arg_ask_password = false;
785 break;
786
787 case ARG_ADJUST_SYSTEM_CLOCK:
788 arg_adjust_system_clock = true;
789 break;
790
791 case ARG_NO_PAGER:
792 arg_no_pager = true;
793 break;
794
795 case ARG_MONITOR:
796 arg_monitor = true;
797 break;
798
799 case 'p': {
800 r = strv_extend(&arg_property, optarg);
801 if (r < 0)
802 return log_oom();
803
804 /* If the user asked for a particular
805 * property, show it to him, even if it is
806 * empty. */
807 arg_all = true;
808 break;
809 }
810
811 case 'a':
812 arg_all = true;
813 break;
814
815 case ARG_VALUE:
816 arg_value = true;
817 break;
818
819 case '?':
820 return -EINVAL;
821
822 default:
823 assert_not_reached("Unhandled option");
824 }
825
826 return 1;
827 }
828
829 static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) {
830
831 static const Verb verbs[] = {
832 { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
833 { "show", VERB_ANY, 1, 0, show_properties },
834 { "set-time", 2, 2, 0, set_time },
835 { "set-timezone", 2, 2, 0, set_timezone },
836 { "list-timezones", VERB_ANY, 1, 0, list_timezones },
837 { "set-local-rtc", 2, 2, 0, set_local_rtc },
838 { "set-ntp", 2, 2, 0, set_ntp },
839 { "timesync-status", VERB_ANY, 1, 0, show_timesync_status },
840 { "show-timesync", VERB_ANY, 1, 0, show_timesync },
841 { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
842 {}
843 };
844
845 return dispatch_verb(argc, argv, verbs, bus);
846 }
847
848 int main(int argc, char *argv[]) {
849 sd_bus *bus = NULL;
850 int r;
851
852 setlocale(LC_ALL, "");
853 log_parse_environment();
854 log_open();
855
856 r = parse_argv(argc, argv);
857 if (r <= 0)
858 goto finish;
859
860 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
861 if (r < 0) {
862 log_error_errno(r, "Failed to create bus connection: %m");
863 goto finish;
864 }
865
866 r = timedatectl_main(bus, argc, argv);
867
868 finish:
869 /* make sure we terminate the bus connection first, and then close the
870 * pager, see issue #3543 for the details. */
871 sd_bus_flush_close_unref(bus);
872 pager_close();
873
874 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
875 }