]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timedate/timedated.c
timedated: support multiple NTP services
[thirdparty/systemd.git] / src / timedate / timedated.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6 ***/
7
8 #include <errno.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include "sd-bus.h"
13 #include "sd-event.h"
14 #include "sd-messages.h"
15
16 #include "alloc-util.h"
17 #include "bus-common-errors.h"
18 #include "bus-error.h"
19 #include "bus-util.h"
20 #include "clock-util.h"
21 #include "def.h"
22 #include "fileio-label.h"
23 #include "fs-util.h"
24 #include "hashmap.h"
25 #include "list.h"
26 #include "path-util.h"
27 #include "selinux-util.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "unit-def.h"
31 #include "unit-name.h"
32 #include "user-util.h"
33 #include "util.h"
34
35 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
36 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
37
38 typedef struct UnitStatusInfo {
39 char *name;
40 char *load_state;
41 char *unit_file_state;
42 char *active_state;
43
44 LIST_FIELDS(struct UnitStatusInfo, units);
45 } UnitStatusInfo;
46
47 typedef struct Context {
48 char *zone;
49 bool local_rtc;
50 Hashmap *polkit_registry;
51
52 LIST_HEAD(UnitStatusInfo, units);
53 } Context;
54
55 static void unit_status_info_clear(UnitStatusInfo *p) {
56 assert(p);
57
58 p->load_state = mfree(p->load_state);
59 p->unit_file_state = mfree(p->unit_file_state);
60 p->active_state = mfree(p->active_state);
61 }
62
63 static void unit_status_info_free(UnitStatusInfo *p) {
64 assert(p);
65
66 unit_status_info_clear(p);
67 free(p->name);
68 free(p);
69 }
70
71 static void context_free(Context *c) {
72 UnitStatusInfo *p;
73
74 assert(c);
75
76 free(c->zone);
77 bus_verify_polkit_async_registry_free(c->polkit_registry);
78
79 while ((p = c->units)) {
80 LIST_REMOVE(units, c->units, p);
81 unit_status_info_free(p);
82 }
83 }
84
85 static int context_add_ntp_service(Context *c, const char *s) {
86 _cleanup_free_ char *name = NULL;
87 UnitStatusInfo *u;
88
89 if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
90 return -EINVAL;
91
92 /* Do not add this if it is already listed */
93 LIST_FOREACH(units, u, c->units)
94 if (streq(u->name, s))
95 return 0;
96
97 u = new0(UnitStatusInfo, 1);
98 if (!u)
99 return -ENOMEM;
100
101 u->name = strdup(s);
102 if (!u->name) {
103 free(u);
104 return -ENOMEM;
105 }
106
107 LIST_APPEND(units, c->units, u);
108
109 return 0;
110 }
111
112 static int context_parse_ntp_services(Context *c) {
113 const char *env, *p;
114 int r;
115
116 assert(c);
117
118 env = getenv("SYSTEMD_TIMEDATED_NTP_SERVICES");
119 if (!env) {
120 r = context_add_ntp_service(c, "systemd-timesyncd.service");
121 if (r < 0)
122 log_warning_errno(r, "Failed to add NTP service \"systemd-timesyncd.service\", ignoring: %m");
123
124 return 0;
125 }
126
127 for (p = env;;) {
128 _cleanup_free_ char *word = NULL;
129
130 r = extract_first_word(&p, &word, ":", 0);
131 if (r == 0)
132 break;
133 if (r == -ENOMEM)
134 return log_oom();
135 if (r < 0) {
136 log_error("Invalid syntax, ignoring: %s", env);
137 break;
138 }
139
140 r = context_add_ntp_service(c, word);
141 if (r < 0)
142 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
143 }
144
145 return 0;
146 }
147
148 static int context_ntp_service_is_active(Context *c) {
149 UnitStatusInfo *info;
150 int count = 0;
151
152 assert(c);
153
154 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
155
156 LIST_FOREACH(units, info, c->units)
157 count += streq_ptr(info->active_state, "active");
158
159 return count;
160 }
161
162 static int context_ntp_service_is_enabled(Context *c) {
163 UnitStatusInfo *info;
164 int count = 0;
165
166 assert(c);
167
168 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
169
170 LIST_FOREACH(units, info, c->units)
171 count += STRPTR_IN_SET(info->unit_file_state, "enabled", "enabled-runtime");
172
173 return count;
174 }
175
176 static int context_ntp_service_exists(Context *c) {
177 UnitStatusInfo *info;
178 int count = 0;
179
180 assert(c);
181
182 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
183
184 LIST_FOREACH(units, info, c->units)
185 count += streq_ptr(info->load_state, "loaded");
186
187 return count;
188 }
189
190 static int context_read_data(Context *c) {
191 _cleanup_free_ char *t = NULL;
192 int r;
193
194 assert(c);
195
196 r = get_timezone(&t);
197 if (r == -EINVAL)
198 log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
199 else if (r < 0)
200 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
201
202 free_and_replace(c->zone, t);
203
204 c->local_rtc = clock_is_localtime(NULL) > 0;
205
206 return 0;
207 }
208
209 static int context_write_data_timezone(Context *c) {
210 _cleanup_free_ char *p = NULL;
211 int r = 0;
212
213 assert(c);
214
215 if (isempty(c->zone)) {
216 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
217 r = -errno;
218
219 return r;
220 }
221
222 p = strappend("../usr/share/zoneinfo/", c->zone);
223 if (!p)
224 return log_oom();
225
226 r = symlink_atomic(p, "/etc/localtime");
227 if (r < 0)
228 return r;
229
230 return 0;
231 }
232
233 static int context_write_data_local_rtc(Context *c) {
234 int r;
235 _cleanup_free_ char *s = NULL, *w = NULL;
236
237 assert(c);
238
239 r = read_full_file("/etc/adjtime", &s, NULL);
240 if (r < 0) {
241 if (r != -ENOENT)
242 return r;
243
244 if (!c->local_rtc)
245 return 0;
246
247 w = strdup(NULL_ADJTIME_LOCAL);
248 if (!w)
249 return -ENOMEM;
250 } else {
251 char *p;
252 const char *e = "\n"; /* default if there is less than 3 lines */
253 const char *prepend = "";
254 size_t a, b;
255
256 p = strchrnul(s, '\n');
257 if (*p == '\0')
258 /* only one line, no \n terminator */
259 prepend = "\n0\n";
260 else if (p[1] == '\0') {
261 /* only one line, with \n terminator */
262 ++p;
263 prepend = "0\n";
264 } else {
265 p = strchr(p+1, '\n');
266 if (!p) {
267 /* only two lines, no \n terminator */
268 prepend = "\n";
269 p = s + strlen(s);
270 } else {
271 char *end;
272 /* third line might have a \n terminator or not */
273 p++;
274 end = strchr(p, '\n');
275 /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */
276 if (end)
277 e = end;
278 }
279 }
280
281 a = p - s;
282 b = strlen(e);
283
284 w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1);
285 if (!w)
286 return -ENOMEM;
287
288 *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
289
290 if (streq(w, NULL_ADJTIME_UTC)) {
291 if (unlink("/etc/adjtime") < 0)
292 if (errno != ENOENT)
293 return -errno;
294
295 return 0;
296 }
297 }
298
299 mac_selinux_init();
300 return write_string_file_atomic_label("/etc/adjtime", w);
301 }
302
303 static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) {
304 static const struct bus_properties_map map[] = {
305 { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
306 { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
307 { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
308 {}
309 };
310 static sd_bus_message *_m = NULL;
311 UnitStatusInfo *u;
312 int r;
313
314 assert(c);
315 assert(bus);
316
317 /* Suppress multiple call of context_update_ntp_status() within single DBus transaction. */
318 if (m && m == _m)
319 return 0;
320
321 _m = m;
322
323 LIST_FOREACH(units, u, c->units) {
324 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
325 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
326 _cleanup_free_ char *path = NULL;
327
328 unit_status_info_clear(u);
329
330 path = unit_dbus_path_from_name(u->name);
331 if (!path)
332 return -ENOMEM;
333
334 r = bus_map_all_properties(
335 bus,
336 "org.freedesktop.systemd1",
337 path,
338 map,
339 BUS_MAP_STRDUP,
340 &error,
341 NULL,
342 u);
343 if (r < 0)
344 return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
345 }
346
347 return 0;
348 }
349
350 static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
351 int r;
352
353 assert(u);
354 assert(bus);
355 assert(error);
356
357 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
358
359 if (streq(u->active_state, "active") == start)
360 return 0;
361
362 r = sd_bus_call_method(
363 bus,
364 "org.freedesktop.systemd1",
365 "/org/freedesktop/systemd1",
366 "org.freedesktop.systemd1.Manager",
367 start ? "StartUnit" : "StopUnit",
368 error,
369 NULL,
370 "ss",
371 u->name,
372 "replace");
373 if (r < 0)
374 return r;
375
376 return 0;
377 }
378
379 static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool enable) {
380 int r;
381
382 assert(u);
383 assert(bus);
384 assert(error);
385
386 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
387
388 if (streq(u->unit_file_state, "enabled") == enable)
389 return 0;
390
391 if (enable)
392 r = sd_bus_call_method(
393 bus,
394 "org.freedesktop.systemd1",
395 "/org/freedesktop/systemd1",
396 "org.freedesktop.systemd1.Manager",
397 "EnableUnitFiles",
398 error,
399 NULL,
400 "asbb", 1,
401 u->name,
402 false, true);
403 else
404 r = sd_bus_call_method(
405 bus,
406 "org.freedesktop.systemd1",
407 "/org/freedesktop/systemd1",
408 "org.freedesktop.systemd1.Manager",
409 "DisableUnitFiles",
410 error,
411 NULL,
412 "asb", 1,
413 u->name,
414 false);
415 if (r < 0)
416 return r;
417
418 r = sd_bus_call_method(
419 bus,
420 "org.freedesktop.systemd1",
421 "/org/freedesktop/systemd1",
422 "org.freedesktop.systemd1.Manager",
423 "Reload",
424 error,
425 NULL,
426 NULL);
427 if (r < 0)
428 return r;
429 return 0;
430 }
431
432 static int property_get_rtc_time(
433 sd_bus *bus,
434 const char *path,
435 const char *interface,
436 const char *property,
437 sd_bus_message *reply,
438 void *userdata,
439 sd_bus_error *error) {
440
441 struct tm tm;
442 usec_t t;
443 int r;
444
445 zero(tm);
446 r = clock_get_hwclock(&tm);
447 if (r == -EBUSY) {
448 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
449 t = 0;
450 } else if (r == -ENOENT) {
451 log_debug("/dev/rtc not found.");
452 t = 0; /* no RTC found */
453 } else if (r < 0)
454 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
455 else
456 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
457
458 return sd_bus_message_append(reply, "t", t);
459 }
460
461 static int property_get_time(
462 sd_bus *bus,
463 const char *path,
464 const char *interface,
465 const char *property,
466 sd_bus_message *reply,
467 void *userdata,
468 sd_bus_error *error) {
469
470 return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
471 }
472
473 static int property_get_ntp_sync(
474 sd_bus *bus,
475 const char *path,
476 const char *interface,
477 const char *property,
478 sd_bus_message *reply,
479 void *userdata,
480 sd_bus_error *error) {
481
482 return sd_bus_message_append(reply, "b", ntp_synced());
483 }
484
485 static int property_get_can_ntp(
486 sd_bus *bus,
487 const char *path,
488 const char *interface,
489 const char *property,
490 sd_bus_message *reply,
491 void *userdata,
492 sd_bus_error *error) {
493
494 Context *c = userdata;
495 int r;
496
497 assert(c);
498 assert(bus);
499 assert(property);
500 assert(reply);
501 assert(error);
502
503 r = context_update_ntp_status(c, bus, reply);
504 if (r < 0)
505 return r;
506
507 return sd_bus_message_append(reply, "b", context_ntp_service_exists(c) > 0);
508 }
509
510 static int property_get_ntp(
511 sd_bus *bus,
512 const char *path,
513 const char *interface,
514 const char *property,
515 sd_bus_message *reply,
516 void *userdata,
517 sd_bus_error *error) {
518
519 Context *c = userdata;
520 int r;
521
522 assert(c);
523 assert(bus);
524 assert(property);
525 assert(reply);
526 assert(error);
527
528 r = context_update_ntp_status(c, bus, reply);
529 if (r < 0)
530 return r;
531
532 return sd_bus_message_append(reply, "b", context_ntp_service_is_active(c) > 0);
533 }
534
535 static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
536 Context *c = userdata;
537 const char *z;
538 int interactive;
539 char *t;
540 int r;
541
542 assert(m);
543 assert(c);
544
545 r = sd_bus_message_read(m, "sb", &z, &interactive);
546 if (r < 0)
547 return r;
548
549 if (!timezone_is_valid(z))
550 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
551
552 if (streq_ptr(z, c->zone))
553 return sd_bus_reply_method_return(m, NULL);
554
555 r = bus_verify_polkit_async(
556 m,
557 CAP_SYS_TIME,
558 "org.freedesktop.timedate1.set-timezone",
559 NULL,
560 interactive,
561 UID_INVALID,
562 &c->polkit_registry,
563 error);
564 if (r < 0)
565 return r;
566 if (r == 0)
567 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
568
569 t = strdup(z);
570 if (!t)
571 return -ENOMEM;
572
573 free(c->zone);
574 c->zone = t;
575
576 /* 1. Write new configuration file */
577 r = context_write_data_timezone(c);
578 if (r < 0) {
579 log_error_errno(r, "Failed to set time zone: %m");
580 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
581 }
582
583 /* 2. Tell the kernel our timezone */
584 clock_set_timezone(NULL);
585
586 if (c->local_rtc) {
587 struct timespec ts;
588 struct tm *tm;
589
590 /* 3. Sync RTC from system clock, with the new delta */
591 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
592 assert_se(tm = localtime(&ts.tv_sec));
593 clock_set_hwclock(tm);
594 }
595
596 log_struct(LOG_INFO,
597 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
598 "TIMEZONE=%s", c->zone,
599 LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
600 NULL);
601
602 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
603
604 return sd_bus_reply_method_return(m, NULL);
605 }
606
607 static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
608 int lrtc, fix_system, interactive;
609 Context *c = userdata;
610 struct timespec ts;
611 int r;
612
613 assert(m);
614 assert(c);
615
616 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
617 if (r < 0)
618 return r;
619
620 if (lrtc == c->local_rtc)
621 return sd_bus_reply_method_return(m, NULL);
622
623 r = bus_verify_polkit_async(
624 m,
625 CAP_SYS_TIME,
626 "org.freedesktop.timedate1.set-local-rtc",
627 NULL,
628 interactive,
629 UID_INVALID,
630 &c->polkit_registry,
631 error);
632 if (r < 0)
633 return r;
634 if (r == 0)
635 return 1;
636
637 c->local_rtc = lrtc;
638
639 /* 1. Write new configuration file */
640 r = context_write_data_local_rtc(c);
641 if (r < 0) {
642 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
643 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m");
644 }
645
646 /* 2. Tell the kernel our timezone */
647 clock_set_timezone(NULL);
648
649 /* 3. Synchronize clocks */
650 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
651
652 if (fix_system) {
653 struct tm tm;
654
655 /* Sync system clock from RTC; first,
656 * initialize the timezone fields of
657 * struct tm. */
658 if (c->local_rtc)
659 tm = *localtime(&ts.tv_sec);
660 else
661 tm = *gmtime(&ts.tv_sec);
662
663 /* Override the main fields of
664 * struct tm, but not the timezone
665 * fields */
666 if (clock_get_hwclock(&tm) >= 0) {
667
668 /* And set the system clock
669 * with this */
670 if (c->local_rtc)
671 ts.tv_sec = mktime(&tm);
672 else
673 ts.tv_sec = timegm(&tm);
674
675 clock_settime(CLOCK_REALTIME, &ts);
676 }
677
678 } else {
679 struct tm *tm;
680
681 /* Sync RTC from system clock */
682 if (c->local_rtc)
683 tm = localtime(&ts.tv_sec);
684 else
685 tm = gmtime(&ts.tv_sec);
686
687 clock_set_hwclock(tm);
688 }
689
690 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
691
692 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
693
694 return sd_bus_reply_method_return(m, NULL);
695 }
696
697 static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
698 sd_bus *bus = sd_bus_message_get_bus(m);
699 int relative, interactive, r;
700 Context *c = userdata;
701 int64_t utc;
702 struct timespec ts;
703 usec_t start;
704 struct tm* tm;
705
706 assert(m);
707 assert(c);
708
709 r = context_update_ntp_status(c, bus, m);
710 if (r < 0)
711 return sd_bus_error_set_errnof(error, r, "Failed to update context: %m");
712
713 if (context_ntp_service_is_active(c) > 0)
714 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
715
716 /* this only gets used if dbus does not provide a timestamp */
717 start = now(CLOCK_MONOTONIC);
718
719 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
720 if (r < 0)
721 return r;
722
723 if (!relative && utc <= 0)
724 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
725
726 if (relative && utc == 0)
727 return sd_bus_reply_method_return(m, NULL);
728
729 if (relative) {
730 usec_t n, x;
731
732 n = now(CLOCK_REALTIME);
733 x = n + utc;
734
735 if ((utc > 0 && x < n) ||
736 (utc < 0 && x > n))
737 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
738
739 timespec_store(&ts, x);
740 } else
741 timespec_store(&ts, (usec_t) utc);
742
743 r = bus_verify_polkit_async(
744 m,
745 CAP_SYS_TIME,
746 "org.freedesktop.timedate1.set-time",
747 NULL,
748 interactive,
749 UID_INVALID,
750 &c->polkit_registry,
751 error);
752 if (r < 0)
753 return r;
754 if (r == 0)
755 return 1;
756
757 /* adjust ts for time spent in program */
758 r = sd_bus_message_get_monotonic_usec(m, &start);
759 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
760 if (r < 0 && r != -ENODATA)
761 return r;
762
763 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
764
765 /* Set system clock */
766 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
767 log_error_errno(errno, "Failed to set local time: %m");
768 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
769 }
770
771 /* Sync down to RTC */
772 if (c->local_rtc)
773 tm = localtime(&ts.tv_sec);
774 else
775 tm = gmtime(&ts.tv_sec);
776 clock_set_hwclock(tm);
777
778 log_struct(LOG_INFO,
779 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
780 "REALTIME="USEC_FMT, timespec_load(&ts),
781 LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
782 NULL);
783
784 return sd_bus_reply_method_return(m, NULL);
785 }
786
787 static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
788 sd_bus *bus = sd_bus_message_get_bus(m);
789 Context *c = userdata;
790 UnitStatusInfo *u;
791 int enable, interactive, q, r;
792
793 assert(m);
794 assert(bus);
795 assert(c);
796
797 r = sd_bus_message_read(m, "bb", &enable, &interactive);
798 if (r < 0)
799 return r;
800
801 r = context_update_ntp_status(c, bus, m);
802 if (r < 0)
803 return r;
804
805 if (context_ntp_service_exists(c) <= 0)
806 return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
807
808 r = bus_verify_polkit_async(
809 m,
810 CAP_SYS_TIME,
811 "org.freedesktop.timedate1.set-ntp",
812 NULL,
813 interactive,
814 UID_INVALID,
815 &c->polkit_registry,
816 error);
817 if (r < 0)
818 return r;
819 if (r == 0)
820 return 1;
821
822 if (!enable)
823 LIST_FOREACH(units, u, c->units) {
824 if (!streq(u->load_state, "loaded"))
825 continue;
826
827 q = unit_enable_or_disable(u, bus, error, enable);
828 if (q < 0)
829 r = q;
830
831 q = unit_start_or_stop(u, bus, error, enable);
832 if (q < 0)
833 r = q;
834 }
835
836 else if (context_ntp_service_is_enabled(c) <= 0)
837 LIST_FOREACH(units, u, c->units) {
838 if (!streq(u->load_state, "loaded"))
839 continue;
840
841 r = unit_enable_or_disable(u, bus, error, enable);
842 if (r < 0)
843 continue;
844
845 r = unit_start_or_stop(u, bus, error, enable);
846 break;
847 }
848
849 else if (context_ntp_service_is_active(c) <= 0)
850 LIST_FOREACH(units, u, c->units) {
851 if (!streq(u->load_state, "loaded") ||
852 !streq(u->unit_file_state, "enabled"))
853 continue;
854
855 r = unit_start_or_stop(u, bus, error, enable);
856 break;
857 }
858
859 if (r < 0)
860 return r;
861
862 log_info("Set NTP to %sd", enable_disable(enable));
863
864 (void) sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
865
866 return sd_bus_reply_method_return(m, NULL);
867 }
868
869 static const sd_bus_vtable timedate_vtable[] = {
870 SD_BUS_VTABLE_START(0),
871 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
872 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
873 SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0),
874 SD_BUS_PROPERTY("NTP", "b", property_get_ntp, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
875 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
876 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
877 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
878 SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
879 SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
880 SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
881 SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
882 SD_BUS_VTABLE_END,
883 };
884
885 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
886 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
887 int r;
888
889 assert(c);
890 assert(event);
891 assert(_bus);
892
893 r = sd_bus_default_system(&bus);
894 if (r < 0)
895 return log_error_errno(r, "Failed to get system bus connection: %m");
896
897 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
898 if (r < 0)
899 return log_error_errno(r, "Failed to register object: %m");
900
901 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
902 if (r < 0)
903 return log_error_errno(r, "Failed to request name: %m");
904
905 r = sd_bus_attach_event(bus, event, 0);
906 if (r < 0)
907 return log_error_errno(r, "Failed to attach bus to event loop: %m");
908
909 *_bus = TAKE_PTR(bus);
910
911 return 0;
912 }
913
914 int main(int argc, char *argv[]) {
915 Context context = {};
916 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
917 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
918 int r;
919
920 log_set_target(LOG_TARGET_AUTO);
921 log_parse_environment();
922 log_open();
923
924 umask(0022);
925
926 if (argc != 1) {
927 log_error("This program takes no arguments.");
928 r = -EINVAL;
929 goto finish;
930 }
931
932 r = sd_event_default(&event);
933 if (r < 0) {
934 log_error_errno(r, "Failed to allocate event loop: %m");
935 goto finish;
936 }
937
938 sd_event_set_watchdog(event, true);
939
940 r = connect_bus(&context, event, &bus);
941 if (r < 0)
942 goto finish;
943
944 (void) sd_bus_negotiate_timestamp(bus, true);
945
946 r = context_read_data(&context);
947 if (r < 0) {
948 log_error_errno(r, "Failed to read time zone data: %m");
949 goto finish;
950 }
951
952 r = context_parse_ntp_services(&context);
953 if (r < 0)
954 goto finish;
955
956 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
957 if (r < 0) {
958 log_error_errno(r, "Failed to run event loop: %m");
959 goto finish;
960 }
961
962 finish:
963 context_free(&context);
964
965 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
966 }