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