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