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