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