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