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