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