chiark / gitweb /
0d4a9dd7e21230a02df7b9e3ba303cf0432fa4b5
[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", EOPNOTSUPP),
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(sd_bus *bus, sd_bus_error *error, bool enabled) {
226         int r;
227
228         assert(bus);
229         assert(error);
230
231         r = sd_bus_call_method(
232                 bus,
233                 "org.freedesktop.systemd1",
234                 "/org/freedesktop/systemd1",
235                 "org.freedesktop.systemd1.Manager",
236                 enabled ? "StartUnit" : "StopUnit",
237                 error,
238                 NULL,
239                 "ss",
240                 "systemd-timesyncd.service",
241                 "replace");
242         if (r < 0) {
243                 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
244                     sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
245                     sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit"))
246                         return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
247
248                 return r;
249         }
250
251         return 0;
252 }
253
254 static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) {
255         int r;
256
257         assert(bus);
258         assert(error);
259
260         if (enabled)
261                 r = sd_bus_call_method(
262                                 bus,
263                                 "org.freedesktop.systemd1",
264                                 "/org/freedesktop/systemd1",
265                                 "org.freedesktop.systemd1.Manager",
266                                 "EnableUnitFiles",
267                                 error,
268                                 NULL,
269                                 "asbb", 1,
270                                 "systemd-timesyncd.service",
271                                 false, true);
272         else
273                 r = sd_bus_call_method(
274                                 bus,
275                                 "org.freedesktop.systemd1",
276                                 "/org/freedesktop/systemd1",
277                                 "org.freedesktop.systemd1.Manager",
278                                 "DisableUnitFiles",
279                                 error,
280                                 NULL,
281                                 "asb", 1,
282                                 "systemd-timesyncd.service",
283                                 false);
284
285         if (r < 0) {
286                 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND))
287                         return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
288
289                 return r;
290         }
291
292         r = sd_bus_call_method(
293                         bus,
294                         "org.freedesktop.systemd1",
295                         "/org/freedesktop/systemd1",
296                         "org.freedesktop.systemd1.Manager",
297                         "Reload",
298                         error,
299                         NULL,
300                         NULL);
301         if (r < 0)
302                 return r;
303
304         return 0;
305 }
306
307 static int property_get_rtc_time(
308                 sd_bus *bus,
309                 const char *path,
310                 const char *interface,
311                 const char *property,
312                 sd_bus_message *reply,
313                 void *userdata,
314                 sd_bus_error *error) {
315
316         struct tm tm;
317         usec_t t;
318         int r;
319
320         zero(tm);
321         r = clock_get_hwclock(&tm);
322         if (r == -EBUSY) {
323                 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
324                 t = 0;
325         } else if (r == -ENOENT) {
326                 log_debug("/dev/rtc not found.");
327                 t = 0; /* no RTC found */
328         } else if (r < 0)
329                 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %s", strerror(-r));
330         else
331                 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
332
333         return sd_bus_message_append(reply, "t", t);
334 }
335
336 static int property_get_time(
337                 sd_bus *bus,
338                 const char *path,
339                 const char *interface,
340                 const char *property,
341                 sd_bus_message *reply,
342                 void *userdata,
343                 sd_bus_error *error) {
344
345         return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
346 }
347
348 static int property_get_ntp_sync(
349                 sd_bus *bus,
350                 const char *path,
351                 const char *interface,
352                 const char *property,
353                 sd_bus_message *reply,
354                 void *userdata,
355                 sd_bus_error *error) {
356
357         return sd_bus_message_append(reply, "b", ntp_synced());
358 }
359
360 static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
361         Context *c = userdata;
362         const char *z;
363         int interactive;
364         char *t;
365         int r;
366
367         assert(bus);
368         assert(m);
369         assert(c);
370
371         r = sd_bus_message_read(m, "sb", &z, &interactive);
372         if (r < 0)
373                 return r;
374
375         if (!timezone_is_valid(z))
376                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
377
378         if (streq_ptr(z, c->zone))
379                 return sd_bus_reply_method_return(m, NULL);
380
381         r = bus_verify_polkit_async(
382                         m,
383                         CAP_SYS_TIME,
384                         "org.freedesktop.timedate1.set-timezone",
385                         interactive,
386                         UID_INVALID,
387                         &c->polkit_registry,
388                         error);
389         if (r < 0)
390                 return r;
391         if (r == 0)
392                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
393
394         t = strdup(z);
395         if (!t)
396                 return -ENOMEM;
397
398         free(c->zone);
399         c->zone = t;
400
401         /* 1. Write new configuration file */
402         r = context_write_data_timezone(c);
403         if (r < 0) {
404                 log_error_errno(r, "Failed to set time zone: %m");
405                 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %s", strerror(-r));
406         }
407
408         /* 2. Tell the kernel our timezone */
409         clock_set_timezone(NULL);
410
411         if (c->local_rtc) {
412                 struct timespec ts;
413                 struct tm *tm;
414
415                 /* 3. Sync RTC from system clock, with the new delta */
416                 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
417                 assert_se(tm = localtime(&ts.tv_sec));
418                 clock_set_hwclock(tm);
419         }
420
421         log_struct(LOG_INFO,
422                    LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
423                    "TIMEZONE=%s", c->zone,
424                    LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
425                    NULL);
426
427         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
428
429         return sd_bus_reply_method_return(m, NULL);
430 }
431
432 static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
433         int lrtc, fix_system, interactive;
434         Context *c = userdata;
435         struct timespec ts;
436         int r;
437
438         assert(bus);
439         assert(m);
440         assert(c);
441
442         r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
443         if (r < 0)
444                 return r;
445
446         if (lrtc == c->local_rtc)
447                 return sd_bus_reply_method_return(m, NULL);
448
449         r = bus_verify_polkit_async(
450                         m,
451                         CAP_SYS_TIME,
452                         "org.freedesktop.timedate1.set-local-rtc",
453                         interactive,
454                         UID_INVALID,
455                         &c->polkit_registry,
456                         error);
457         if (r < 0)
458                 return r;
459         if (r == 0)
460                 return 1;
461
462         c->local_rtc = lrtc;
463
464         /* 1. Write new configuration file */
465         r = context_write_data_local_rtc(c);
466         if (r < 0) {
467                 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
468                 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %s", strerror(-r));
469         }
470
471         /* 2. Tell the kernel our timezone */
472         clock_set_timezone(NULL);
473
474         /* 3. Synchronize clocks */
475         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
476
477         if (fix_system) {
478                 struct tm tm;
479
480                 /* Sync system clock from RTC; first,
481                  * initialize the timezone fields of
482                  * struct tm. */
483                 if (c->local_rtc)
484                         tm = *localtime(&ts.tv_sec);
485                 else
486                         tm = *gmtime(&ts.tv_sec);
487
488                 /* Override the main fields of
489                  * struct tm, but not the timezone
490                  * fields */
491                 if (clock_get_hwclock(&tm) >= 0) {
492
493                         /* And set the system clock
494                          * with this */
495                         if (c->local_rtc)
496                                 ts.tv_sec = mktime(&tm);
497                         else
498                                 ts.tv_sec = timegm(&tm);
499
500                         clock_settime(CLOCK_REALTIME, &ts);
501                 }
502
503         } else {
504                 struct tm *tm;
505
506                 /* Sync RTC from system clock */
507                 if (c->local_rtc)
508                         tm = localtime(&ts.tv_sec);
509                 else
510                         tm = gmtime(&ts.tv_sec);
511
512                 clock_set_hwclock(tm);
513         }
514
515         log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
516
517         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
518
519         return sd_bus_reply_method_return(m, NULL);
520 }
521
522 static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
523         int relative, interactive;
524         Context *c = userdata;
525         int64_t utc;
526         struct timespec ts;
527         usec_t start;
528         struct tm* tm;
529         int r;
530
531         assert(bus);
532         assert(m);
533         assert(c);
534
535         if (c->use_ntp)
536                 return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
537
538         /* this only gets used if dbus does not provide a timestamp */
539         start = now(CLOCK_MONOTONIC);
540
541         r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
542         if (r < 0)
543                 return r;
544
545         if (!relative && utc <= 0)
546                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
547
548         if (relative && utc == 0)
549                 return sd_bus_reply_method_return(m, NULL);
550
551         if (relative) {
552                 usec_t n, x;
553
554                 n = now(CLOCK_REALTIME);
555                 x = n + utc;
556
557                 if ((utc > 0 && x < n) ||
558                     (utc < 0 && x > n))
559                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
560
561                 timespec_store(&ts, x);
562         } else
563                 timespec_store(&ts, (usec_t) utc);
564
565         r = bus_verify_polkit_async(
566                         m,
567                         CAP_SYS_TIME,
568                         "org.freedesktop.timedate1.set-time",
569                         interactive,
570                         UID_INVALID,
571                         &c->polkit_registry,
572                         error);
573         if (r < 0)
574                 return r;
575         if (r == 0)
576                 return 1;
577
578         /* adjust ts for time spent in program */
579         r = sd_bus_message_get_monotonic_usec(m, &start);
580         /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */
581         if (r < 0 && r != -ENODATA)
582                 return r;
583
584         timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
585
586         /* Set system clock */
587         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
588                 log_error_errno(errno, "Failed to set local time: %m");
589                 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
590         }
591
592         /* Sync down to RTC */
593         if (c->local_rtc)
594                 tm = localtime(&ts.tv_sec);
595         else
596                 tm = gmtime(&ts.tv_sec);
597         clock_set_hwclock(tm);
598
599         log_struct(LOG_INFO,
600                    LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
601                    "REALTIME="USEC_FMT, timespec_load(&ts),
602                    LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
603                    NULL);
604
605         return sd_bus_reply_method_return(m, NULL);
606 }
607
608 static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
609         int enabled, interactive;
610         Context *c = userdata;
611         int r;
612
613         r = sd_bus_message_read(m, "bb", &enabled, &interactive);
614         if (r < 0)
615                 return r;
616
617         if ((bool)enabled == c->use_ntp)
618                 return sd_bus_reply_method_return(m, NULL);
619
620         r = bus_verify_polkit_async(
621                         m,
622                         CAP_SYS_TIME,
623                         "org.freedesktop.timedate1.set-ntp",
624                         interactive,
625                         UID_INVALID,
626                         &c->polkit_registry,
627                         error);
628         if (r < 0)
629                 return r;
630         if (r == 0)
631                 return 1;
632
633         r = context_enable_ntp(bus, error, enabled);
634         if (r < 0)
635                 return r;
636
637         r = context_start_ntp(bus, error, enabled);
638         if (r < 0)
639                 return r;
640
641         c->use_ntp = enabled;
642         log_info("Set NTP to %s", enabled ? "enabled" : "disabled");
643
644         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
645
646         return sd_bus_reply_method_return(m, NULL);
647 }
648
649 static const sd_bus_vtable timedate_vtable[] = {
650         SD_BUS_VTABLE_START(0),
651         SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
652         SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
653         SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0),
654         SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
655         SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
656         SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
657         SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
658         SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
659         SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
660         SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
661         SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
662         SD_BUS_VTABLE_END,
663 };
664
665 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
666         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
667         int r;
668
669         assert(c);
670         assert(event);
671         assert(_bus);
672
673         r = sd_bus_default_system(&bus);
674         if (r < 0)
675                 return log_error_errno(r, "Failed to get system bus connection: %m");
676
677         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
678         if (r < 0)
679                 return log_error_errno(r, "Failed to register object: %m");
680
681         r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
682         if (r < 0)
683                 return log_error_errno(r, "Failed to register name: %m");
684
685         r = sd_bus_attach_event(bus, event, 0);
686         if (r < 0)
687                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
688
689         *_bus = bus;
690         bus = NULL;
691
692         return 0;
693 }
694
695 int main(int argc, char *argv[]) {
696         Context context = {};
697         _cleanup_event_unref_ sd_event *event = NULL;
698         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
699         int r;
700
701         log_set_target(LOG_TARGET_AUTO);
702         log_parse_environment();
703         log_open();
704
705         umask(0022);
706
707         if (argc != 1) {
708                 log_error("This program takes no arguments.");
709                 r = -EINVAL;
710                 goto finish;
711         }
712
713         r = sd_event_default(&event);
714         if (r < 0) {
715                 log_error_errno(r, "Failed to allocate event loop: %m");
716                 goto finish;
717         }
718
719         sd_event_set_watchdog(event, true);
720
721         r = connect_bus(&context, event, &bus);
722         if (r < 0)
723                 goto finish;
724
725         (void) sd_bus_negotiate_timestamp(bus, true);
726
727         r = context_read_data(&context);
728         if (r < 0) {
729                 log_error_errno(r, "Failed to read time zone data: %m");
730                 goto finish;
731         }
732
733         r = context_read_ntp(&context, bus);
734         if (r < 0) {
735                 log_error_errno(r, "Failed to determine whether NTP is enabled: %m");
736                 goto finish;
737         }
738
739         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
740         if (r < 0) {
741                 log_error_errno(r, "Failed to run event loop: %m");
742                 goto finish;
743         }
744
745 finish:
746         context_free(&context);
747
748         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
749 }