]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
Merge pull request #18863 from keszybz/cmdline-escaping
[thirdparty/systemd.git] / src / timedate / timedated.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
f401e48c 2
f401e48c 3#include <errno.h>
ca78ad1d
ZJS
4#include <sys/stat.h>
5#include <sys/types.h>
f401e48c
LP
6#include <unistd.h>
7
40ca29a1 8#include "sd-bus.h"
f4f15635
LP
9#include "sd-event.h"
10#include "sd-messages.h"
40ca29a1 11
b5efdb8a 12#include "alloc-util.h"
96aad8d1 13#include "bus-common-errors.h"
f4f15635 14#include "bus-error.h"
40af3d02 15#include "bus-get-properties.h"
9b71e4ab 16#include "bus-locator.h"
ac9f55ed 17#include "bus-log-control-api.h"
807542be 18#include "bus-map-properties.h"
269e4d2d 19#include "bus-polkit.h"
f4f15635 20#include "clock-util.h"
afaae43b 21#include "conf-files.h"
f4f15635 22#include "def.h"
afaae43b 23#include "fd-util.h"
f4f15635 24#include "fileio-label.h"
e4de7287 25#include "fileio.h"
f4f15635 26#include "fs-util.h"
5d280742
YW
27#include "hashmap.h"
28#include "list.h"
1f47bc33 29#include "main-func.h"
0a970718 30#include "memory-util.h"
36dd5ffd 31#include "missing_capability.h"
f4f15635 32#include "path-util.h"
d7b8eec7 33#include "selinux-util.h"
fc021a5b 34#include "service-util.h"
754f0269 35#include "signal-util.h"
5d280742 36#include "string-util.h"
f4f15635 37#include "strv.h"
5d280742
YW
38#include "unit-def.h"
39#include "unit-name.h"
ee104e11 40#include "user-util.h"
f401e48c
LP
41
42#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
43#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
44
afaae43b
ZJS
45#define UNIT_LIST_DIRS (const char* const*) CONF_PATHS_STRV("systemd/ntp-units.d")
46
5d280742
YW
47typedef struct UnitStatusInfo {
48 char *name;
49 char *load_state;
50 char *unit_file_state;
51 char *active_state;
cf3872bd 52 char *path;
5d280742
YW
53
54 LIST_FIELDS(struct UnitStatusInfo, units);
55} UnitStatusInfo;
56
40ca29a1 57typedef struct Context {
d200735e
MS
58 char *zone;
59 bool local_rtc;
40ca29a1 60 Hashmap *polkit_registry;
2770af85 61 sd_bus_message *cache;
5d280742 62
3af0a96c 63 sd_bus_slot *slot_job_removed;
3af0a96c 64
5d280742 65 LIST_HEAD(UnitStatusInfo, units);
40ca29a1 66} Context;
f401e48c 67
5b098203 68#define log_unit_full_errno_zerook(unit, level, error, ...) \
ad7fb943
ZJS
69 ({ \
70 const UnitStatusInfo *_u = (unit); \
5b098203
YW
71 _u ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "UNIT=", _u->name, NULL, NULL, ##__VA_ARGS__) : \
72 log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
ad7fb943
ZJS
73 })
74
5b098203
YW
75#define log_unit_full_errno(unit, level, error, ...) \
76 ({ \
77 int _error = (error); \
78 ASSERT_NON_ZERO(_error); \
79 log_unit_full_errno_zerook(unit, level, _error, ##__VA_ARGS__); \
80 })
81
82#define log_unit_full(unit, level, ...) (void) log_unit_full_errno_zerook(unit, level, 0, ##__VA_ARGS__)
83
84#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, ##__VA_ARGS__)
85#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, ##__VA_ARGS__)
86#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, ##__VA_ARGS__)
87#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, ##__VA_ARGS__)
88#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, ##__VA_ARGS__)
ad7fb943 89
5b098203
YW
90#define log_unit_debug_errno(unit, error, ...) log_unit_full_errno(unit, LOG_DEBUG, error, ##__VA_ARGS__)
91#define log_unit_info_errno(unit, error, ...) log_unit_full_errno(unit, LOG_INFO, error, ##__VA_ARGS__)
92#define log_unit_notice_errno(unit, error, ...) log_unit_full_errno(unit, LOG_NOTICE, error, ##__VA_ARGS__)
93#define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, ##__VA_ARGS__)
94#define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, ##__VA_ARGS__)
ad7fb943 95
5d280742
YW
96static void unit_status_info_clear(UnitStatusInfo *p) {
97 assert(p);
98
99 p->load_state = mfree(p->load_state);
100 p->unit_file_state = mfree(p->unit_file_state);
101 p->active_state = mfree(p->active_state);
102}
103
104static void unit_status_info_free(UnitStatusInfo *p) {
105 assert(p);
106
107 unit_status_info_clear(p);
108 free(p->name);
cf3872bd 109 free(p->path);
5d280742
YW
110 free(p);
111}
112
1f47bc33 113static void context_clear(Context *c) {
5d280742
YW
114 UnitStatusInfo *p;
115
40ca29a1 116 assert(c);
f401e48c 117
82d115d9 118 free(c->zone);
36e34057 119 bus_verify_polkit_async_registry_free(c->polkit_registry);
2770af85 120 sd_bus_message_unref(c->cache);
5d280742 121
3af0a96c 122 sd_bus_slot_unref(c->slot_job_removed);
3af0a96c 123
5d280742
YW
124 while ((p = c->units)) {
125 LIST_REMOVE(units, c->units, p);
126 unit_status_info_free(p);
127 }
128}
129
ad7fb943 130static int context_add_ntp_service(Context *c, const char *s, const char *source) {
5d280742
YW
131 UnitStatusInfo *u;
132
133 if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
134 return -EINVAL;
135
136 /* Do not add this if it is already listed */
137 LIST_FOREACH(units, u, c->units)
138 if (streq(u->name, s))
139 return 0;
140
141 u = new0(UnitStatusInfo, 1);
142 if (!u)
143 return -ENOMEM;
144
145 u->name = strdup(s);
146 if (!u->name) {
147 free(u);
148 return -ENOMEM;
149 }
150
151 LIST_APPEND(units, c->units, u);
ad7fb943 152 log_unit_debug(u, "added from %s.", source);
5d280742
YW
153
154 return 0;
155}
156
afaae43b 157static int context_parse_ntp_services_from_environment(Context *c) {
5d280742
YW
158 const char *env, *p;
159 int r;
160
161 assert(c);
162
163 env = getenv("SYSTEMD_TIMEDATED_NTP_SERVICES");
afaae43b 164 if (!env)
5d280742 165 return 0;
afaae43b 166
ad7fb943 167 log_debug("Using list of ntp services from environment variable $SYSTEMD_TIMEDATED_NTP_SERVICES=%s.", env);
5d280742
YW
168
169 for (p = env;;) {
170 _cleanup_free_ char *word = NULL;
171
172 r = extract_first_word(&p, &word, ":", 0);
173 if (r == 0)
174 break;
175 if (r == -ENOMEM)
176 return log_oom();
177 if (r < 0) {
178 log_error("Invalid syntax, ignoring: %s", env);
179 break;
180 }
181
ad7fb943 182 r = context_add_ntp_service(c, word, "$SYSTEMD_TIMEDATED_NTP_SERVICES");
5d280742
YW
183 if (r < 0)
184 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
185 }
186
afaae43b
ZJS
187 return 1;
188}
189
190static int context_parse_ntp_services_from_disk(Context *c) {
191 _cleanup_strv_free_ char **files = NULL;
192 char **f;
193 int r;
194
195 r = conf_files_list_strv(&files, ".list", NULL, CONF_FILES_FILTER_MASKED, UNIT_LIST_DIRS);
196 if (r < 0)
197 return log_error_errno(r, "Failed to enumerate .list files: %m");
198
199 STRV_FOREACH(f, files) {
200 _cleanup_fclose_ FILE *file = NULL;
201
202 log_debug("Reading file '%s'", *f);
203
204 r = fopen_unlocked(*f, "re", &file);
205 if (r < 0) {
206 log_error_errno(r, "Failed to open %s, ignoring: %m", *f);
207 continue;
208 }
209
210 for (;;) {
211 _cleanup_free_ char *line = NULL;
212 const char *word;
213
214 r = read_line(file, LINE_MAX, &line);
215 if (r < 0) {
216 log_error_errno(r, "Failed to read %s, ignoring: %m", *f);
217 continue;
218 }
219 if (r == 0)
220 break;
221
222 word = strstrip(line);
03a81441 223 if (isempty(word) || startswith(word, "#"))
afaae43b
ZJS
224 continue;
225
ad7fb943 226 r = context_add_ntp_service(c, word, *f);
afaae43b
ZJS
227 if (r < 0)
228 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
229 }
230 }
231
232 return 1;
233}
234
235static int context_parse_ntp_services(Context *c) {
236 int r;
237
238 r = context_parse_ntp_services_from_environment(c);
239 if (r != 0)
240 return r;
241
242 return context_parse_ntp_services_from_disk(c);
5d280742
YW
243}
244
245static int context_ntp_service_is_active(Context *c) {
246 UnitStatusInfo *info;
247 int count = 0;
248
249 assert(c);
250
251 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
252
253 LIST_FOREACH(units, info, c->units)
84a87726 254 count += !STRPTR_IN_SET(info->active_state, "inactive", "failed");
5d280742
YW
255
256 return count;
257}
258
5d280742
YW
259static int context_ntp_service_exists(Context *c) {
260 UnitStatusInfo *info;
261 int count = 0;
262
263 assert(c);
264
265 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
266
267 LIST_FOREACH(units, info, c->units)
268 count += streq_ptr(info->load_state, "loaded");
269
270 return count;
f401e48c
LP
271}
272
40ca29a1 273static int context_read_data(Context *c) {
424a19f8 274 _cleanup_free_ char *t = NULL;
40ca29a1
LP
275 int r;
276
277 assert(c);
f401e48c 278
5c904ba5
LP
279 r = get_timezone(&t);
280 if (r == -EINVAL)
281 log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
282 else if (r < 0)
283 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
92c4ef2d 284
f9ecfd3b 285 free_and_replace(c->zone, t);
f401e48c 286
6369641d 287 c->local_rtc = clock_is_localtime(NULL) > 0;
f401e48c
LP
288
289 return 0;
290}
291
40ca29a1 292static int context_write_data_timezone(Context *c) {
424a19f8 293 _cleanup_free_ char *p = NULL;
9193af0f 294 const char *source;
40ca29a1
LP
295
296 assert(c);
e19a21a8 297
9193af0f
LP
298 /* No timezone is very similar to UTC. Hence in either of these cases link the UTC file in. Except if
299 * it isn't installed, in which case we remove the symlink altogether. Since glibc defaults to an
300 * internal version of UTC in that case behaviour is mostly equivalent. We still prefer creating the
301 * symlink though, since things are more self explanatory then. */
302
303 if (isempty(c->zone) || streq(c->zone, "UTC")) {
304
305 if (access("/usr/share/zoneinfo/UTC", F_OK) < 0) {
306
307 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
308 return -errno;
f401e48c 309
9193af0f
LP
310 return 0;
311 }
312
313 source = "../usr/share/zoneinfo/UTC";
314 } else {
315 p = path_join("../usr/share/zoneinfo", c->zone);
316 if (!p)
317 return -ENOMEM;
318
319 source = p;
320 }
f401e48c 321
9193af0f 322 return symlink_atomic(source, "/etc/localtime");
f401e48c
LP
323}
324
40ca29a1 325static int context_write_data_local_rtc(Context *c) {
7fd1b19b 326 _cleanup_free_ char *s = NULL, *w = NULL;
d9cb4bba 327 int r;
f401e48c 328
40ca29a1
LP
329 assert(c);
330
f401e48c
LP
331 r = read_full_file("/etc/adjtime", &s, NULL);
332 if (r < 0) {
333 if (r != -ENOENT)
334 return r;
335
40ca29a1 336 if (!c->local_rtc)
f401e48c
LP
337 return 0;
338
339 w = strdup(NULL_ADJTIME_LOCAL);
340 if (!w)
341 return -ENOMEM;
342 } else {
c9410dd4 343 char *p;
8f462d87 344 const char *e = "\n"; /* default if there is less than 3 lines */
c9410dd4 345 const char *prepend = "";
f401e48c
LP
346 size_t a, b;
347
c9410dd4 348 p = strchrnul(s, '\n');
cb971cc0 349 if (*p == '\0')
c9410dd4
MP
350 /* only one line, no \n terminator */
351 prepend = "\n0\n";
cb971cc0 352 else if (p[1] == '\0') {
c9410dd4
MP
353 /* only one line, with \n terminator */
354 ++p;
355 prepend = "0\n";
356 } else {
357 p = strchr(p+1, '\n');
358 if (!p) {
359 /* only two lines, no \n terminator */
360 prepend = "\n";
361 p = s + strlen(s);
362 } else {
363 char *end;
364 /* third line might have a \n terminator or not */
365 p++;
366 end = strchr(p, '\n');
367 /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */
368 if (end)
369 e = end;
370 }
371 }
f401e48c
LP
372
373 a = p - s;
374 b = strlen(e);
375
c9410dd4 376 w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1);
d257f05a 377 if (!w)
f401e48c 378 return -ENOMEM;
f401e48c 379
c9410dd4 380 *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
f401e48c
LP
381
382 if (streq(w, NULL_ADJTIME_UTC)) {
d257f05a 383 if (unlink("/etc/adjtime") < 0)
f401e48c
LP
384 if (errno != ENOENT)
385 return -errno;
f401e48c
LP
386
387 return 0;
388 }
389 }
40ca29a1 390
a9ba0e32
CG
391 r = mac_selinux_init();
392 if (r < 0)
393 return r;
394
d257f05a 395 return write_string_file_atomic_label("/etc/adjtime", w);
f401e48c
LP
396}
397
5d280742
YW
398static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) {
399 static const struct bus_properties_map map[] = {
400 { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
401 { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
402 { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
403 {}
404 };
5d280742 405 UnitStatusInfo *u;
c5f0532f
LP
406 int r;
407
40ca29a1 408 assert(c);
c5f0532f
LP
409 assert(bus);
410
2770af85
YW
411 /* Suppress calling context_update_ntp_status() multiple times within single DBus transaction. */
412 if (m) {
413 if (m == c->cache)
414 return 0;
2aa4c315 415
2770af85
YW
416 sd_bus_message_unref(c->cache);
417 c->cache = sd_bus_message_ref(m);
418 }
2aa4c315 419
5d280742
YW
420 LIST_FOREACH(units, u, c->units) {
421 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5d280742 422 _cleanup_free_ char *path = NULL;
c5f0532f 423
5d280742 424 unit_status_info_clear(u);
40ca29a1 425
5d280742
YW
426 path = unit_dbus_path_from_name(u->name);
427 if (!path)
428 return -ENOMEM;
429
430 r = bus_map_all_properties(
431 bus,
432 "org.freedesktop.systemd1",
433 path,
434 map,
435 BUS_MAP_STRDUP,
436 &error,
437 NULL,
438 u);
439 if (r < 0)
ad7fb943 440 return log_unit_error_errno(u, r, "Failed to get properties: %s", bus_error_message(&error, r));
5d280742 441 }
c5f0532f 442
40ca29a1 443 return 0;
c5f0532f
LP
444}
445
3af0a96c 446static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
3af0a96c 447 Context *c = userdata;
cf3872bd
YW
448 UnitStatusInfo *u;
449 const char *path;
450 unsigned n = 0;
3af0a96c
YW
451 int r;
452
453 assert(c);
454 assert(m);
455
456 r = sd_bus_message_read(m, "uoss", NULL, &path, NULL, NULL);
457 if (r < 0) {
458 bus_log_parse_error(r);
459 return 0;
460 }
461
cf3872bd
YW
462 LIST_FOREACH(units, u, c->units)
463 if (streq_ptr(path, u->path))
464 u->path = mfree(u->path);
465 else
466 n += !!u->path;
3af0a96c 467
cf3872bd 468 if (n == 0) {
cf3872bd 469 c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
49942d6b 470
ad7fb943
ZJS
471 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
472 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP",
473 NULL);
cf3872bd 474 }
3af0a96c
YW
475
476 return 0;
477}
478
cf3872bd 479static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
3af0a96c 480 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3af0a96c 481 const char *path;
c5f0532f
LP
482 int r;
483
5d280742 484 assert(u);
c5f0532f
LP
485 assert(bus);
486 assert(error);
487
43fe4f76 488 r = bus_call_method(
81b84399 489 bus,
43fe4f76 490 bus_systemd_mgr,
5d280742 491 start ? "StartUnit" : "StopUnit",
81b84399 492 error,
3af0a96c 493 &reply,
81b84399 494 "ss",
5d280742 495 u->name,
81b84399 496 "replace");
5b098203
YW
497 log_unit_full_errno_zerook(u, r < 0 ? LOG_WARNING : LOG_DEBUG, r,
498 "%s unit: %m", start ? "Starting" : "Stopping");
5d280742 499 if (r < 0)
b72ddf0f 500 return r;
c5f0532f 501
3af0a96c
YW
502 r = sd_bus_message_read(reply, "o", &path);
503 if (r < 0)
504 return bus_log_parse_error(r);
505
cf3872bd 506 r = free_and_strdup(&u->path, path);
3af0a96c
YW
507 if (r < 0)
508 return log_oom();
509
b72ddf0f 510 return 0;
c5f0532f
LP
511}
512
5d280742 513static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool enable) {
c5f0532f 514 int r;
c5f0532f 515
5d280742 516 assert(u);
c5f0532f
LP
517 assert(bus);
518 assert(error);
519
5d280742
YW
520 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
521
ad7fb943
ZJS
522 if (streq(u->unit_file_state, "enabled") == enable) {
523 log_unit_debug(u, "already %sd.", enable_disable(enable));
5d280742 524 return 0;
ad7fb943
ZJS
525 }
526
527 log_unit_info(u, "%s unit.", enable ? "Enabling" : "Disabling");
5d280742
YW
528
529 if (enable)
43fe4f76 530 r = bus_call_method(
40ca29a1 531 bus,
43fe4f76 532 bus_systemd_mgr,
b72ddf0f 533 "EnableUnitFiles",
40ca29a1
LP
534 error,
535 NULL,
b72ddf0f 536 "asbb", 1,
5d280742 537 u->name,
b72ddf0f
KS
538 false, true);
539 else
43fe4f76 540 r = bus_call_method(
b72ddf0f 541 bus,
43fe4f76 542 bus_systemd_mgr,
b72ddf0f
KS
543 "DisableUnitFiles",
544 error,
545 NULL,
546 "asb", 1,
5d280742 547 u->name,
b72ddf0f 548 false);
5d280742 549 if (r < 0)
b72ddf0f 550 return r;
c5f0532f 551
43fe4f76 552 r = bus_call_method(bus, bus_systemd_mgr, "Reload", error, NULL, NULL);
3af0a96c
YW
553 if (r < 0)
554 return r;
555
b72ddf0f 556 return 0;
40ca29a1 557}
c5f0532f 558
6cc379b5
YW
559static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_time, "t", now(CLOCK_REALTIME));
560static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_ntp_sync, "b", ntp_synced());
561
6fc60278
LP
562static int property_get_rtc_time(
563 sd_bus *bus,
564 const char *path,
565 const char *interface,
566 const char *property,
567 sd_bus_message *reply,
ebcf1f97
LP
568 void *userdata,
569 sd_bus_error *error) {
6fc60278 570
d9cb4bba
ZJS
571 struct tm tm = {};
572 usec_t t = 0;
6fc60278
LP
573 int r;
574
60989612 575 r = clock_get_hwclock(&tm);
d9cb4bba 576 if (r == -EBUSY)
07a062a7 577 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
d9cb4bba 578 else if (r == -ENOENT)
07a062a7 579 log_debug("/dev/rtc not found.");
d9cb4bba 580 else if (r < 0)
10a87006 581 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
ebcf1f97 582 else
2f6a5907 583 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
6fc60278 584
ebcf1f97 585 return sd_bus_message_append(reply, "t", t);
6fc60278
LP
586}
587
5d280742
YW
588static int property_get_can_ntp(
589 sd_bus *bus,
590 const char *path,
591 const char *interface,
592 const char *property,
593 sd_bus_message *reply,
594 void *userdata,
595 sd_bus_error *error) {
596
597 Context *c = userdata;
598 int r;
599
600 assert(c);
601 assert(bus);
602 assert(property);
603 assert(reply);
604 assert(error);
605
b4356b57
YW
606 if (c->slot_job_removed)
607 /* When the previous request is not finished, then assume NTP is enabled. */
608 return sd_bus_message_append(reply, "b", true);
609
5d280742
YW
610 r = context_update_ntp_status(c, bus, reply);
611 if (r < 0)
612 return r;
613
614 return sd_bus_message_append(reply, "b", context_ntp_service_exists(c) > 0);
615}
616
617static int property_get_ntp(
618 sd_bus *bus,
619 const char *path,
620 const char *interface,
621 const char *property,
622 sd_bus_message *reply,
623 void *userdata,
624 sd_bus_error *error) {
625
626 Context *c = userdata;
627 int r;
628
629 assert(c);
630 assert(bus);
631 assert(property);
632 assert(reply);
633 assert(error);
634
b4356b57
YW
635 if (c->slot_job_removed)
636 /* When the previous request is not finished, then assume NTP is active. */
637 return sd_bus_message_append(reply, "b", true);
638
5d280742
YW
639 r = context_update_ntp_status(c, bus, reply);
640 if (r < 0)
641 return r;
642
643 return sd_bus_message_append(reply, "b", context_ntp_service_is_active(c) > 0);
644}
645
19070062 646static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
40ca29a1 647 Context *c = userdata;
2c3def62 648 int interactive, r;
40ca29a1 649 const char *z;
c5f0532f 650
7e9cf16c
LP
651 assert(m);
652 assert(c);
653
40ca29a1
LP
654 r = sd_bus_message_read(m, "sb", &z, &interactive);
655 if (r < 0)
ebcf1f97 656 return r;
c5f0532f 657
089fb865 658 if (!timezone_is_valid(z, LOG_DEBUG))
5322db06 659 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid or not installed time zone '%s'", z);
ac7019f3 660
539a68e0 661 if (streq_ptr(z, c->zone))
df2d202e 662 return sd_bus_reply_method_return(m, NULL);
c5f0532f 663
c529695e
LP
664 r = bus_verify_polkit_async(
665 m,
666 CAP_SYS_TIME,
667 "org.freedesktop.timedate1.set-timezone",
403ed0e5 668 NULL,
c529695e
LP
669 interactive,
670 UID_INVALID,
671 &c->polkit_registry,
672 error);
40ca29a1 673 if (r < 0)
ebcf1f97 674 return r;
40ca29a1 675 if (r == 0)
6fc60278 676 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
6ffe5e37 677
539a68e0
YW
678 r = free_and_strdup(&c->zone, z);
679 if (r < 0)
680 return r;
681
40ca29a1
LP
682 /* 1. Write new configuration file */
683 r = context_write_data_timezone(c);
684 if (r < 0) {
da927ba9 685 log_error_errno(r, "Failed to set time zone: %m");
10a87006 686 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
40ca29a1 687 }
6ffe5e37 688
8a50b96f
LP
689 /* 2. Make glibc notice the new timezone */
690 tzset();
691
692 /* 3. Tell the kernel our timezone */
2a7ff45f
LP
693 r = clock_set_timezone(NULL);
694 if (r < 0)
695 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
6ffe5e37 696
40ca29a1
LP
697 if (c->local_rtc) {
698 struct timespec ts;
e0f691e1 699 struct tm tm;
c5f0532f 700
8a50b96f 701 /* 4. Sync RTC from system clock, with the new delta */
40ca29a1 702 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
e0f691e1 703 assert_se(localtime_r(&ts.tv_sec, &tm));
2a7ff45f 704
e0f691e1 705 r = clock_set_hwclock(&tm);
2a7ff45f
LP
706 if (r < 0)
707 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 708 }
c5f0532f 709
40ca29a1 710 log_struct(LOG_INFO,
2b044526 711 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
40ca29a1 712 "TIMEZONE=%s", c->zone,
8a50b96f
LP
713 "TIMEZONE_SHORTNAME=%s", tzname[daylight],
714 "DAYLIGHT=%i", daylight,
a1230ff9 715 LOG_MESSAGE("Changed time zone to '%s' (%s).", c->zone, tzname[daylight]));
c5f0532f 716
ad7fb943
ZJS
717 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
718 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone",
719 NULL);
c5f0532f 720
df2d202e 721 return sd_bus_reply_method_return(m, NULL);
c5f0532f
LP
722}
723
19070062 724static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102d8f81 725 int lrtc, fix_system, interactive;
40ca29a1
LP
726 Context *c = userdata;
727 struct timespec ts;
f401e48c
LP
728 int r;
729
40ca29a1
LP
730 assert(m);
731 assert(c);
f401e48c 732
40ca29a1
LP
733 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
734 if (r < 0)
ebcf1f97 735 return r;
f401e48c 736
2be6c475 737 if (lrtc == c->local_rtc && !fix_system)
df2d202e 738 return sd_bus_reply_method_return(m, NULL);
f401e48c 739
c529695e
LP
740 r = bus_verify_polkit_async(
741 m,
742 CAP_SYS_TIME,
743 "org.freedesktop.timedate1.set-local-rtc",
403ed0e5 744 NULL,
c529695e
LP
745 interactive,
746 UID_INVALID,
747 &c->polkit_registry,
748 error);
40ca29a1 749 if (r < 0)
ebcf1f97 750 return r;
40ca29a1
LP
751 if (r == 0)
752 return 1;
f401e48c 753
2be6c475
YW
754 if (lrtc != c->local_rtc) {
755 c->local_rtc = lrtc;
f401e48c 756
2be6c475
YW
757 /* 1. Write new configuration file */
758 r = context_write_data_local_rtc(c);
759 if (r < 0) {
760 log_error_errno(r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
761 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
762 }
40ca29a1 763 }
f401e48c 764
40ca29a1 765 /* 2. Tell the kernel our timezone */
2a7ff45f
LP
766 r = clock_set_timezone(NULL);
767 if (r < 0)
768 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
f401e48c 769
40ca29a1
LP
770 /* 3. Synchronize clocks */
771 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
f401e48c 772
40ca29a1
LP
773 if (fix_system) {
774 struct tm tm;
f401e48c 775
2a7ff45f 776 /* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
56b0ef2f 777 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
72edcff5 778
2a7ff45f
LP
779 /* Override the main fields of struct tm, but not the timezone fields */
780 r = clock_get_hwclock(&tm);
781 if (r < 0)
782 log_debug_errno(r, "Failed to get hardware clock, ignoring: %m");
783 else {
784 /* And set the system clock with this */
df49792e 785 ts.tv_sec = mktime_or_timegm(&tm, !c->local_rtc);
2076cf88 786
2a7ff45f
LP
787 if (clock_settime(CLOCK_REALTIME, &ts) < 0)
788 log_debug_errno(errno, "Failed to update system clock, ignoring: %m");
f401e48c
LP
789 }
790
40ca29a1 791 } else {
e46acb79 792 struct tm tm;
f401e48c 793
40ca29a1 794 /* Sync RTC from system clock */
56b0ef2f 795 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
2076cf88 796
e46acb79 797 r = clock_set_hwclock(&tm);
2a7ff45f
LP
798 if (r < 0)
799 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 800 }
2076cf88 801
40ca29a1 802 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
2076cf88 803
ad7fb943
ZJS
804 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
805 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC",
806 NULL);
2076cf88 807
df2d202e 808 return sd_bus_reply_method_return(m, NULL);
40ca29a1 809}
2076cf88 810
19070062 811static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
5d280742 812 sd_bus *bus = sd_bus_message_get_bus(m);
dfc5c4f2 813 char buf[FORMAT_TIMESTAMP_MAX];
5d280742 814 int relative, interactive, r;
40ca29a1
LP
815 Context *c = userdata;
816 int64_t utc;
817 struct timespec ts;
2479df30 818 usec_t start;
e46acb79 819 struct tm tm;
2076cf88 820
40ca29a1
LP
821 assert(m);
822 assert(c);
2076cf88 823
b4356b57
YW
824 if (c->slot_job_removed)
825 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Previous request is not finished, refusing.");
826
5d280742
YW
827 r = context_update_ntp_status(c, bus, m);
828 if (r < 0)
829 return sd_bus_error_set_errnof(error, r, "Failed to update context: %m");
830
831 if (context_ntp_service_is_active(c) > 0)
e9e5ea88 832 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
82d115d9 833
6829cec4
SL
834 /* this only gets used if dbus does not provide a timestamp */
835 start = now(CLOCK_MONOTONIC);
836
40ca29a1
LP
837 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
838 if (r < 0)
ebcf1f97 839 return r;
2076cf88 840
40ca29a1 841 if (!relative && utc <= 0)
e9e5ea88 842 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
2076cf88 843
40ca29a1 844 if (relative && utc == 0)
df2d202e 845 return sd_bus_reply_method_return(m, NULL);
2076cf88 846
40ca29a1
LP
847 if (relative) {
848 usec_t n, x;
f401e48c 849
40ca29a1
LP
850 n = now(CLOCK_REALTIME);
851 x = n + utc;
f401e48c 852
40ca29a1
LP
853 if ((utc > 0 && x < n) ||
854 (utc < 0 && x > n))
e9e5ea88 855 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
f401e48c 856
40ca29a1
LP
857 timespec_store(&ts, x);
858 } else
859 timespec_store(&ts, (usec_t) utc);
2076cf88 860
c529695e
LP
861 r = bus_verify_polkit_async(
862 m,
863 CAP_SYS_TIME,
864 "org.freedesktop.timedate1.set-time",
403ed0e5 865 NULL,
c529695e
LP
866 interactive,
867 UID_INVALID,
868 &c->polkit_registry,
869 error);
40ca29a1 870 if (r < 0)
ebcf1f97 871 return r;
40ca29a1
LP
872 if (r == 0)
873 return 1;
874
2479df30
SL
875 /* adjust ts for time spent in program */
876 r = sd_bus_message_get_monotonic_usec(m, &start);
6829cec4 877 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
2479df30
SL
878 if (r < 0 && r != -ENODATA)
879 return r;
6829cec4
SL
880
881 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
2479df30 882
40ca29a1
LP
883 /* Set system clock */
884 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
56f64d95 885 log_error_errno(errno, "Failed to set local time: %m");
ebcf1f97 886 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
40ca29a1 887 }
2076cf88 888
40ca29a1 889 /* Sync down to RTC */
56b0ef2f 890 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
2a7ff45f 891
e46acb79 892 r = clock_set_hwclock(&tm);
2a7ff45f
LP
893 if (r < 0)
894 log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
f401e48c 895
40ca29a1 896 log_struct(LOG_INFO,
2b044526 897 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
de0671ee 898 "REALTIME="USEC_FMT, timespec_load(&ts),
dfc5c4f2 899 LOG_MESSAGE("Changed local time to %s", strnull(format_timestamp(buf, sizeof(buf), timespec_load(&ts)))));
f401e48c 900
df2d202e 901 return sd_bus_reply_method_return(m, NULL);
40ca29a1 902}
f401e48c 903
19070062 904static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
cf3872bd 905 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
5d280742 906 sd_bus *bus = sd_bus_message_get_bus(m);
40ca29a1 907 Context *c = userdata;
5d280742 908 UnitStatusInfo *u;
ad7fb943 909 const UnitStatusInfo *selected = NULL;
5d280742 910 int enable, interactive, q, r;
f401e48c 911
19070062 912 assert(m);
5d280742 913 assert(bus);
19070062
LP
914 assert(c);
915
5d280742 916 r = sd_bus_message_read(m, "bb", &enable, &interactive);
40ca29a1 917 if (r < 0)
ebcf1f97 918 return r;
f401e48c 919
5d280742
YW
920 r = context_update_ntp_status(c, bus, m);
921 if (r < 0)
922 return r;
923
924 if (context_ntp_service_exists(c) <= 0)
925 return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
f401e48c 926
c529695e
LP
927 r = bus_verify_polkit_async(
928 m,
929 CAP_SYS_TIME,
930 "org.freedesktop.timedate1.set-ntp",
403ed0e5 931 NULL,
c529695e
LP
932 interactive,
933 UID_INVALID,
934 &c->polkit_registry,
935 error);
40ca29a1 936 if (r < 0)
ebcf1f97 937 return r;
40ca29a1
LP
938 if (r == 0)
939 return 1;
f401e48c 940
cf3872bd
YW
941 /* This method may be called frequently. Forget the previous job if it has not completed yet. */
942 LIST_FOREACH(units, u, c->units)
943 u->path = mfree(u->path);
944
945 if (!c->slot_job_removed) {
43fe4f76 946 r = bus_match_signal_async(
cf3872bd
YW
947 bus,
948 &slot,
43fe4f76 949 bus_systemd_mgr,
cf3872bd
YW
950 "JobRemoved",
951 match_job_removed, NULL, c);
952 if (r < 0)
953 return r;
954 }
955
0957790b 956 if (enable)
5d280742 957 LIST_FOREACH(units, u, c->units) {
0957790b 958 bool enable_this_one = !selected;
5d280742 959
5d280742
YW
960 if (!streq(u->load_state, "loaded"))
961 continue;
962
0957790b 963 r = unit_enable_or_disable(u, bus, error, enable_this_one);
5d280742 964 if (r < 0)
0957790b
ZJS
965 /* If enablement failed, don't start this unit. */
966 enable_this_one = false;
5d280742 967
0957790b
ZJS
968 r = unit_start_or_stop(u, bus, error, enable_this_one);
969 if (r < 0)
970 log_unit_warning_errno(u, r, "Failed to %s %sd NTP unit, ignoring: %m",
971 enable_this_one ? "start" : "stop",
972 enable_disable(enable_this_one));
973 if (enable_this_one)
974 selected = u;
5d280742 975 }
3af0a96c 976 else
5d280742 977 LIST_FOREACH(units, u, c->units) {
0957790b 978 if (!streq(u->load_state, "loaded"))
5d280742
YW
979 continue;
980
0957790b
ZJS
981 q = unit_enable_or_disable(u, bus, error, false);
982 if (q < 0)
983 r = q;
984
985 q = unit_start_or_stop(u, bus, error, false);
986 if (q < 0)
987 r = q;
5d280742 988 }
40ca29a1 989
40ca29a1 990 if (r < 0)
ebcf1f97 991 return r;
ad7fb943
ZJS
992 if (enable && !selected)
993 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No NTP service found to enable.");
f401e48c 994
cf3872bd
YW
995 if (slot)
996 c->slot_job_removed = TAKE_PTR(slot);
997
ad7fb943
ZJS
998 if (selected)
999 log_info("Set NTP to enabled (%s).", selected->name);
1000 else
1001 log_info("Set NTP to disabled.");
f401e48c 1002
df2d202e 1003 return sd_bus_reply_method_return(m, NULL);
f401e48c
LP
1004}
1005
2cf0b2fe
NT
1006static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1007 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1008 _cleanup_strv_free_ char **zones = NULL;
1009 int r;
1010
1011 assert(m);
1012
1013 r = get_timezones(&zones);
1014 if (r < 0)
1015 return sd_bus_error_set_errnof(error, r, "Failed to read list of time zones: %m");
1016
1017 r = sd_bus_message_new_method_return(m, &reply);
1018 if (r < 0)
1019 return r;
1020
1021 r = sd_bus_message_append_strv(reply, zones);
1022 if (r < 0)
1023 return r;
1024
1025 return sd_bus_send(NULL, reply, NULL);
1026}
1027
40ca29a1
LP
1028static const sd_bus_vtable timedate_vtable[] = {
1029 SD_BUS_VTABLE_START(0),
599c99ee 1030
40ca29a1 1031 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
82d115d9 1032 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
5d280742
YW
1033 SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0),
1034 SD_BUS_PROPERTY("NTP", "b", property_get_ntp, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
03cc26dd
LP
1035 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
1036 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
6fc60278 1037 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
599c99ee
ZJS
1038
1039 SD_BUS_METHOD_WITH_NAMES("SetTime",
1040 "xbb",
1041 SD_BUS_PARAM(usec_utc)
1042 SD_BUS_PARAM(relative)
1043 SD_BUS_PARAM(interactive),
1044 NULL,,
1045 method_set_time,
1046 SD_BUS_VTABLE_UNPRIVILEGED),
1047 SD_BUS_METHOD_WITH_NAMES("SetTimezone",
1048 "sb",
1049 SD_BUS_PARAM(timezone)
1050 SD_BUS_PARAM(interactive),
1051 NULL,,
1052 method_set_timezone,
1053 SD_BUS_VTABLE_UNPRIVILEGED),
1054 SD_BUS_METHOD_WITH_NAMES("SetLocalRTC",
1055 "bbb",
1056 SD_BUS_PARAM(local_rtc)
1057 SD_BUS_PARAM(fix_system)
1058 SD_BUS_PARAM(interactive),
1059 NULL,,
1060 method_set_local_rtc,
1061 SD_BUS_VTABLE_UNPRIVILEGED),
1062 SD_BUS_METHOD_WITH_NAMES("SetNTP",
1063 "bb",
1064 SD_BUS_PARAM(use_ntp)
1065 SD_BUS_PARAM(interactive),
1066 NULL,,
1067 method_set_ntp,
1068 SD_BUS_VTABLE_UNPRIVILEGED),
1069 SD_BUS_METHOD_WITH_NAMES("ListTimezones",
1070 NULL,,
1071 "as",
1072 SD_BUS_PARAM(timezones),
1073 method_list_timezones,
1074 SD_BUS_VTABLE_UNPRIVILEGED),
1075
40ca29a1
LP
1076 SD_BUS_VTABLE_END,
1077};
1078
c4b7d95c
ZJS
1079const BusObjectImplementation manager_object = {
1080 "/org/freedesktop/timedate1",
1081 "org.freedesktop.timedate1",
1082 .vtables = BUS_VTABLES(timedate_vtable),
1083};
1084
40ca29a1 1085static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 1086 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c
LP
1087 int r;
1088
40ca29a1
LP
1089 assert(c);
1090 assert(event);
f401e48c
LP
1091 assert(_bus);
1092
76b54375 1093 r = sd_bus_default_system(&bus);
f647962d
MS
1094 if (r < 0)
1095 return log_error_errno(r, "Failed to get system bus connection: %m");
f401e48c 1096
c4b7d95c 1097 r = bus_add_implementation(bus, &manager_object, c);
f647962d 1098 if (r < 0)
c4b7d95c 1099 return r;
f401e48c 1100
ac9f55ed
LP
1101 r = bus_log_control_api_register(bus);
1102 if (r < 0)
1103 return r;
1104
0c0b9306 1105 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
f647962d 1106 if (r < 0)
0c0b9306 1107 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1108
40ca29a1 1109 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1110 if (r < 0)
1111 return log_error_errno(r, "Failed to attach bus to event loop: %m");
f401e48c 1112
1cc6c93a 1113 *_bus = TAKE_PTR(bus);
f401e48c 1114
40ca29a1 1115 return 0;
f401e48c
LP
1116}
1117
1f47bc33
YW
1118static int run(int argc, char *argv[]) {
1119 _cleanup_(context_clear) Context context = {};
4afd3348
LP
1120 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1121 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c 1122 int r;
f401e48c 1123
d2acb93d 1124 log_setup();
f401e48c 1125
fc021a5b
ZJS
1126 r = service_parse_argv("systemd-timedated.service",
1127 "Manage the system clock and timezone and NTP enablement.",
c4b7d95c
ZJS
1128 BUS_IMPLEMENTATIONS(&manager_object,
1129 &log_control_object),
fc021a5b
ZJS
1130 argc, argv);
1131 if (r <= 0)
1132 return r;
4c12626c 1133
fc021a5b 1134 umask(0022);
f401e48c 1135
754f0269
YW
1136 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1137
afc6adb5 1138 r = sd_event_default(&event);
1f47bc33
YW
1139 if (r < 0)
1140 return log_error_errno(r, "Failed to allocate event loop: %m");
f401e48c 1141
754f0269
YW
1142 (void) sd_event_set_watchdog(event, true);
1143
1144 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1f47bc33
YW
1145 if (r < 0)
1146 return log_error_errno(r, "Failed to install SIGINT handler: %m");
754f0269
YW
1147
1148 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1f47bc33
YW
1149 if (r < 0)
1150 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1151
40ca29a1 1152 r = connect_bus(&context, event, &bus);
f401e48c 1153 if (r < 0)
1f47bc33 1154 return r;
f401e48c 1155
dc751688 1156 (void) sd_bus_negotiate_timestamp(bus, true);
2479df30 1157
40ca29a1 1158 r = context_read_data(&context);
1f47bc33
YW
1159 if (r < 0)
1160 return log_error_errno(r, "Failed to read time zone data: %m");
c5f0532f 1161
5d280742
YW
1162 r = context_parse_ntp_services(&context);
1163 if (r < 0)
1f47bc33 1164 return r;
ad740100 1165
37224a5f 1166 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
1f47bc33
YW
1167 if (r < 0)
1168 return log_error_errno(r, "Failed to run event loop: %m");
f401e48c 1169
1f47bc33 1170 return 0;
f401e48c 1171}
1f47bc33
YW
1172
1173DEFINE_MAIN_FUNCTION(run);