]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
tree-wide: use LIST_CLEAR()
[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"
f4f15635 21#include "clock-util.h"
afaae43b 22#include "conf-files.h"
28db6fbf 23#include "constants.h"
afaae43b 24#include "fd-util.h"
f4f15635 25#include "fileio-label.h"
e4de7287 26#include "fileio.h"
f4f15635 27#include "fs-util.h"
5d280742
YW
28#include "hashmap.h"
29#include "list.h"
1f47bc33 30#include "main-func.h"
0a970718 31#include "memory-util.h"
36dd5ffd 32#include "missing_capability.h"
f4f15635 33#include "path-util.h"
d7b8eec7 34#include "selinux-util.h"
fc021a5b 35#include "service-util.h"
754f0269 36#include "signal-util.h"
5d280742 37#include "string-util.h"
f4f15635 38#include "strv.h"
5d280742
YW
39#include "unit-def.h"
40#include "unit-name.h"
ee104e11 41#include "user-util.h"
f401e48c
LP
42
43#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
44#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
45
afaae43b
ZJS
46#define UNIT_LIST_DIRS (const char* const*) CONF_PATHS_STRV("systemd/ntp-units.d")
47
5d280742
YW
48typedef struct UnitStatusInfo {
49 char *name;
50 char *load_state;
51 char *unit_file_state;
52 char *active_state;
cf3872bd 53 char *path;
5d280742
YW
54
55 LIST_FIELDS(struct UnitStatusInfo, units);
56} UnitStatusInfo;
57
40ca29a1 58typedef struct Context {
d200735e
MS
59 char *zone;
60 bool local_rtc;
40ca29a1 61 Hashmap *polkit_registry;
2770af85 62 sd_bus_message *cache;
5d280742 63
3af0a96c 64 sd_bus_slot *slot_job_removed;
3af0a96c 65
5d280742 66 LIST_HEAD(UnitStatusInfo, units);
40ca29a1 67} Context;
f401e48c 68
5b098203 69#define log_unit_full_errno_zerook(unit, level, error, ...) \
ad7fb943
ZJS
70 ({ \
71 const UnitStatusInfo *_u = (unit); \
5b098203
YW
72 _u ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "UNIT=", _u->name, NULL, NULL, ##__VA_ARGS__) : \
73 log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
ad7fb943
ZJS
74 })
75
5b098203
YW
76#define log_unit_full_errno(unit, level, error, ...) \
77 ({ \
78 int _error = (error); \
79 ASSERT_NON_ZERO(_error); \
80 log_unit_full_errno_zerook(unit, level, _error, ##__VA_ARGS__); \
81 })
82
83#define log_unit_full(unit, level, ...) (void) log_unit_full_errno_zerook(unit, level, 0, ##__VA_ARGS__)
84
85#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, ##__VA_ARGS__)
86#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, ##__VA_ARGS__)
87#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, ##__VA_ARGS__)
88#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, ##__VA_ARGS__)
89#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, ##__VA_ARGS__)
ad7fb943 90
5b098203
YW
91#define log_unit_debug_errno(unit, error, ...) log_unit_full_errno(unit, LOG_DEBUG, error, ##__VA_ARGS__)
92#define log_unit_info_errno(unit, error, ...) log_unit_full_errno(unit, LOG_INFO, error, ##__VA_ARGS__)
93#define log_unit_notice_errno(unit, error, ...) log_unit_full_errno(unit, LOG_NOTICE, error, ##__VA_ARGS__)
94#define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, ##__VA_ARGS__)
95#define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, ##__VA_ARGS__)
ad7fb943 96
5d280742
YW
97static void unit_status_info_clear(UnitStatusInfo *p) {
98 assert(p);
99
100 p->load_state = mfree(p->load_state);
101 p->unit_file_state = mfree(p->unit_file_state);
102 p->active_state = mfree(p->active_state);
103}
104
8b9afa55
YW
105static UnitStatusInfo *unit_status_info_free(UnitStatusInfo *p) {
106 if (!p)
107 return NULL;
5d280742
YW
108
109 unit_status_info_clear(p);
110 free(p->name);
cf3872bd 111 free(p->path);
8b9afa55 112 return mfree(p);
5d280742
YW
113}
114
8b9afa55
YW
115DEFINE_TRIVIAL_CLEANUP_FUNC(UnitStatusInfo*, unit_status_info_free);
116
1f47bc33 117static void context_clear(Context *c) {
40ca29a1 118 assert(c);
f401e48c 119
82d115d9 120 free(c->zone);
36e34057 121 bus_verify_polkit_async_registry_free(c->polkit_registry);
2770af85 122 sd_bus_message_unref(c->cache);
5d280742 123
3af0a96c 124 sd_bus_slot_unref(c->slot_job_removed);
3af0a96c 125
9aad490e 126 LIST_CLEAR(units, c->units, unit_status_info_free);
5d280742
YW
127}
128
ad7fb943 129static int context_add_ntp_service(Context *c, const char *s, const char *source) {
8b9afa55 130 _cleanup_(unit_status_info_freep) UnitStatusInfo *unit = NULL;
5d280742 131
8b9afa55
YW
132 assert(c);
133 assert(s);
134 assert(source);
135
5d280742
YW
136 if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
137 return -EINVAL;
138
139 /* Do not add this if it is already listed */
140 LIST_FOREACH(units, u, c->units)
141 if (streq(u->name, s))
142 return 0;
143
8b9afa55
YW
144 unit = new0(UnitStatusInfo, 1);
145 if (!unit)
5d280742
YW
146 return -ENOMEM;
147
8b9afa55
YW
148 unit->name = strdup(s);
149 if (!unit->name)
5d280742 150 return -ENOMEM;
5d280742 151
8b9afa55
YW
152 LIST_APPEND(units, c->units, unit);
153 log_unit_debug(unit, "added from %s.", source);
154 TAKE_PTR(unit);
5d280742
YW
155
156 return 0;
157}
158
afaae43b 159static int context_parse_ntp_services_from_environment(Context *c) {
5d280742
YW
160 const char *env, *p;
161 int r;
162
163 assert(c);
164
165 env = getenv("SYSTEMD_TIMEDATED_NTP_SERVICES");
afaae43b 166 if (!env)
5d280742 167 return 0;
afaae43b 168
ad7fb943 169 log_debug("Using list of ntp services from environment variable $SYSTEMD_TIMEDATED_NTP_SERVICES=%s.", env);
5d280742
YW
170
171 for (p = env;;) {
172 _cleanup_free_ char *word = NULL;
173
174 r = extract_first_word(&p, &word, ":", 0);
175 if (r == 0)
176 break;
177 if (r == -ENOMEM)
178 return log_oom();
179 if (r < 0) {
180 log_error("Invalid syntax, ignoring: %s", env);
181 break;
182 }
183
ad7fb943 184 r = context_add_ntp_service(c, word, "$SYSTEMD_TIMEDATED_NTP_SERVICES");
5d280742
YW
185 if (r < 0)
186 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
187 }
188
afaae43b
ZJS
189 return 1;
190}
191
192static int context_parse_ntp_services_from_disk(Context *c) {
193 _cleanup_strv_free_ char **files = NULL;
afaae43b
ZJS
194 int r;
195
196 r = conf_files_list_strv(&files, ".list", NULL, CONF_FILES_FILTER_MASKED, UNIT_LIST_DIRS);
197 if (r < 0)
198 return log_error_errno(r, "Failed to enumerate .list files: %m");
199
200 STRV_FOREACH(f, files) {
201 _cleanup_fclose_ FILE *file = NULL;
202
203 log_debug("Reading file '%s'", *f);
204
205 r = fopen_unlocked(*f, "re", &file);
206 if (r < 0) {
207 log_error_errno(r, "Failed to open %s, ignoring: %m", *f);
208 continue;
209 }
210
211 for (;;) {
212 _cleanup_free_ char *line = NULL;
213 const char *word;
214
215 r = read_line(file, LINE_MAX, &line);
216 if (r < 0) {
217 log_error_errno(r, "Failed to read %s, ignoring: %m", *f);
218 continue;
219 }
220 if (r == 0)
221 break;
222
223 word = strstrip(line);
03a81441 224 if (isempty(word) || startswith(word, "#"))
afaae43b
ZJS
225 continue;
226
ad7fb943 227 r = context_add_ntp_service(c, word, *f);
afaae43b
ZJS
228 if (r < 0)
229 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
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
43fe4f76 548 r = bus_call_method(bus, bus_systemd_mgr, "Reload", error, NULL, NULL);
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
c529695e
LP
669 r = bus_verify_polkit_async(
670 m,
671 CAP_SYS_TIME,
672 "org.freedesktop.timedate1.set-timezone",
403ed0e5 673 NULL,
c529695e
LP
674 interactive,
675 UID_INVALID,
676 &c->polkit_registry,
677 error);
40ca29a1 678 if (r < 0)
ebcf1f97 679 return r;
40ca29a1 680 if (r == 0)
6fc60278 681 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
6ffe5e37 682
539a68e0
YW
683 r = free_and_strdup(&c->zone, z);
684 if (r < 0)
685 return r;
686
40ca29a1
LP
687 /* 1. Write new configuration file */
688 r = context_write_data_timezone(c);
689 if (r < 0) {
da927ba9 690 log_error_errno(r, "Failed to set time zone: %m");
10a87006 691 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
40ca29a1 692 }
6ffe5e37 693
8a50b96f
LP
694 /* 2. Make glibc notice the new timezone */
695 tzset();
696
697 /* 3. Tell the kernel our timezone */
2a7ff45f
LP
698 r = clock_set_timezone(NULL);
699 if (r < 0)
700 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
6ffe5e37 701
40ca29a1
LP
702 if (c->local_rtc) {
703 struct timespec ts;
e0f691e1 704 struct tm tm;
c5f0532f 705
8a50b96f 706 /* 4. Sync RTC from system clock, with the new delta */
40ca29a1 707 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
e0f691e1 708 assert_se(localtime_r(&ts.tv_sec, &tm));
2a7ff45f 709
e0f691e1 710 r = clock_set_hwclock(&tm);
2a7ff45f
LP
711 if (r < 0)
712 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 713 }
c5f0532f 714
40ca29a1 715 log_struct(LOG_INFO,
2b044526 716 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
40ca29a1 717 "TIMEZONE=%s", c->zone,
8a50b96f
LP
718 "TIMEZONE_SHORTNAME=%s", tzname[daylight],
719 "DAYLIGHT=%i", daylight,
a1230ff9 720 LOG_MESSAGE("Changed time zone to '%s' (%s).", c->zone, tzname[daylight]));
c5f0532f 721
ad7fb943
ZJS
722 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
723 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone",
724 NULL);
c5f0532f 725
df2d202e 726 return sd_bus_reply_method_return(m, NULL);
c5f0532f
LP
727}
728
19070062 729static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102d8f81 730 int lrtc, fix_system, interactive;
99534007 731 Context *c = ASSERT_PTR(userdata);
40ca29a1 732 struct timespec ts;
f401e48c
LP
733 int r;
734
40ca29a1 735 assert(m);
f401e48c 736
40ca29a1
LP
737 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
738 if (r < 0)
ebcf1f97 739 return r;
f401e48c 740
2be6c475 741 if (lrtc == c->local_rtc && !fix_system)
df2d202e 742 return sd_bus_reply_method_return(m, NULL);
f401e48c 743
c529695e
LP
744 r = bus_verify_polkit_async(
745 m,
746 CAP_SYS_TIME,
747 "org.freedesktop.timedate1.set-local-rtc",
403ed0e5 748 NULL,
c529695e
LP
749 interactive,
750 UID_INVALID,
751 &c->polkit_registry,
752 error);
40ca29a1 753 if (r < 0)
ebcf1f97 754 return r;
40ca29a1
LP
755 if (r == 0)
756 return 1;
f401e48c 757
2be6c475
YW
758 if (lrtc != c->local_rtc) {
759 c->local_rtc = lrtc;
f401e48c 760
2be6c475
YW
761 /* 1. Write new configuration file */
762 r = context_write_data_local_rtc(c);
763 if (r < 0) {
764 log_error_errno(r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
765 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
766 }
40ca29a1 767 }
f401e48c 768
40ca29a1 769 /* 2. Tell the kernel our timezone */
2a7ff45f
LP
770 r = clock_set_timezone(NULL);
771 if (r < 0)
772 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
f401e48c 773
40ca29a1
LP
774 /* 3. Synchronize clocks */
775 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
f401e48c 776
40ca29a1
LP
777 if (fix_system) {
778 struct tm tm;
f401e48c 779
2a7ff45f 780 /* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
56b0ef2f 781 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
72edcff5 782
2a7ff45f
LP
783 /* Override the main fields of struct tm, but not the timezone fields */
784 r = clock_get_hwclock(&tm);
785 if (r < 0)
786 log_debug_errno(r, "Failed to get hardware clock, ignoring: %m");
787 else {
788 /* And set the system clock with this */
df49792e 789 ts.tv_sec = mktime_or_timegm(&tm, !c->local_rtc);
2076cf88 790
2a7ff45f
LP
791 if (clock_settime(CLOCK_REALTIME, &ts) < 0)
792 log_debug_errno(errno, "Failed to update system clock, ignoring: %m");
f401e48c
LP
793 }
794
40ca29a1 795 } else {
e46acb79 796 struct tm tm;
f401e48c 797
40ca29a1 798 /* Sync RTC from system clock */
56b0ef2f 799 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
2076cf88 800
e46acb79 801 r = clock_set_hwclock(&tm);
2a7ff45f
LP
802 if (r < 0)
803 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 804 }
2076cf88 805
40ca29a1 806 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
2076cf88 807
ad7fb943
ZJS
808 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
809 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC",
810 NULL);
2076cf88 811
df2d202e 812 return sd_bus_reply_method_return(m, NULL);
40ca29a1 813}
2076cf88 814
19070062 815static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
5d280742 816 sd_bus *bus = sd_bus_message_get_bus(m);
dfc5c4f2 817 char buf[FORMAT_TIMESTAMP_MAX];
5d280742 818 int relative, interactive, r;
99534007 819 Context *c = ASSERT_PTR(userdata);
40ca29a1
LP
820 int64_t utc;
821 struct timespec ts;
2479df30 822 usec_t start;
e46acb79 823 struct tm tm;
2076cf88 824
40ca29a1 825 assert(m);
2076cf88 826
b4356b57
YW
827 if (c->slot_job_removed)
828 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Previous request is not finished, refusing.");
829
5d280742
YW
830 r = context_update_ntp_status(c, bus, m);
831 if (r < 0)
832 return sd_bus_error_set_errnof(error, r, "Failed to update context: %m");
833
834 if (context_ntp_service_is_active(c) > 0)
e9e5ea88 835 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
82d115d9 836
6829cec4
SL
837 /* this only gets used if dbus does not provide a timestamp */
838 start = now(CLOCK_MONOTONIC);
839
40ca29a1
LP
840 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
841 if (r < 0)
ebcf1f97 842 return r;
2076cf88 843
40ca29a1 844 if (!relative && utc <= 0)
e9e5ea88 845 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
2076cf88 846
40ca29a1 847 if (relative && utc == 0)
df2d202e 848 return sd_bus_reply_method_return(m, NULL);
2076cf88 849
40ca29a1
LP
850 if (relative) {
851 usec_t n, x;
f401e48c 852
40ca29a1
LP
853 n = now(CLOCK_REALTIME);
854 x = n + utc;
f401e48c 855
40ca29a1
LP
856 if ((utc > 0 && x < n) ||
857 (utc < 0 && x > n))
e9e5ea88 858 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
f401e48c 859
40ca29a1
LP
860 timespec_store(&ts, x);
861 } else
862 timespec_store(&ts, (usec_t) utc);
2076cf88 863
c529695e
LP
864 r = bus_verify_polkit_async(
865 m,
866 CAP_SYS_TIME,
867 "org.freedesktop.timedate1.set-time",
403ed0e5 868 NULL,
c529695e
LP
869 interactive,
870 UID_INVALID,
871 &c->polkit_registry,
872 error);
40ca29a1 873 if (r < 0)
ebcf1f97 874 return r;
40ca29a1
LP
875 if (r == 0)
876 return 1;
877
2479df30
SL
878 /* adjust ts for time spent in program */
879 r = sd_bus_message_get_monotonic_usec(m, &start);
6829cec4 880 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
2479df30
SL
881 if (r < 0 && r != -ENODATA)
882 return r;
6829cec4
SL
883
884 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
2479df30 885
40ca29a1
LP
886 /* Set system clock */
887 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
56f64d95 888 log_error_errno(errno, "Failed to set local time: %m");
ebcf1f97 889 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
40ca29a1 890 }
2076cf88 891
40ca29a1 892 /* Sync down to RTC */
56b0ef2f 893 localtime_or_gmtime_r(&ts.tv_sec, &tm, !c->local_rtc);
2a7ff45f 894
e46acb79 895 r = clock_set_hwclock(&tm);
2a7ff45f
LP
896 if (r < 0)
897 log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
f401e48c 898
40ca29a1 899 log_struct(LOG_INFO,
2b044526 900 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
de0671ee 901 "REALTIME="USEC_FMT, timespec_load(&ts),
dfc5c4f2 902 LOG_MESSAGE("Changed local time to %s", strnull(format_timestamp(buf, sizeof(buf), timespec_load(&ts)))));
f401e48c 903
df2d202e 904 return sd_bus_reply_method_return(m, NULL);
40ca29a1 905}
f401e48c 906
19070062 907static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
cf3872bd 908 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
5d280742 909 sd_bus *bus = sd_bus_message_get_bus(m);
99534007 910 Context *c = ASSERT_PTR(userdata);
ad7fb943 911 const UnitStatusInfo *selected = NULL;
5d280742 912 int enable, interactive, q, r;
f401e48c 913
19070062 914 assert(m);
5d280742 915 assert(bus);
19070062 916
5d280742 917 r = sd_bus_message_read(m, "bb", &enable, &interactive);
40ca29a1 918 if (r < 0)
ebcf1f97 919 return r;
f401e48c 920
5d280742
YW
921 r = context_update_ntp_status(c, bus, m);
922 if (r < 0)
923 return r;
924
925 if (context_ntp_service_exists(c) <= 0)
926 return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
f401e48c 927
c529695e
LP
928 r = bus_verify_polkit_async(
929 m,
930 CAP_SYS_TIME,
931 "org.freedesktop.timedate1.set-ntp",
403ed0e5 932 NULL,
c529695e
LP
933 interactive,
934 UID_INVALID,
935 &c->polkit_registry,
936 error);
40ca29a1 937 if (r < 0)
ebcf1f97 938 return r;
40ca29a1
LP
939 if (r == 0)
940 return 1;
f401e48c 941
cf3872bd
YW
942 /* This method may be called frequently. Forget the previous job if it has not completed yet. */
943 LIST_FOREACH(units, u, c->units)
944 u->path = mfree(u->path);
945
946 if (!c->slot_job_removed) {
43fe4f76 947 r = bus_match_signal_async(
cf3872bd
YW
948 bus,
949 &slot,
43fe4f76 950 bus_systemd_mgr,
cf3872bd
YW
951 "JobRemoved",
952 match_job_removed, NULL, c);
953 if (r < 0)
954 return r;
955 }
956
0957790b 957 if (enable)
5d280742 958 LIST_FOREACH(units, u, c->units) {
0957790b 959 bool enable_this_one = !selected;
5d280742 960
5d280742
YW
961 if (!streq(u->load_state, "loaded"))
962 continue;
963
0957790b 964 r = unit_enable_or_disable(u, bus, error, enable_this_one);
5d280742 965 if (r < 0)
0957790b
ZJS
966 /* If enablement failed, don't start this unit. */
967 enable_this_one = false;
5d280742 968
0957790b
ZJS
969 r = unit_start_or_stop(u, bus, error, enable_this_one);
970 if (r < 0)
971 log_unit_warning_errno(u, r, "Failed to %s %sd NTP unit, ignoring: %m",
972 enable_this_one ? "start" : "stop",
973 enable_disable(enable_this_one));
974 if (enable_this_one)
975 selected = u;
5d280742 976 }
3af0a96c 977 else
5d280742 978 LIST_FOREACH(units, u, c->units) {
0957790b 979 if (!streq(u->load_state, "loaded"))
5d280742
YW
980 continue;
981
0957790b
ZJS
982 q = unit_enable_or_disable(u, bus, error, false);
983 if (q < 0)
984 r = q;
985
986 q = unit_start_or_stop(u, bus, error, false);
987 if (q < 0)
988 r = q;
5d280742 989 }
40ca29a1 990
40ca29a1 991 if (r < 0)
ebcf1f97 992 return r;
ad7fb943
ZJS
993 if (enable && !selected)
994 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No NTP service found to enable.");
f401e48c 995
cf3872bd
YW
996 if (slot)
997 c->slot_job_removed = TAKE_PTR(slot);
998
ad7fb943
ZJS
999 if (selected)
1000 log_info("Set NTP to enabled (%s).", selected->name);
1001 else
1002 log_info("Set NTP to disabled.");
f401e48c 1003
df2d202e 1004 return sd_bus_reply_method_return(m, NULL);
f401e48c
LP
1005}
1006
2cf0b2fe
NT
1007static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1008 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1009 _cleanup_strv_free_ char **zones = NULL;
1010 int r;
1011
1012 assert(m);
1013
1014 r = get_timezones(&zones);
1015 if (r < 0)
1016 return sd_bus_error_set_errnof(error, r, "Failed to read list of time zones: %m");
1017
1018 r = sd_bus_message_new_method_return(m, &reply);
1019 if (r < 0)
1020 return r;
1021
1022 r = sd_bus_message_append_strv(reply, zones);
1023 if (r < 0)
1024 return r;
1025
1026 return sd_bus_send(NULL, reply, NULL);
1027}
1028
40ca29a1
LP
1029static const sd_bus_vtable timedate_vtable[] = {
1030 SD_BUS_VTABLE_START(0),
599c99ee 1031
40ca29a1 1032 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
82d115d9 1033 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
5d280742
YW
1034 SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0),
1035 SD_BUS_PROPERTY("NTP", "b", property_get_ntp, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
03cc26dd
LP
1036 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
1037 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
6fc60278 1038 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
599c99ee 1039
008714f8
SS
1040 SD_BUS_METHOD_WITH_ARGS("SetTime",
1041 SD_BUS_ARGS("x", usec_utc, "b", relative, "b", interactive),
1042 SD_BUS_NO_RESULT,
1043 method_set_time,
1044 SD_BUS_VTABLE_UNPRIVILEGED),
1045 SD_BUS_METHOD_WITH_ARGS("SetTimezone",
1046 SD_BUS_ARGS("s", timezone, "b", interactive),
1047 SD_BUS_NO_RESULT,
1048 method_set_timezone,
1049 SD_BUS_VTABLE_UNPRIVILEGED),
1050 SD_BUS_METHOD_WITH_ARGS("SetLocalRTC",
1051 SD_BUS_ARGS("b", local_rtc, "b", fix_system, "b", interactive),
1052 SD_BUS_NO_RESULT,
1053 method_set_local_rtc,
1054 SD_BUS_VTABLE_UNPRIVILEGED),
1055 SD_BUS_METHOD_WITH_ARGS("SetNTP",
1056 SD_BUS_ARGS("b", use_ntp, "b", interactive),
1057 SD_BUS_NO_RESULT,
1058 method_set_ntp,
1059 SD_BUS_VTABLE_UNPRIVILEGED),
1060 SD_BUS_METHOD_WITH_ARGS("ListTimezones",
1061 SD_BUS_NO_ARGS,
1062 SD_BUS_RESULT("as", timezones),
1063 method_list_timezones,
1064 SD_BUS_VTABLE_UNPRIVILEGED),
599c99ee 1065
40ca29a1
LP
1066 SD_BUS_VTABLE_END,
1067};
1068
c4b7d95c
ZJS
1069const BusObjectImplementation manager_object = {
1070 "/org/freedesktop/timedate1",
1071 "org.freedesktop.timedate1",
1072 .vtables = BUS_VTABLES(timedate_vtable),
1073};
1074
40ca29a1 1075static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 1076 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c
LP
1077 int r;
1078
40ca29a1
LP
1079 assert(c);
1080 assert(event);
f401e48c
LP
1081 assert(_bus);
1082
76b54375 1083 r = sd_bus_default_system(&bus);
f647962d
MS
1084 if (r < 0)
1085 return log_error_errno(r, "Failed to get system bus connection: %m");
f401e48c 1086
c4b7d95c 1087 r = bus_add_implementation(bus, &manager_object, c);
f647962d 1088 if (r < 0)
c4b7d95c 1089 return r;
f401e48c 1090
ac9f55ed
LP
1091 r = bus_log_control_api_register(bus);
1092 if (r < 0)
1093 return r;
1094
0c0b9306 1095 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
f647962d 1096 if (r < 0)
0c0b9306 1097 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1098
40ca29a1 1099 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1100 if (r < 0)
1101 return log_error_errno(r, "Failed to attach bus to event loop: %m");
f401e48c 1102
1cc6c93a 1103 *_bus = TAKE_PTR(bus);
f401e48c 1104
40ca29a1 1105 return 0;
f401e48c
LP
1106}
1107
1f47bc33
YW
1108static int run(int argc, char *argv[]) {
1109 _cleanup_(context_clear) Context context = {};
4afd3348
LP
1110 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1111 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c 1112 int r;
f401e48c 1113
d2acb93d 1114 log_setup();
f401e48c 1115
fc021a5b
ZJS
1116 r = service_parse_argv("systemd-timedated.service",
1117 "Manage the system clock and timezone and NTP enablement.",
c4b7d95c
ZJS
1118 BUS_IMPLEMENTATIONS(&manager_object,
1119 &log_control_object),
fc021a5b
ZJS
1120 argc, argv);
1121 if (r <= 0)
1122 return r;
4c12626c 1123
fc021a5b 1124 umask(0022);
f401e48c 1125
754f0269
YW
1126 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1127
afc6adb5 1128 r = sd_event_default(&event);
1f47bc33
YW
1129 if (r < 0)
1130 return log_error_errno(r, "Failed to allocate event loop: %m");
f401e48c 1131
754f0269
YW
1132 (void) sd_event_set_watchdog(event, true);
1133
1134 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1f47bc33
YW
1135 if (r < 0)
1136 return log_error_errno(r, "Failed to install SIGINT handler: %m");
754f0269
YW
1137
1138 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1f47bc33
YW
1139 if (r < 0)
1140 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1141
40ca29a1 1142 r = connect_bus(&context, event, &bus);
f401e48c 1143 if (r < 0)
1f47bc33 1144 return r;
f401e48c 1145
dc751688 1146 (void) sd_bus_negotiate_timestamp(bus, true);
2479df30 1147
40ca29a1 1148 r = context_read_data(&context);
1f47bc33
YW
1149 if (r < 0)
1150 return log_error_errno(r, "Failed to read time zone data: %m");
c5f0532f 1151
5d280742
YW
1152 r = context_parse_ntp_services(&context);
1153 if (r < 0)
1f47bc33 1154 return r;
ad740100 1155
37224a5f 1156 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
1f47bc33
YW
1157 if (r < 0)
1158 return log_error_errno(r, "Failed to run event loop: %m");
f401e48c 1159
1f47bc33 1160 return 0;
f401e48c 1161}
1f47bc33
YW
1162
1163DEFINE_MAIN_FUNCTION(run);