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