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