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