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