]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timedate/timedated.c
Merge pull request #9684 from yuwata/fix-9672
[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 sd_bus_message *cache;
48
49 sd_bus_slot *slot_job_removed;
50 char *path_ntp_unit;
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 sd_bus_message_unref(c->cache);
79
80 sd_bus_slot_unref(c->slot_job_removed);
81 free(c->path_ntp_unit);
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 const char *path;
356 Context *c = userdata;
357 int r;
358
359 assert(c);
360 assert(m);
361
362 r = sd_bus_message_read(m, "uoss", NULL, &path, NULL, NULL);
363 if (r < 0) {
364 bus_log_parse_error(r);
365 return 0;
366 }
367
368 if (!streq_ptr(path, c->path_ntp_unit))
369 return 0;
370
371 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
372
373 c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
374 c->path_ntp_unit = mfree(c->path_ntp_unit);
375
376 return 0;
377 }
378
379 static int unit_start_or_stop(Context *c, UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
380 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
381 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
382 const char *path;
383 int r;
384
385 assert(c);
386 assert(u);
387 assert(bus);
388 assert(error);
389
390 /* This method may be called frequently. Forget the previous job if it has not completed yet. */
391 c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
392
393 r = sd_bus_match_signal_async(
394 bus,
395 &slot,
396 "org.freedesktop.systemd1",
397 "/org/freedesktop/systemd1",
398 "org.freedesktop.systemd1.Manager",
399 "JobRemoved",
400 match_job_removed, NULL, c);
401 if (r < 0)
402 return r;
403
404 r = sd_bus_call_method(
405 bus,
406 "org.freedesktop.systemd1",
407 "/org/freedesktop/systemd1",
408 "org.freedesktop.systemd1.Manager",
409 start ? "StartUnit" : "StopUnit",
410 error,
411 &reply,
412 "ss",
413 u->name,
414 "replace");
415 if (r < 0)
416 return r;
417
418 r = sd_bus_message_read(reply, "o", &path);
419 if (r < 0)
420 return bus_log_parse_error(r);
421
422 r = free_and_strdup(&c->path_ntp_unit, path);
423 if (r < 0)
424 return log_oom();
425
426 c->slot_job_removed = TAKE_PTR(slot);
427 return 0;
428 }
429
430 static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool enable) {
431 int r;
432
433 assert(u);
434 assert(bus);
435 assert(error);
436
437 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
438
439 if (streq(u->unit_file_state, "enabled") == enable)
440 return 0;
441
442 if (enable)
443 r = sd_bus_call_method(
444 bus,
445 "org.freedesktop.systemd1",
446 "/org/freedesktop/systemd1",
447 "org.freedesktop.systemd1.Manager",
448 "EnableUnitFiles",
449 error,
450 NULL,
451 "asbb", 1,
452 u->name,
453 false, true);
454 else
455 r = sd_bus_call_method(
456 bus,
457 "org.freedesktop.systemd1",
458 "/org/freedesktop/systemd1",
459 "org.freedesktop.systemd1.Manager",
460 "DisableUnitFiles",
461 error,
462 NULL,
463 "asb", 1,
464 u->name,
465 false);
466 if (r < 0)
467 return r;
468
469 r = sd_bus_call_method(
470 bus,
471 "org.freedesktop.systemd1",
472 "/org/freedesktop/systemd1",
473 "org.freedesktop.systemd1.Manager",
474 "Reload",
475 error,
476 NULL,
477 NULL);
478 if (r < 0)
479 return r;
480
481 return 0;
482 }
483
484 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_time, "t", now(CLOCK_REALTIME));
485 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_ntp_sync, "b", ntp_synced());
486
487 static int property_get_rtc_time(
488 sd_bus *bus,
489 const char *path,
490 const char *interface,
491 const char *property,
492 sd_bus_message *reply,
493 void *userdata,
494 sd_bus_error *error) {
495
496 struct tm tm;
497 usec_t t;
498 int r;
499
500 zero(tm);
501 r = clock_get_hwclock(&tm);
502 if (r == -EBUSY) {
503 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
504 t = 0;
505 } else if (r == -ENOENT) {
506 log_debug("/dev/rtc not found.");
507 t = 0; /* no RTC found */
508 } else if (r < 0)
509 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
510 else
511 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
512
513 return sd_bus_message_append(reply, "t", t);
514 }
515
516 static int property_get_can_ntp(
517 sd_bus *bus,
518 const char *path,
519 const char *interface,
520 const char *property,
521 sd_bus_message *reply,
522 void *userdata,
523 sd_bus_error *error) {
524
525 Context *c = userdata;
526 int r;
527
528 assert(c);
529 assert(bus);
530 assert(property);
531 assert(reply);
532 assert(error);
533
534 r = context_update_ntp_status(c, bus, reply);
535 if (r < 0)
536 return r;
537
538 return sd_bus_message_append(reply, "b", context_ntp_service_exists(c) > 0);
539 }
540
541 static int property_get_ntp(
542 sd_bus *bus,
543 const char *path,
544 const char *interface,
545 const char *property,
546 sd_bus_message *reply,
547 void *userdata,
548 sd_bus_error *error) {
549
550 Context *c = userdata;
551 int r;
552
553 assert(c);
554 assert(bus);
555 assert(property);
556 assert(reply);
557 assert(error);
558
559 r = context_update_ntp_status(c, bus, reply);
560 if (r < 0)
561 return r;
562
563 return sd_bus_message_append(reply, "b", context_ntp_service_is_active(c) > 0);
564 }
565
566 static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
567 Context *c = userdata;
568 int interactive, r;
569 const char *z;
570
571 assert(m);
572 assert(c);
573
574 r = sd_bus_message_read(m, "sb", &z, &interactive);
575 if (r < 0)
576 return r;
577
578 if (!timezone_is_valid(z, LOG_DEBUG))
579 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
580
581 if (streq_ptr(z, c->zone))
582 return sd_bus_reply_method_return(m, NULL);
583
584 r = bus_verify_polkit_async(
585 m,
586 CAP_SYS_TIME,
587 "org.freedesktop.timedate1.set-timezone",
588 NULL,
589 interactive,
590 UID_INVALID,
591 &c->polkit_registry,
592 error);
593 if (r < 0)
594 return r;
595 if (r == 0)
596 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
597
598 r = free_and_strdup(&c->zone, z);
599 if (r < 0)
600 return r;
601
602 /* 1. Write new configuration file */
603 r = context_write_data_timezone(c);
604 if (r < 0) {
605 log_error_errno(r, "Failed to set time zone: %m");
606 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
607 }
608
609 /* 2. Make glibc notice the new timezone */
610 tzset();
611
612 /* 3. Tell the kernel our timezone */
613 r = clock_set_timezone(NULL);
614 if (r < 0)
615 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
616
617 if (c->local_rtc) {
618 struct timespec ts;
619 struct tm tm;
620
621 /* 4. Sync RTC from system clock, with the new delta */
622 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
623 assert_se(localtime_r(&ts.tv_sec, &tm));
624
625 r = clock_set_hwclock(&tm);
626 if (r < 0)
627 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
628 }
629
630 log_struct(LOG_INFO,
631 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
632 "TIMEZONE=%s", c->zone,
633 "TIMEZONE_SHORTNAME=%s", tzname[daylight],
634 "DAYLIGHT=%i", daylight,
635 LOG_MESSAGE("Changed time zone to '%s' (%s).", c->zone, tzname[daylight]));
636
637 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
638
639 return sd_bus_reply_method_return(m, NULL);
640 }
641
642 static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
643 int lrtc, fix_system, interactive;
644 Context *c = userdata;
645 struct timespec ts;
646 int r;
647
648 assert(m);
649 assert(c);
650
651 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
652 if (r < 0)
653 return r;
654
655 if (lrtc == c->local_rtc)
656 return sd_bus_reply_method_return(m, NULL);
657
658 r = bus_verify_polkit_async(
659 m,
660 CAP_SYS_TIME,
661 "org.freedesktop.timedate1.set-local-rtc",
662 NULL,
663 interactive,
664 UID_INVALID,
665 &c->polkit_registry,
666 error);
667 if (r < 0)
668 return r;
669 if (r == 0)
670 return 1;
671
672 c->local_rtc = lrtc;
673
674 /* 1. Write new configuration file */
675 r = context_write_data_local_rtc(c);
676 if (r < 0) {
677 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
678 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m");
679 }
680
681 /* 2. Tell the kernel our timezone */
682 r = clock_set_timezone(NULL);
683 if (r < 0)
684 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
685
686 /* 3. Synchronize clocks */
687 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
688
689 if (fix_system) {
690 struct tm tm;
691
692 /* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
693 if (c->local_rtc)
694 localtime_r(&ts.tv_sec, &tm);
695 else
696 gmtime_r(&ts.tv_sec, &tm);
697
698 /* Override the main fields of struct tm, but not the timezone fields */
699 r = clock_get_hwclock(&tm);
700 if (r < 0)
701 log_debug_errno(r, "Failed to get hardware clock, ignoring: %m");
702 else {
703 /* And set the system clock with this */
704 if (c->local_rtc)
705 ts.tv_sec = mktime(&tm);
706 else
707 ts.tv_sec = timegm(&tm);
708
709 if (clock_settime(CLOCK_REALTIME, &ts) < 0)
710 log_debug_errno(errno, "Failed to update system clock, ignoring: %m");
711 }
712
713 } else {
714 struct tm tm;
715
716 /* Sync RTC from system clock */
717 if (c->local_rtc)
718 localtime_r(&ts.tv_sec, &tm);
719 else
720 gmtime_r(&ts.tv_sec, &tm);
721
722 r = clock_set_hwclock(&tm);
723 if (r < 0)
724 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
725 }
726
727 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
728
729 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
730
731 return sd_bus_reply_method_return(m, NULL);
732 }
733
734 static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
735 sd_bus *bus = sd_bus_message_get_bus(m);
736 int relative, interactive, r;
737 Context *c = userdata;
738 int64_t utc;
739 struct timespec ts;
740 usec_t start;
741 struct tm tm;
742
743 assert(m);
744 assert(c);
745
746 r = context_update_ntp_status(c, bus, m);
747 if (r < 0)
748 return sd_bus_error_set_errnof(error, r, "Failed to update context: %m");
749
750 if (context_ntp_service_is_active(c) > 0)
751 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
752
753 /* this only gets used if dbus does not provide a timestamp */
754 start = now(CLOCK_MONOTONIC);
755
756 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
757 if (r < 0)
758 return r;
759
760 if (!relative && utc <= 0)
761 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
762
763 if (relative && utc == 0)
764 return sd_bus_reply_method_return(m, NULL);
765
766 if (relative) {
767 usec_t n, x;
768
769 n = now(CLOCK_REALTIME);
770 x = n + utc;
771
772 if ((utc > 0 && x < n) ||
773 (utc < 0 && x > n))
774 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
775
776 timespec_store(&ts, x);
777 } else
778 timespec_store(&ts, (usec_t) utc);
779
780 r = bus_verify_polkit_async(
781 m,
782 CAP_SYS_TIME,
783 "org.freedesktop.timedate1.set-time",
784 NULL,
785 interactive,
786 UID_INVALID,
787 &c->polkit_registry,
788 error);
789 if (r < 0)
790 return r;
791 if (r == 0)
792 return 1;
793
794 /* adjust ts for time spent in program */
795 r = sd_bus_message_get_monotonic_usec(m, &start);
796 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
797 if (r < 0 && r != -ENODATA)
798 return r;
799
800 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
801
802 /* Set system clock */
803 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
804 log_error_errno(errno, "Failed to set local time: %m");
805 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
806 }
807
808 /* Sync down to RTC */
809 if (c->local_rtc)
810 localtime_r(&ts.tv_sec, &tm);
811 else
812 gmtime_r(&ts.tv_sec, &tm);
813
814 r = clock_set_hwclock(&tm);
815 if (r < 0)
816 log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
817
818 log_struct(LOG_INFO,
819 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
820 "REALTIME="USEC_FMT, timespec_load(&ts),
821 LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)));
822
823 return sd_bus_reply_method_return(m, NULL);
824 }
825
826 static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
827 sd_bus *bus = sd_bus_message_get_bus(m);
828 Context *c = userdata;
829 UnitStatusInfo *u;
830 int enable, interactive, q, r;
831
832 assert(m);
833 assert(bus);
834 assert(c);
835
836 r = sd_bus_message_read(m, "bb", &enable, &interactive);
837 if (r < 0)
838 return r;
839
840 r = context_update_ntp_status(c, bus, m);
841 if (r < 0)
842 return r;
843
844 if (context_ntp_service_exists(c) <= 0)
845 return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
846
847 r = bus_verify_polkit_async(
848 m,
849 CAP_SYS_TIME,
850 "org.freedesktop.timedate1.set-ntp",
851 NULL,
852 interactive,
853 UID_INVALID,
854 &c->polkit_registry,
855 error);
856 if (r < 0)
857 return r;
858 if (r == 0)
859 return 1;
860
861 if (!enable)
862 LIST_FOREACH(units, u, c->units) {
863 if (!streq(u->load_state, "loaded"))
864 continue;
865
866 q = unit_enable_or_disable(u, bus, error, enable);
867 if (q < 0)
868 r = q;
869
870 q = unit_start_or_stop(c, u, bus, error, enable);
871 if (q < 0)
872 r = q;
873 }
874
875 else if (context_ntp_service_is_enabled(c) <= 0)
876 LIST_FOREACH(units, u, c->units) {
877 if (!streq(u->load_state, "loaded"))
878 continue;
879
880 r = unit_enable_or_disable(u, bus, error, enable);
881 if (r < 0)
882 continue;
883
884 r = unit_start_or_stop(c, u, bus, error, enable);
885 break;
886 }
887
888 else
889 LIST_FOREACH(units, u, c->units) {
890 if (!streq(u->load_state, "loaded") ||
891 !streq(u->unit_file_state, "enabled"))
892 continue;
893
894 r = unit_start_or_stop(c, u, bus, error, enable);
895 break;
896 }
897
898 if (r < 0)
899 return r;
900
901 log_info("Set NTP to %sd", enable_disable(enable));
902
903 return sd_bus_reply_method_return(m, NULL);
904 }
905
906 static const sd_bus_vtable timedate_vtable[] = {
907 SD_BUS_VTABLE_START(0),
908 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
909 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
910 SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0),
911 SD_BUS_PROPERTY("NTP", "b", property_get_ntp, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
912 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
913 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
914 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
915 SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
916 SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
917 SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
918 SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
919 SD_BUS_VTABLE_END,
920 };
921
922 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
923 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
924 int r;
925
926 assert(c);
927 assert(event);
928 assert(_bus);
929
930 r = sd_bus_default_system(&bus);
931 if (r < 0)
932 return log_error_errno(r, "Failed to get system bus connection: %m");
933
934 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
935 if (r < 0)
936 return log_error_errno(r, "Failed to register object: %m");
937
938 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
939 if (r < 0)
940 return log_error_errno(r, "Failed to request name: %m");
941
942 r = sd_bus_attach_event(bus, event, 0);
943 if (r < 0)
944 return log_error_errno(r, "Failed to attach bus to event loop: %m");
945
946 *_bus = TAKE_PTR(bus);
947
948 return 0;
949 }
950
951 int main(int argc, char *argv[]) {
952 Context context = {};
953 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
954 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
955 int r;
956
957 log_set_target(LOG_TARGET_AUTO);
958 log_parse_environment();
959 log_open();
960
961 umask(0022);
962
963 if (argc != 1) {
964 log_error("This program takes no arguments.");
965 r = -EINVAL;
966 goto finish;
967 }
968
969 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
970
971 r = sd_event_default(&event);
972 if (r < 0) {
973 log_error_errno(r, "Failed to allocate event loop: %m");
974 goto finish;
975 }
976
977 (void) sd_event_set_watchdog(event, true);
978
979 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
980 if (r < 0)
981 return r;
982
983 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
984 if (r < 0)
985 return r;
986
987 r = connect_bus(&context, event, &bus);
988 if (r < 0)
989 goto finish;
990
991 (void) sd_bus_negotiate_timestamp(bus, true);
992
993 r = context_read_data(&context);
994 if (r < 0) {
995 log_error_errno(r, "Failed to read time zone data: %m");
996 goto finish;
997 }
998
999 r = context_parse_ntp_services(&context);
1000 if (r < 0)
1001 goto finish;
1002
1003 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
1004 if (r < 0) {
1005 log_error_errno(r, "Failed to run event loop: %m");
1006 goto finish;
1007 }
1008
1009 finish:
1010 context_free(&context);
1011
1012 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1013 }