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