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