]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timedate/timedated.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / timedate / timedated.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "sd-bus.h"
26 #include "sd-event.h"
27 #include "sd-messages.h"
28
29 #include "alloc-util.h"
30 #include "bus-common-errors.h"
31 #include "bus-error.h"
32 #include "bus-util.h"
33 #include "clock-util.h"
34 #include "def.h"
35 #include "fileio-label.h"
36 #include "fs-util.h"
37 #include "path-util.h"
38 #include "selinux-util.h"
39 #include "strv.h"
40 #include "user-util.h"
41 #include "util.h"
42
43 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
44 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
45
46 static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = {
47 SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", EOPNOTSUPP),
48 SD_BUS_ERROR_MAP_END
49 };
50
51 typedef struct Context {
52 char *zone;
53 bool local_rtc;
54 bool can_ntp;
55 bool use_ntp;
56 Hashmap *polkit_registry;
57 } Context;
58
59 static void context_free(Context *c) {
60 assert(c);
61
62 free(c->zone);
63 bus_verify_polkit_async_registry_free(c->polkit_registry);
64 }
65
66 static int context_read_data(Context *c) {
67 _cleanup_free_ char *t = NULL;
68 int r;
69
70 assert(c);
71
72 r = get_timezone(&t);
73 if (r == -EINVAL)
74 log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
75 else if (r < 0)
76 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
77
78 free(c->zone);
79 c->zone = t;
80 t = NULL;
81
82 c->local_rtc = clock_is_localtime(NULL) > 0;
83
84 return 0;
85 }
86
87 static int context_write_data_timezone(Context *c) {
88 _cleanup_free_ char *p = NULL;
89 int r = 0;
90
91 assert(c);
92
93 if (isempty(c->zone)) {
94 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
95 r = -errno;
96
97 return r;
98 }
99
100 p = strappend("../usr/share/zoneinfo/", c->zone);
101 if (!p)
102 return log_oom();
103
104 r = symlink_atomic(p, "/etc/localtime");
105 if (r < 0)
106 return r;
107
108 return 0;
109 }
110
111 static int context_write_data_local_rtc(Context *c) {
112 int r;
113 _cleanup_free_ char *s = NULL, *w = NULL;
114
115 assert(c);
116
117 r = read_full_file("/etc/adjtime", &s, NULL);
118 if (r < 0) {
119 if (r != -ENOENT)
120 return r;
121
122 if (!c->local_rtc)
123 return 0;
124
125 w = strdup(NULL_ADJTIME_LOCAL);
126 if (!w)
127 return -ENOMEM;
128 } else {
129 char *p;
130 const char *e = "\n"; /* default if there is less than 3 lines */
131 const char *prepend = "";
132 size_t a, b;
133
134 p = strchrnul(s, '\n');
135 if (*p == '\0')
136 /* only one line, no \n terminator */
137 prepend = "\n0\n";
138 else if (p[1] == '\0') {
139 /* only one line, with \n terminator */
140 ++p;
141 prepend = "0\n";
142 } else {
143 p = strchr(p+1, '\n');
144 if (!p) {
145 /* only two lines, no \n terminator */
146 prepend = "\n";
147 p = s + strlen(s);
148 } else {
149 char *end;
150 /* third line might have a \n terminator or not */
151 p++;
152 end = strchr(p, '\n');
153 /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */
154 if (end)
155 e = end;
156 }
157 }
158
159 a = p - s;
160 b = strlen(e);
161
162 w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1);
163 if (!w)
164 return -ENOMEM;
165
166 *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
167
168 if (streq(w, NULL_ADJTIME_UTC)) {
169 if (unlink("/etc/adjtime") < 0)
170 if (errno != ENOENT)
171 return -errno;
172
173 return 0;
174 }
175 }
176
177 mac_selinux_init();
178 return write_string_file_atomic_label("/etc/adjtime", w);
179 }
180
181 static int context_read_ntp(Context *c, sd_bus *bus) {
182 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
183 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
184 const char *s;
185 int r;
186
187 assert(c);
188 assert(bus);
189
190 r = sd_bus_call_method(
191 bus,
192 "org.freedesktop.systemd1",
193 "/org/freedesktop/systemd1",
194 "org.freedesktop.systemd1.Manager",
195 "GetUnitFileState",
196 &error,
197 &reply,
198 "s",
199 "systemd-timesyncd.service");
200
201 if (r < 0) {
202 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
203 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") ||
204 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit"))
205 return 0;
206
207 return r;
208 }
209
210 r = sd_bus_message_read(reply, "s", &s);
211 if (r < 0)
212 return r;
213
214 c->can_ntp = true;
215 c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime");
216
217 return 0;
218 }
219
220 static int context_start_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
221 int r;
222
223 assert(bus);
224 assert(error);
225
226 r = sd_bus_call_method(
227 bus,
228 "org.freedesktop.systemd1",
229 "/org/freedesktop/systemd1",
230 "org.freedesktop.systemd1.Manager",
231 enabled ? "StartUnit" : "StopUnit",
232 error,
233 NULL,
234 "ss",
235 "systemd-timesyncd.service",
236 "replace");
237 if (r < 0) {
238 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
239 sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
240 sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit"))
241 return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
242
243 return r;
244 }
245
246 return 0;
247 }
248
249 static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
250 int r;
251
252 assert(bus);
253 assert(error);
254
255 if (enabled)
256 r = sd_bus_call_method(
257 bus,
258 "org.freedesktop.systemd1",
259 "/org/freedesktop/systemd1",
260 "org.freedesktop.systemd1.Manager",
261 "EnableUnitFiles",
262 error,
263 NULL,
264 "asbb", 1,
265 "systemd-timesyncd.service",
266 false, true);
267 else
268 r = sd_bus_call_method(
269 bus,
270 "org.freedesktop.systemd1",
271 "/org/freedesktop/systemd1",
272 "org.freedesktop.systemd1.Manager",
273 "DisableUnitFiles",
274 error,
275 NULL,
276 "asb", 1,
277 "systemd-timesyncd.service",
278 false);
279
280 if (r < 0) {
281 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND))
282 return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
283
284 return r;
285 }
286
287 r = sd_bus_call_method(
288 bus,
289 "org.freedesktop.systemd1",
290 "/org/freedesktop/systemd1",
291 "org.freedesktop.systemd1.Manager",
292 "Reload",
293 error,
294 NULL,
295 NULL);
296 if (r < 0)
297 return r;
298
299 return 0;
300 }
301
302 static int property_get_rtc_time(
303 sd_bus *bus,
304 const char *path,
305 const char *interface,
306 const char *property,
307 sd_bus_message *reply,
308 void *userdata,
309 sd_bus_error *error) {
310
311 struct tm tm;
312 usec_t t;
313 int r;
314
315 zero(tm);
316 r = clock_get_hwclock(&tm);
317 if (r == -EBUSY) {
318 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
319 t = 0;
320 } else if (r == -ENOENT) {
321 log_debug("/dev/rtc not found.");
322 t = 0; /* no RTC found */
323 } else if (r < 0)
324 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
325 else
326 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
327
328 return sd_bus_message_append(reply, "t", t);
329 }
330
331 static int property_get_time(
332 sd_bus *bus,
333 const char *path,
334 const char *interface,
335 const char *property,
336 sd_bus_message *reply,
337 void *userdata,
338 sd_bus_error *error) {
339
340 return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
341 }
342
343 static int property_get_ntp_sync(
344 sd_bus *bus,
345 const char *path,
346 const char *interface,
347 const char *property,
348 sd_bus_message *reply,
349 void *userdata,
350 sd_bus_error *error) {
351
352 return sd_bus_message_append(reply, "b", ntp_synced());
353 }
354
355 static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
356 Context *c = userdata;
357 const char *z;
358 int interactive;
359 char *t;
360 int r;
361
362 assert(m);
363 assert(c);
364
365 r = sd_bus_message_read(m, "sb", &z, &interactive);
366 if (r < 0)
367 return r;
368
369 if (!timezone_is_valid(z))
370 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
371
372 if (streq_ptr(z, c->zone))
373 return sd_bus_reply_method_return(m, NULL);
374
375 r = bus_verify_polkit_async(
376 m,
377 CAP_SYS_TIME,
378 "org.freedesktop.timedate1.set-timezone",
379 NULL,
380 interactive,
381 UID_INVALID,
382 &c->polkit_registry,
383 error);
384 if (r < 0)
385 return r;
386 if (r == 0)
387 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
388
389 t = strdup(z);
390 if (!t)
391 return -ENOMEM;
392
393 free(c->zone);
394 c->zone = t;
395
396 /* 1. Write new configuration file */
397 r = context_write_data_timezone(c);
398 if (r < 0) {
399 log_error_errno(r, "Failed to set time zone: %m");
400 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
401 }
402
403 /* 2. Tell the kernel our timezone */
404 clock_set_timezone(NULL);
405
406 if (c->local_rtc) {
407 struct timespec ts;
408 struct tm *tm;
409
410 /* 3. Sync RTC from system clock, with the new delta */
411 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
412 assert_se(tm = localtime(&ts.tv_sec));
413 clock_set_hwclock(tm);
414 }
415
416 log_struct(LOG_INFO,
417 "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR,
418 "TIMEZONE=%s", c->zone,
419 LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
420 NULL);
421
422 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
423
424 return sd_bus_reply_method_return(m, NULL);
425 }
426
427 static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) {
428 int lrtc, fix_system, interactive;
429 Context *c = userdata;
430 struct timespec ts;
431 int r;
432
433 assert(m);
434 assert(c);
435
436 r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
437 if (r < 0)
438 return r;
439
440 if (lrtc == c->local_rtc)
441 return sd_bus_reply_method_return(m, NULL);
442
443 r = bus_verify_polkit_async(
444 m,
445 CAP_SYS_TIME,
446 "org.freedesktop.timedate1.set-local-rtc",
447 NULL,
448 interactive,
449 UID_INVALID,
450 &c->polkit_registry,
451 error);
452 if (r < 0)
453 return r;
454 if (r == 0)
455 return 1;
456
457 c->local_rtc = lrtc;
458
459 /* 1. Write new configuration file */
460 r = context_write_data_local_rtc(c);
461 if (r < 0) {
462 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
463 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m");
464 }
465
466 /* 2. Tell the kernel our timezone */
467 clock_set_timezone(NULL);
468
469 /* 3. Synchronize clocks */
470 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
471
472 if (fix_system) {
473 struct tm tm;
474
475 /* Sync system clock from RTC; first,
476 * initialize the timezone fields of
477 * struct tm. */
478 if (c->local_rtc)
479 tm = *localtime(&ts.tv_sec);
480 else
481 tm = *gmtime(&ts.tv_sec);
482
483 /* Override the main fields of
484 * struct tm, but not the timezone
485 * fields */
486 if (clock_get_hwclock(&tm) >= 0) {
487
488 /* And set the system clock
489 * with this */
490 if (c->local_rtc)
491 ts.tv_sec = mktime(&tm);
492 else
493 ts.tv_sec = timegm(&tm);
494
495 clock_settime(CLOCK_REALTIME, &ts);
496 }
497
498 } else {
499 struct tm *tm;
500
501 /* Sync RTC from system clock */
502 if (c->local_rtc)
503 tm = localtime(&ts.tv_sec);
504 else
505 tm = gmtime(&ts.tv_sec);
506
507 clock_set_hwclock(tm);
508 }
509
510 log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
511
512 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
513
514 return sd_bus_reply_method_return(m, NULL);
515 }
516
517 static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
518 int relative, interactive;
519 Context *c = userdata;
520 int64_t utc;
521 struct timespec ts;
522 usec_t start;
523 struct tm* tm;
524 int r;
525
526 assert(m);
527 assert(c);
528
529 if (c->use_ntp)
530 return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
531
532 /* this only gets used if dbus does not provide a timestamp */
533 start = now(CLOCK_MONOTONIC);
534
535 r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
536 if (r < 0)
537 return r;
538
539 if (!relative && utc <= 0)
540 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
541
542 if (relative && utc == 0)
543 return sd_bus_reply_method_return(m, NULL);
544
545 if (relative) {
546 usec_t n, x;
547
548 n = now(CLOCK_REALTIME);
549 x = n + utc;
550
551 if ((utc > 0 && x < n) ||
552 (utc < 0 && x > n))
553 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
554
555 timespec_store(&ts, x);
556 } else
557 timespec_store(&ts, (usec_t) utc);
558
559 r = bus_verify_polkit_async(
560 m,
561 CAP_SYS_TIME,
562 "org.freedesktop.timedate1.set-time",
563 NULL,
564 interactive,
565 UID_INVALID,
566 &c->polkit_registry,
567 error);
568 if (r < 0)
569 return r;
570 if (r == 0)
571 return 1;
572
573 /* adjust ts for time spent in program */
574 r = sd_bus_message_get_monotonic_usec(m, &start);
575 /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
576 if (r < 0 && r != -ENODATA)
577 return r;
578
579 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
580
581 /* Set system clock */
582 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
583 log_error_errno(errno, "Failed to set local time: %m");
584 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
585 }
586
587 /* Sync down to RTC */
588 if (c->local_rtc)
589 tm = localtime(&ts.tv_sec);
590 else
591 tm = gmtime(&ts.tv_sec);
592 clock_set_hwclock(tm);
593
594 log_struct(LOG_INFO,
595 "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
596 "REALTIME="USEC_FMT, timespec_load(&ts),
597 LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
598 NULL);
599
600 return sd_bus_reply_method_return(m, NULL);
601 }
602
603 static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) {
604 int enabled, interactive;
605 Context *c = userdata;
606 int r;
607
608 assert(m);
609 assert(c);
610
611 r = sd_bus_message_read(m, "bb", &enabled, &interactive);
612 if (r < 0)
613 return r;
614
615 if ((bool)enabled == c->use_ntp)
616 return sd_bus_reply_method_return(m, NULL);
617
618 r = bus_verify_polkit_async(
619 m,
620 CAP_SYS_TIME,
621 "org.freedesktop.timedate1.set-ntp",
622 NULL,
623 interactive,
624 UID_INVALID,
625 &c->polkit_registry,
626 error);
627 if (r < 0)
628 return r;
629 if (r == 0)
630 return 1;
631
632 r = context_enable_ntp(sd_bus_message_get_bus(m), error, enabled);
633 if (r < 0)
634 return r;
635
636 r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled);
637 if (r < 0)
638 return r;
639
640 c->use_ntp = enabled;
641 log_info("Set NTP to %sd", enable_disable(enabled));
642
643 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
644
645 return sd_bus_reply_method_return(m, NULL);
646 }
647
648 static const sd_bus_vtable timedate_vtable[] = {
649 SD_BUS_VTABLE_START(0),
650 SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
651 SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
652 SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0),
653 SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
654 SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
655 SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
656 SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
657 SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
658 SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
659 SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
660 SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
661 SD_BUS_VTABLE_END,
662 };
663
664 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
665 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
666 int r;
667
668 assert(c);
669 assert(event);
670 assert(_bus);
671
672 r = sd_bus_default_system(&bus);
673 if (r < 0)
674 return log_error_errno(r, "Failed to get system bus connection: %m");
675
676 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
677 if (r < 0)
678 return log_error_errno(r, "Failed to register object: %m");
679
680 r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
681 if (r < 0)
682 return log_error_errno(r, "Failed to register name: %m");
683
684 r = sd_bus_attach_event(bus, event, 0);
685 if (r < 0)
686 return log_error_errno(r, "Failed to attach bus to event loop: %m");
687
688 *_bus = bus;
689 bus = NULL;
690
691 return 0;
692 }
693
694 int main(int argc, char *argv[]) {
695 Context context = {};
696 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
697 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
698 int r;
699
700 log_set_target(LOG_TARGET_AUTO);
701 log_parse_environment();
702 log_open();
703
704 umask(0022);
705
706 if (argc != 1) {
707 log_error("This program takes no arguments.");
708 r = -EINVAL;
709 goto finish;
710 }
711
712 r = sd_event_default(&event);
713 if (r < 0) {
714 log_error_errno(r, "Failed to allocate event loop: %m");
715 goto finish;
716 }
717
718 sd_event_set_watchdog(event, true);
719
720 r = connect_bus(&context, event, &bus);
721 if (r < 0)
722 goto finish;
723
724 (void) sd_bus_negotiate_timestamp(bus, true);
725
726 r = context_read_data(&context);
727 if (r < 0) {
728 log_error_errno(r, "Failed to read time zone data: %m");
729 goto finish;
730 }
731
732 r = context_read_ntp(&context, bus);
733 if (r < 0) {
734 log_error_errno(r, "Failed to determine whether NTP is enabled: %m");
735 goto finish;
736 }
737
738 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
739 if (r < 0) {
740 log_error_errno(r, "Failed to run event loop: %m");
741 goto finish;
742 }
743
744 finish:
745 context_free(&context);
746
747 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
748 }