]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
shared: split out polkit stuff from bus-util.c → bus-polkit.c
[thirdparty/systemd.git] / src / timedate / timedated.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f401e48c 2
f401e48c 3#include <errno.h>
ca78ad1d
ZJS
4#include <sys/stat.h>
5#include <sys/types.h>
f401e48c
LP
6#include <unistd.h>
7
40ca29a1 8#include "sd-bus.h"
f4f15635
LP
9#include "sd-event.h"
10#include "sd-messages.h"
40ca29a1 11
b5efdb8a 12#include "alloc-util.h"
96aad8d1 13#include "bus-common-errors.h"
f4f15635 14#include "bus-error.h"
269e4d2d 15#include "bus-polkit.h"
f4f15635 16#include "clock-util.h"
afaae43b 17#include "conf-files.h"
f4f15635 18#include "def.h"
afaae43b 19#include "fd-util.h"
f4f15635 20#include "fileio-label.h"
e4de7287 21#include "fileio.h"
f4f15635 22#include "fs-util.h"
5d280742
YW
23#include "hashmap.h"
24#include "list.h"
1f47bc33 25#include "main-func.h"
0a970718 26#include "memory-util.h"
36dd5ffd 27#include "missing_capability.h"
f4f15635 28#include "path-util.h"
d7b8eec7 29#include "selinux-util.h"
754f0269 30#include "signal-util.h"
5d280742 31#include "string-util.h"
f4f15635 32#include "strv.h"
5d280742
YW
33#include "unit-def.h"
34#include "unit-name.h"
ee104e11 35#include "user-util.h"
f401e48c
LP
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
afaae43b
ZJS
40#define UNIT_LIST_DIRS (const char* const*) CONF_PATHS_STRV("systemd/ntp-units.d")
41
5d280742
YW
42typedef struct UnitStatusInfo {
43 char *name;
44 char *load_state;
45 char *unit_file_state;
46 char *active_state;
cf3872bd 47 char *path;
5d280742
YW
48
49 LIST_FIELDS(struct UnitStatusInfo, units);
50} UnitStatusInfo;
51
40ca29a1 52typedef struct Context {
d200735e
MS
53 char *zone;
54 bool local_rtc;
40ca29a1 55 Hashmap *polkit_registry;
2770af85 56 sd_bus_message *cache;
5d280742 57
3af0a96c 58 sd_bus_slot *slot_job_removed;
3af0a96c 59
5d280742 60 LIST_HEAD(UnitStatusInfo, units);
40ca29a1 61} Context;
f401e48c 62
ad7fb943
ZJS
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
5d280742
YW
82static 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
90static void unit_status_info_free(UnitStatusInfo *p) {
91 assert(p);
92
93 unit_status_info_clear(p);
94 free(p->name);
cf3872bd 95 free(p->path);
5d280742
YW
96 free(p);
97}
98
1f47bc33 99static void context_clear(Context *c) {
5d280742
YW
100 UnitStatusInfo *p;
101
40ca29a1 102 assert(c);
f401e48c 103
82d115d9 104 free(c->zone);
36e34057 105 bus_verify_polkit_async_registry_free(c->polkit_registry);
2770af85 106 sd_bus_message_unref(c->cache);
5d280742 107
3af0a96c 108 sd_bus_slot_unref(c->slot_job_removed);
3af0a96c 109
5d280742
YW
110 while ((p = c->units)) {
111 LIST_REMOVE(units, c->units, p);
112 unit_status_info_free(p);
113 }
114}
115
ad7fb943 116static int context_add_ntp_service(Context *c, const char *s, const char *source) {
5d280742
YW
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);
ad7fb943 138 log_unit_debug(u, "added from %s.", source);
5d280742
YW
139
140 return 0;
141}
142
afaae43b 143static int context_parse_ntp_services_from_environment(Context *c) {
5d280742
YW
144 const char *env, *p;
145 int r;
146
147 assert(c);
148
149 env = getenv("SYSTEMD_TIMEDATED_NTP_SERVICES");
afaae43b 150 if (!env)
5d280742 151 return 0;
afaae43b 152
ad7fb943 153 log_debug("Using list of ntp services from environment variable $SYSTEMD_TIMEDATED_NTP_SERVICES=%s.", env);
5d280742
YW
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
ad7fb943 168 r = context_add_ntp_service(c, word, "$SYSTEMD_TIMEDATED_NTP_SERVICES");
5d280742
YW
169 if (r < 0)
170 log_warning_errno(r, "Failed to add NTP service \"%s\", ignoring: %m", word);
171 }
172
afaae43b
ZJS
173 return 1;
174}
175
176static 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
ad7fb943 212 r = context_add_ntp_service(c, word, *f);
afaae43b
ZJS
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
221static 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);
5d280742
YW
229}
230
231static 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)
84a87726 240 count += !STRPTR_IN_SET(info->active_state, "inactive", "failed");
5d280742
YW
241
242 return count;
243}
244
5d280742
YW
245static 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;
f401e48c
LP
257}
258
40ca29a1 259static int context_read_data(Context *c) {
424a19f8 260 _cleanup_free_ char *t = NULL;
40ca29a1
LP
261 int r;
262
263 assert(c);
f401e48c 264
5c904ba5
LP
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");
92c4ef2d 270
f9ecfd3b 271 free_and_replace(c->zone, t);
f401e48c 272
6369641d 273 c->local_rtc = clock_is_localtime(NULL) > 0;
f401e48c
LP
274
275 return 0;
276}
277
40ca29a1 278static int context_write_data_timezone(Context *c) {
424a19f8 279 _cleanup_free_ char *p = NULL;
9193af0f 280 const char *source;
40ca29a1
LP
281
282 assert(c);
e19a21a8 283
9193af0f
LP
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;
f401e48c 295
9193af0f
LP
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 }
f401e48c 307
9193af0f 308 return symlink_atomic(source, "/etc/localtime");
f401e48c
LP
309}
310
40ca29a1 311static int context_write_data_local_rtc(Context *c) {
7fd1b19b 312 _cleanup_free_ char *s = NULL, *w = NULL;
d9cb4bba 313 int r;
f401e48c 314
40ca29a1
LP
315 assert(c);
316
f401e48c
LP
317 r = read_full_file("/etc/adjtime", &s, NULL);
318 if (r < 0) {
319 if (r != -ENOENT)
320 return r;
321
40ca29a1 322 if (!c->local_rtc)
f401e48c
LP
323 return 0;
324
325 w = strdup(NULL_ADJTIME_LOCAL);
326 if (!w)
327 return -ENOMEM;
328 } else {
c9410dd4 329 char *p;
8f462d87 330 const char *e = "\n"; /* default if there is less than 3 lines */
c9410dd4 331 const char *prepend = "";
f401e48c
LP
332 size_t a, b;
333
c9410dd4 334 p = strchrnul(s, '\n');
cb971cc0 335 if (*p == '\0')
c9410dd4
MP
336 /* only one line, no \n terminator */
337 prepend = "\n0\n";
cb971cc0 338 else if (p[1] == '\0') {
c9410dd4
MP
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 }
f401e48c
LP
358
359 a = p - s;
360 b = strlen(e);
361
c9410dd4 362 w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1);
d257f05a 363 if (!w)
f401e48c 364 return -ENOMEM;
f401e48c 365
c9410dd4 366 *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
f401e48c
LP
367
368 if (streq(w, NULL_ADJTIME_UTC)) {
d257f05a 369 if (unlink("/etc/adjtime") < 0)
f401e48c
LP
370 if (errno != ENOENT)
371 return -errno;
f401e48c
LP
372
373 return 0;
374 }
375 }
40ca29a1 376
c3dacc8b 377 mac_selinux_init();
d257f05a 378 return write_string_file_atomic_label("/etc/adjtime", w);
f401e48c
LP
379}
380
5d280742
YW
381static 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 };
5d280742 388 UnitStatusInfo *u;
c5f0532f
LP
389 int r;
390
40ca29a1 391 assert(c);
c5f0532f
LP
392 assert(bus);
393
2770af85
YW
394 /* Suppress calling context_update_ntp_status() multiple times within single DBus transaction. */
395 if (m) {
396 if (m == c->cache)
397 return 0;
2aa4c315 398
2770af85
YW
399 sd_bus_message_unref(c->cache);
400 c->cache = sd_bus_message_ref(m);
401 }
2aa4c315 402
5d280742
YW
403 LIST_FOREACH(units, u, c->units) {
404 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5d280742 405 _cleanup_free_ char *path = NULL;
c5f0532f 406
5d280742 407 unit_status_info_clear(u);
40ca29a1 408
5d280742
YW
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)
ad7fb943 423 return log_unit_error_errno(u, r, "Failed to get properties: %s", bus_error_message(&error, r));
5d280742 424 }
c5f0532f 425
40ca29a1 426 return 0;
c5f0532f
LP
427}
428
3af0a96c 429static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
3af0a96c 430 Context *c = userdata;
cf3872bd
YW
431 UnitStatusInfo *u;
432 const char *path;
433 unsigned n = 0;
3af0a96c
YW
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
cf3872bd
YW
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;
3af0a96c 450
cf3872bd 451 if (n == 0) {
cf3872bd 452 c->slot_job_removed = sd_bus_slot_unref(c->slot_job_removed);
49942d6b 453
ad7fb943
ZJS
454 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
455 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP",
456 NULL);
cf3872bd 457 }
3af0a96c
YW
458
459 return 0;
460}
461
cf3872bd 462static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool start) {
3af0a96c 463 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3af0a96c 464 const char *path;
c5f0532f
LP
465 int r;
466
5d280742 467 assert(u);
c5f0532f
LP
468 assert(bus);
469 assert(error);
470
81b84399
ZJS
471 r = sd_bus_call_method(
472 bus,
473 "org.freedesktop.systemd1",
474 "/org/freedesktop/systemd1",
475 "org.freedesktop.systemd1.Manager",
5d280742 476 start ? "StartUnit" : "StopUnit",
81b84399 477 error,
3af0a96c 478 &reply,
81b84399 479 "ss",
5d280742 480 u->name,
81b84399 481 "replace");
ad7fb943
ZJS
482 log_unit_full(u, r < 0 ? LOG_WARNING : LOG_DEBUG, r,
483 "%s unit: %m", start ? "Starting" : "Stopping");
5d280742 484 if (r < 0)
b72ddf0f 485 return r;
c5f0532f 486
3af0a96c
YW
487 r = sd_bus_message_read(reply, "o", &path);
488 if (r < 0)
489 return bus_log_parse_error(r);
490
cf3872bd 491 r = free_and_strdup(&u->path, path);
3af0a96c
YW
492 if (r < 0)
493 return log_oom();
494
b72ddf0f 495 return 0;
c5f0532f
LP
496}
497
5d280742 498static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *error, bool enable) {
c5f0532f 499 int r;
c5f0532f 500
5d280742 501 assert(u);
c5f0532f
LP
502 assert(bus);
503 assert(error);
504
5d280742
YW
505 /* Call context_update_ntp_status() to update UnitStatusInfo before calling this. */
506
ad7fb943
ZJS
507 if (streq(u->unit_file_state, "enabled") == enable) {
508 log_unit_debug(u, "already %sd.", enable_disable(enable));
5d280742 509 return 0;
ad7fb943
ZJS
510 }
511
512 log_unit_info(u, "%s unit.", enable ? "Enabling" : "Disabling");
5d280742
YW
513
514 if (enable)
40ca29a1
LP
515 r = sd_bus_call_method(
516 bus,
ac7019f3
LP
517 "org.freedesktop.systemd1",
518 "/org/freedesktop/systemd1",
519 "org.freedesktop.systemd1.Manager",
b72ddf0f 520 "EnableUnitFiles",
40ca29a1
LP
521 error,
522 NULL,
b72ddf0f 523 "asbb", 1,
5d280742 524 u->name,
b72ddf0f
KS
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,
5d280742 536 u->name,
b72ddf0f 537 false);
5d280742 538 if (r < 0)
b72ddf0f 539 return r;
c5f0532f 540
b72ddf0f
KS
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);
3af0a96c
YW
550 if (r < 0)
551 return r;
552
b72ddf0f 553 return 0;
40ca29a1 554}
c5f0532f 555
6cc379b5
YW
556static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_time, "t", now(CLOCK_REALTIME));
557static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_ntp_sync, "b", ntp_synced());
558
6fc60278
LP
559static 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,
ebcf1f97
LP
565 void *userdata,
566 sd_bus_error *error) {
6fc60278 567
d9cb4bba
ZJS
568 struct tm tm = {};
569 usec_t t = 0;
6fc60278
LP
570 int r;
571
60989612 572 r = clock_get_hwclock(&tm);
d9cb4bba 573 if (r == -EBUSY)
07a062a7 574 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
d9cb4bba 575 else if (r == -ENOENT)
07a062a7 576 log_debug("/dev/rtc not found.");
d9cb4bba 577 else if (r < 0)
10a87006 578 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
ebcf1f97 579 else
2f6a5907 580 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
6fc60278 581
ebcf1f97 582 return sd_bus_message_append(reply, "t", t);
6fc60278
LP
583}
584
5d280742
YW
585static 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
b4356b57
YW
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
5d280742
YW
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
614static 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
b4356b57
YW
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
5d280742
YW
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
19070062 643static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
40ca29a1 644 Context *c = userdata;
2c3def62 645 int interactive, r;
40ca29a1 646 const char *z;
c5f0532f 647
7e9cf16c
LP
648 assert(m);
649 assert(c);
650
40ca29a1
LP
651 r = sd_bus_message_read(m, "sb", &z, &interactive);
652 if (r < 0)
ebcf1f97 653 return r;
c5f0532f 654
089fb865 655 if (!timezone_is_valid(z, LOG_DEBUG))
5322db06 656 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid or not installed time zone '%s'", z);
ac7019f3 657
539a68e0 658 if (streq_ptr(z, c->zone))
df2d202e 659 return sd_bus_reply_method_return(m, NULL);
c5f0532f 660
c529695e
LP
661 r = bus_verify_polkit_async(
662 m,
663 CAP_SYS_TIME,
664 "org.freedesktop.timedate1.set-timezone",
403ed0e5 665 NULL,
c529695e
LP
666 interactive,
667 UID_INVALID,
668 &c->polkit_registry,
669 error);
40ca29a1 670 if (r < 0)
ebcf1f97 671 return r;
40ca29a1 672 if (r == 0)
6fc60278 673 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
6ffe5e37 674
539a68e0
YW
675 r = free_and_strdup(&c->zone, z);
676 if (r < 0)
677 return r;
678
40ca29a1
LP
679 /* 1. Write new configuration file */
680 r = context_write_data_timezone(c);
681 if (r < 0) {
da927ba9 682 log_error_errno(r, "Failed to set time zone: %m");
10a87006 683 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
40ca29a1 684 }
6ffe5e37 685
8a50b96f
LP
686 /* 2. Make glibc notice the new timezone */
687 tzset();
688
689 /* 3. Tell the kernel our timezone */
2a7ff45f
LP
690 r = clock_set_timezone(NULL);
691 if (r < 0)
692 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
6ffe5e37 693
40ca29a1
LP
694 if (c->local_rtc) {
695 struct timespec ts;
e0f691e1 696 struct tm tm;
c5f0532f 697
8a50b96f 698 /* 4. Sync RTC from system clock, with the new delta */
40ca29a1 699 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
e0f691e1 700 assert_se(localtime_r(&ts.tv_sec, &tm));
2a7ff45f 701
e0f691e1 702 r = clock_set_hwclock(&tm);
2a7ff45f
LP
703 if (r < 0)
704 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 705 }
c5f0532f 706
40ca29a1 707 log_struct(LOG_INFO,
2b044526 708 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
40ca29a1 709 "TIMEZONE=%s", c->zone,
8a50b96f
LP
710 "TIMEZONE_SHORTNAME=%s", tzname[daylight],
711 "DAYLIGHT=%i", daylight,
a1230ff9 712 LOG_MESSAGE("Changed time zone to '%s' (%s).", c->zone, tzname[daylight]));
c5f0532f 713
ad7fb943
ZJS
714 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
715 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone",
716 NULL);
c5f0532f 717
df2d202e 718 return sd_bus_reply_method_return(m, NULL);
c5f0532f
LP
719}
720
19070062 721static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102d8f81 722 int lrtc, fix_system, interactive;
40ca29a1
LP
723 Context *c = userdata;
724 struct timespec ts;
f401e48c
LP
725 int r;
726
40ca29a1
LP
727 assert(m);
728 assert(c);
f401e48c 729
40ca29a1
LP
730 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
731 if (r < 0)
ebcf1f97 732 return r;
f401e48c 733
40ca29a1 734 if (lrtc == c->local_rtc)
df2d202e 735 return sd_bus_reply_method_return(m, NULL);
f401e48c 736
c529695e
LP
737 r = bus_verify_polkit_async(
738 m,
739 CAP_SYS_TIME,
740 "org.freedesktop.timedate1.set-local-rtc",
403ed0e5 741 NULL,
c529695e
LP
742 interactive,
743 UID_INVALID,
744 &c->polkit_registry,
745 error);
40ca29a1 746 if (r < 0)
ebcf1f97 747 return r;
40ca29a1
LP
748 if (r == 0)
749 return 1;
f401e48c 750
40ca29a1 751 c->local_rtc = lrtc;
f401e48c 752
40ca29a1
LP
753 /* 1. Write new configuration file */
754 r = context_write_data_local_rtc(c);
755 if (r < 0) {
ad7fb943
ZJS
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");
40ca29a1 758 }
f401e48c 759
40ca29a1 760 /* 2. Tell the kernel our timezone */
2a7ff45f
LP
761 r = clock_set_timezone(NULL);
762 if (r < 0)
763 log_debug_errno(r, "Failed to tell kernel about timezone, ignoring: %m");
f401e48c 764
40ca29a1
LP
765 /* 3. Synchronize clocks */
766 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
f401e48c 767
40ca29a1
LP
768 if (fix_system) {
769 struct tm tm;
f401e48c 770
2a7ff45f 771 /* Sync system clock from RTC; first, initialize the timezone fields of struct tm. */
40ca29a1 772 if (c->local_rtc)
e46acb79 773 localtime_r(&ts.tv_sec, &tm);
40ca29a1 774 else
e46acb79 775 gmtime_r(&ts.tv_sec, &tm);
72edcff5 776
2a7ff45f
LP
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 */
40ca29a1
LP
783 if (c->local_rtc)
784 ts.tv_sec = mktime(&tm);
785 else
786 ts.tv_sec = timegm(&tm);
2076cf88 787
2a7ff45f
LP
788 if (clock_settime(CLOCK_REALTIME, &ts) < 0)
789 log_debug_errno(errno, "Failed to update system clock, ignoring: %m");
f401e48c
LP
790 }
791
40ca29a1 792 } else {
e46acb79 793 struct tm tm;
f401e48c 794
40ca29a1
LP
795 /* Sync RTC from system clock */
796 if (c->local_rtc)
e46acb79 797 localtime_r(&ts.tv_sec, &tm);
40ca29a1 798 else
e46acb79 799 gmtime_r(&ts.tv_sec, &tm);
2076cf88 800
e46acb79 801 r = clock_set_hwclock(&tm);
2a7ff45f
LP
802 if (r < 0)
803 log_debug_errno(r, "Failed to sync time to hardware clock, ignoring: %m");
40ca29a1 804 }
2076cf88 805
40ca29a1 806 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
2076cf88 807
ad7fb943
ZJS
808 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
809 "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC",
810 NULL);
2076cf88 811
df2d202e 812 return sd_bus_reply_method_return(m, NULL);
40ca29a1 813}
2076cf88 814
19070062 815static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
5d280742
YW
816 sd_bus *bus = sd_bus_message_get_bus(m);
817 int relative, interactive, r;
40ca29a1
LP
818 Context *c = userdata;
819 int64_t utc;
820 struct timespec ts;
2479df30 821 usec_t start;
e46acb79 822 struct tm tm;
2076cf88 823
40ca29a1
LP
824 assert(m);
825 assert(c);
2076cf88 826
b4356b57
YW
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
5d280742
YW
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)
e9e5ea88 835 return sd_bus_error_set(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
82d115d9 836
6829cec4
SL
837 /* this only gets used if dbus does not provide a timestamp */
838 start = now(CLOCK_MONOTONIC);
839
40ca29a1
LP
840 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
841 if (r < 0)
ebcf1f97 842 return r;
2076cf88 843
40ca29a1 844 if (!relative && utc <= 0)
e9e5ea88 845 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
2076cf88 846
40ca29a1 847 if (relative && utc == 0)
df2d202e 848 return sd_bus_reply_method_return(m, NULL);
2076cf88 849
40ca29a1
LP
850 if (relative) {
851 usec_t n, x;
f401e48c 852
40ca29a1
LP
853 n = now(CLOCK_REALTIME);
854 x = n + utc;
f401e48c 855
40ca29a1
LP
856 if ((utc > 0 && x < n) ||
857 (utc < 0 && x > n))
e9e5ea88 858 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
f401e48c 859
40ca29a1
LP
860 timespec_store(&ts, x);
861 } else
862 timespec_store(&ts, (usec_t) utc);
2076cf88 863
c529695e
LP
864 r = bus_verify_polkit_async(
865 m,
866 CAP_SYS_TIME,
867 "org.freedesktop.timedate1.set-time",
403ed0e5 868 NULL,
c529695e
LP
869 interactive,
870 UID_INVALID,
871 &c->polkit_registry,
872 error);
40ca29a1 873 if (r < 0)
ebcf1f97 874 return r;
40ca29a1
LP
875 if (r == 0)
876 return 1;
877
2479df30
SL
878 /* adjust ts for time spent in program */
879 r = sd_bus_message_get_monotonic_usec(m, &start);
6829cec4 880 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
2479df30
SL
881 if (r < 0 && r != -ENODATA)
882 return r;
6829cec4
SL
883
884 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
2479df30 885
40ca29a1
LP
886 /* Set system clock */
887 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
56f64d95 888 log_error_errno(errno, "Failed to set local time: %m");
ebcf1f97 889 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
40ca29a1 890 }
2076cf88 891
40ca29a1
LP
892 /* Sync down to RTC */
893 if (c->local_rtc)
e46acb79 894 localtime_r(&ts.tv_sec, &tm);
40ca29a1 895 else
e46acb79 896 gmtime_r(&ts.tv_sec, &tm);
2a7ff45f 897
e46acb79 898 r = clock_set_hwclock(&tm);
2a7ff45f
LP
899 if (r < 0)
900 log_debug_errno(r, "Failed to update hardware clock, ignoring: %m");
f401e48c 901
40ca29a1 902 log_struct(LOG_INFO,
2b044526 903 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
de0671ee 904 "REALTIME="USEC_FMT, timespec_load(&ts),
a1230ff9 905 LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)));
f401e48c 906
df2d202e 907 return sd_bus_reply_method_return(m, NULL);
40ca29a1 908}
f401e48c 909
19070062 910static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
cf3872bd 911 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
5d280742 912 sd_bus *bus = sd_bus_message_get_bus(m);
40ca29a1 913 Context *c = userdata;
5d280742 914 UnitStatusInfo *u;
ad7fb943 915 const UnitStatusInfo *selected = NULL;
5d280742 916 int enable, interactive, q, r;
f401e48c 917
19070062 918 assert(m);
5d280742 919 assert(bus);
19070062
LP
920 assert(c);
921
5d280742 922 r = sd_bus_message_read(m, "bb", &enable, &interactive);
40ca29a1 923 if (r < 0)
ebcf1f97 924 return r;
f401e48c 925
5d280742
YW
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");
f401e48c 932
c529695e
LP
933 r = bus_verify_polkit_async(
934 m,
935 CAP_SYS_TIME,
936 "org.freedesktop.timedate1.set-ntp",
403ed0e5 937 NULL,
c529695e
LP
938 interactive,
939 UID_INVALID,
940 &c->polkit_registry,
941 error);
40ca29a1 942 if (r < 0)
ebcf1f97 943 return r;
40ca29a1
LP
944 if (r == 0)
945 return 1;
f401e48c 946
cf3872bd
YW
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
0957790b 964 if (enable)
5d280742 965 LIST_FOREACH(units, u, c->units) {
0957790b 966 bool enable_this_one = !selected;
5d280742 967
5d280742
YW
968 if (!streq(u->load_state, "loaded"))
969 continue;
970
0957790b 971 r = unit_enable_or_disable(u, bus, error, enable_this_one);
5d280742 972 if (r < 0)
0957790b
ZJS
973 /* If enablement failed, don't start this unit. */
974 enable_this_one = false;
5d280742 975
0957790b
ZJS
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;
5d280742 983 }
3af0a96c 984 else
5d280742 985 LIST_FOREACH(units, u, c->units) {
0957790b 986 if (!streq(u->load_state, "loaded"))
5d280742
YW
987 continue;
988
0957790b
ZJS
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;
5d280742 996 }
40ca29a1 997
40ca29a1 998 if (r < 0)
ebcf1f97 999 return r;
ad7fb943
ZJS
1000 if (enable && !selected)
1001 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No NTP service found to enable.");
f401e48c 1002
cf3872bd
YW
1003 if (slot)
1004 c->slot_job_removed = TAKE_PTR(slot);
1005
ad7fb943
ZJS
1006 if (selected)
1007 log_info("Set NTP to enabled (%s).", selected->name);
1008 else
1009 log_info("Set NTP to disabled.");
f401e48c 1010
df2d202e 1011 return sd_bus_reply_method_return(m, NULL);
f401e48c
LP
1012}
1013
2cf0b2fe
NT
1014static 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
40ca29a1
LP
1036static 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),
82d115d9 1039 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
5d280742
YW
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),
03cc26dd
LP
1042 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
1043 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
6fc60278 1044 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
adacb957
LP
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),
2cf0b2fe 1049 SD_BUS_METHOD("ListTimezones", NULL, "as", method_list_timezones, SD_BUS_VTABLE_UNPRIVILEGED),
40ca29a1
LP
1050 SD_BUS_VTABLE_END,
1051};
1052
1053static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 1054 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c
LP
1055 int r;
1056
40ca29a1
LP
1057 assert(c);
1058 assert(event);
f401e48c
LP
1059 assert(_bus);
1060
76b54375 1061 r = sd_bus_default_system(&bus);
f647962d
MS
1062 if (r < 0)
1063 return log_error_errno(r, "Failed to get system bus connection: %m");
f401e48c 1064
19befb2d 1065 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
f647962d
MS
1066 if (r < 0)
1067 return log_error_errno(r, "Failed to register object: %m");
f401e48c 1068
0c0b9306 1069 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
f647962d 1070 if (r < 0)
0c0b9306 1071 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1072
40ca29a1 1073 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1074 if (r < 0)
1075 return log_error_errno(r, "Failed to attach bus to event loop: %m");
f401e48c 1076
1cc6c93a 1077 *_bus = TAKE_PTR(bus);
f401e48c 1078
40ca29a1 1079 return 0;
f401e48c
LP
1080}
1081
1f47bc33
YW
1082static int run(int argc, char *argv[]) {
1083 _cleanup_(context_clear) Context context = {};
4afd3348
LP
1084 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1085 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
f401e48c 1086 int r;
f401e48c 1087
6bf3c61c 1088 log_setup_service();
f401e48c 1089
4c12626c
LP
1090 umask(0022);
1091
1f47bc33
YW
1092 if (argc != 1)
1093 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
f401e48c 1094
754f0269
YW
1095 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1096
afc6adb5 1097 r = sd_event_default(&event);
1f47bc33
YW
1098 if (r < 0)
1099 return log_error_errno(r, "Failed to allocate event loop: %m");
f401e48c 1100
754f0269
YW
1101 (void) sd_event_set_watchdog(event, true);
1102
1103 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1f47bc33
YW
1104 if (r < 0)
1105 return log_error_errno(r, "Failed to install SIGINT handler: %m");
754f0269
YW
1106
1107 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1f47bc33
YW
1108 if (r < 0)
1109 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1110
40ca29a1 1111 r = connect_bus(&context, event, &bus);
f401e48c 1112 if (r < 0)
1f47bc33 1113 return r;
f401e48c 1114
dc751688 1115 (void) sd_bus_negotiate_timestamp(bus, true);
2479df30 1116
40ca29a1 1117 r = context_read_data(&context);
1f47bc33
YW
1118 if (r < 0)
1119 return log_error_errno(r, "Failed to read time zone data: %m");
c5f0532f 1120
5d280742
YW
1121 r = context_parse_ntp_services(&context);
1122 if (r < 0)
1f47bc33 1123 return r;
ad740100 1124
37224a5f 1125 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
1f47bc33
YW
1126 if (r < 0)
1127 return log_error_errno(r, "Failed to run event loop: %m");
f401e48c 1128
1f47bc33 1129 return 0;
f401e48c 1130}
1f47bc33
YW
1131
1132DEFINE_MAIN_FUNCTION(run);