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