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