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