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