chiark / gitweb /
88d57e9d5432da609c5b989ed460aec6c500a304
[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         usec_t start;
544         struct tm* tm;
545         int r;
546
547         assert(bus);
548         assert(m);
549         assert(c);
550
551         if (c->use_ntp)
552                 return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
553
554         r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
555         if (r < 0)
556                 return r;
557
558         if (!relative && utc <= 0)
559                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
560
561         if (relative && utc == 0)
562                 return sd_bus_reply_method_return(m, NULL);
563
564         if (relative) {
565                 usec_t n, x;
566
567                 n = now(CLOCK_REALTIME);
568                 x = n + utc;
569
570                 if ((utc > 0 && x < n) ||
571                     (utc < 0 && x > n))
572                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
573
574                 timespec_store(&ts, x);
575         } else
576                 timespec_store(&ts, (usec_t) utc);
577
578         r = bus_verify_polkit_async(
579                         m,
580                         CAP_SYS_TIME,
581                         "org.freedesktop.timedate1.set-time",
582                         interactive,
583                         UID_INVALID,
584                         &c->polkit_registry,
585                         error);
586         if (r < 0)
587                 return r;
588         if (r == 0)
589                 return 1;
590
591         /* adjust ts for time spent in program */
592         r = sd_bus_message_get_monotonic_usec(m, &start);
593         if (r < 0 && r != -ENODATA)
594                 return r;
595         if (r >= 0)
596                 timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start));
597
598         /* Set system clock */
599         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
600                 log_error_errno(errno, "Failed to set local time: %m");
601                 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
602         }
603
604         /* Sync down to RTC */
605         if (c->local_rtc)
606                 tm = localtime(&ts.tv_sec);
607         else
608                 tm = gmtime(&ts.tv_sec);
609         clock_set_hwclock(tm);
610
611         log_struct(LOG_INFO,
612                    LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
613                    "REALTIME="USEC_FMT, timespec_load(&ts),
614                    LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
615                    NULL);
616
617         return sd_bus_reply_method_return(m, NULL);
618 }
619
620 static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
621         int ntp, interactive;
622         Context *c = userdata;
623         int r;
624
625         r = sd_bus_message_read(m, "bb", &ntp, &interactive);
626         if (r < 0)
627                 return r;
628
629         if ((bool)ntp == c->use_ntp)
630                 return sd_bus_reply_method_return(m, NULL);
631
632         r = bus_verify_polkit_async(
633                         m,
634                         CAP_SYS_TIME,
635                         "org.freedesktop.timedate1.set-ntp",
636                         interactive,
637                         UID_INVALID,
638                         &c->polkit_registry,
639                         error);
640         if (r < 0)
641                 return r;
642         if (r == 0)
643                 return 1;
644
645         c->use_ntp = ntp;
646
647         r = context_enable_ntp(c, bus, error);
648         if (r < 0)
649                 return r;
650
651         r = context_start_ntp(c, bus, error);
652         if (r < 0)
653                 return r;
654
655         log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
656
657         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
658
659         return sd_bus_reply_method_return(m, NULL);
660 }
661
662 static const sd_bus_vtable timedate_vtable[] = {
663         SD_BUS_VTABLE_START(0),
664         SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
665         SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
666         SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0),
667         SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
668         SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
669         SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
670         SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
671         SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
672         SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
673         SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
674         SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
675         SD_BUS_VTABLE_END,
676 };
677
678 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
679         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
680         int r;
681
682         assert(c);
683         assert(event);
684         assert(_bus);
685
686         r = sd_bus_default_system(&bus);
687         if (r < 0)
688                 return log_error_errno(r, "Failed to get system bus connection: %m");
689
690         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
691         if (r < 0)
692                 return log_error_errno(r, "Failed to register object: %m");
693
694         r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
695         if (r < 0)
696                 return log_error_errno(r, "Failed to register name: %m");
697
698         r = sd_bus_attach_event(bus, event, 0);
699         if (r < 0)
700                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
701
702         *_bus = bus;
703         bus = NULL;
704
705         return 0;
706 }
707
708 int main(int argc, char *argv[]) {
709         Context context = {};
710         _cleanup_event_unref_ sd_event *event = NULL;
711         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
712         int r;
713
714         log_set_target(LOG_TARGET_AUTO);
715         log_parse_environment();
716         log_open();
717
718         umask(0022);
719
720         if (argc != 1) {
721                 log_error("This program takes no arguments.");
722                 r = -EINVAL;
723                 goto finish;
724         }
725
726         r = sd_event_default(&event);
727         if (r < 0) {
728                 log_error_errno(r, "Failed to allocate event loop: %m");
729                 goto finish;
730         }
731
732         sd_event_set_watchdog(event, true);
733
734         r = connect_bus(&context, event, &bus);
735         if (r < 0)
736                 goto finish;
737
738         (void)sd_bus_negotiate_timestamp(bus, true);
739
740         r = context_read_data(&context);
741         if (r < 0) {
742                 log_error_errno(r, "Failed to read time zone data: %m");
743                 goto finish;
744         }
745
746         r = context_read_ntp(&context, bus);
747         if (r < 0) {
748                 log_error_errno(r, "Failed to determine whether NTP is enabled: %m");
749                 goto finish;
750         }
751
752         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
753         if (r < 0) {
754                 log_error_errno(r, "Failed to run event loop: %m");
755                 goto finish;
756         }
757
758 finish:
759         context_free(&context);
760
761         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
762 }