chiark / gitweb /
bus: add SD_BUS_NAME_REPLACE_EXISTING to all activatable services, fix one flags...
[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         unsigned can_ntp;
49         unsigned 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         context_reset(c);
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 property_get_rtc_time(
456                 sd_bus *bus,
457                 const char *path,
458                 const char *interface,
459                 const char *property,
460                 sd_bus_message *reply,
461                 void *userdata,
462                 sd_bus_error *error) {
463
464         struct tm tm;
465         usec_t t;
466         int r;
467
468         zero(tm);
469         r = hwclock_get_time(&tm);
470         if (r == -EBUSY) {
471                 log_warning("/dev/rtc is busy, is somebody keeping it open continously? That's not a good idea... Returning a bogus RTC timestamp.");
472                 t = 0;
473         } else if (r == -ENOENT) {
474                 log_debug("Not /dev/rtc found.");
475                 t = 0; /* no RTC found */
476         } else if (r < 0)
477                 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %s", strerror(-r));
478         else
479                 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
480
481         return sd_bus_message_append(reply, "t", t);
482 }
483
484 static int property_get_time(
485                 sd_bus *bus,
486                 const char *path,
487                 const char *interface,
488                 const char *property,
489                 sd_bus_message *reply,
490                 void *userdata,
491                 sd_bus_error *error) {
492
493         return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
494 }
495
496 static int property_get_ntp_sync(
497                 sd_bus *bus,
498                 const char *path,
499                 const char *interface,
500                 const char *property,
501                 sd_bus_message *reply,
502                 void *userdata,
503                 sd_bus_error *error) {
504
505         return sd_bus_message_append(reply, "b", ntp_synced());
506 }
507
508 static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
509         Context *c = userdata;
510         const char *z;
511         int interactive;
512         char *t;
513         int r;
514
515         assert(bus);
516         assert(m);
517         assert(c);
518
519         r = sd_bus_message_read(m, "sb", &z, &interactive);
520         if (r < 0)
521                 return r;
522
523         if (!valid_timezone(z))
524                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
525
526         if (streq_ptr(z, c->zone))
527                 return sd_bus_reply_method_return(m, NULL);
528
529         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-timezone", interactive, error, method_set_timezone, c);
530         if (r < 0)
531                 return r;
532         if (r == 0)
533                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
534
535         t = strdup(z);
536         if (!t)
537                 return -ENOMEM;
538
539         free(c->zone);
540         c->zone = t;
541
542         /* 1. Write new configuration file */
543         r = context_write_data_timezone(c);
544         if (r < 0) {
545                 log_error("Failed to set timezone: %s", strerror(-r));
546                 return sd_bus_error_set_errnof(error, r, "Failed to set timezone: %s", strerror(-r));
547         }
548
549         /* 2. Tell the kernel our timezone */
550         hwclock_set_timezone(NULL);
551
552         if (c->local_rtc) {
553                 struct timespec ts;
554                 struct tm *tm;
555
556                 /* 3. Sync RTC from system clock, with the new delta */
557                 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
558                 assert_se(tm = localtime(&ts.tv_sec));
559                 hwclock_set_time(tm);
560         }
561
562         log_struct(LOG_INFO,
563                    MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
564                    "TIMEZONE=%s", c->zone,
565                    "MESSAGE=Changed timezone to '%s'.", c->zone,
566                    NULL);
567
568         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
569
570         return sd_bus_reply_method_return(m, NULL);
571 }
572
573 static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
574         int lrtc, fix_system, interactive;
575         Context *c = userdata;
576         struct timespec ts;
577         int r;
578
579         assert(bus);
580         assert(m);
581         assert(c);
582
583         r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
584         if (r < 0)
585                 return r;
586
587         if (lrtc == c->local_rtc)
588                 return sd_bus_reply_method_return(m, NULL);
589
590         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-local-rtc", interactive, error, method_set_local_rtc, c);
591         if (r < 0)
592                 return r;
593         if (r == 0)
594                 return 1;
595
596         c->local_rtc = lrtc;
597
598         /* 1. Write new configuration file */
599         r = context_write_data_local_rtc(c);
600         if (r < 0) {
601                 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
602                 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %s", strerror(-r));
603         }
604
605         /* 2. Tell the kernel our timezone */
606         hwclock_set_timezone(NULL);
607
608         /* 3. Synchronize clocks */
609         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
610
611         if (fix_system) {
612                 struct tm tm;
613
614                 /* Sync system clock from RTC; first,
615                  * initialize the timezone fields of
616                  * struct tm. */
617                 if (c->local_rtc)
618                         tm = *localtime(&ts.tv_sec);
619                 else
620                         tm = *gmtime(&ts.tv_sec);
621
622                 /* Override the main fields of
623                  * struct tm, but not the timezone
624                  * fields */
625                 if (hwclock_get_time(&tm) >= 0) {
626
627                         /* And set the system clock
628                          * with this */
629                         if (c->local_rtc)
630                                 ts.tv_sec = mktime(&tm);
631                         else
632                                 ts.tv_sec = timegm(&tm);
633
634                         clock_settime(CLOCK_REALTIME, &ts);
635                 }
636
637         } else {
638                 struct tm *tm;
639
640                 /* Sync RTC from system clock */
641                 if (c->local_rtc)
642                         tm = localtime(&ts.tv_sec);
643                 else
644                         tm = gmtime(&ts.tv_sec);
645
646                 hwclock_set_time(tm);
647         }
648
649         log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
650
651         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
652
653         return sd_bus_reply_method_return(m, NULL);
654 }
655
656 static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
657         int relative, interactive;
658         Context *c = userdata;
659         int64_t utc;
660         struct timespec ts;
661         struct tm* tm;
662         int r;
663
664         assert(bus);
665         assert(m);
666         assert(c);
667
668         r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
669         if (r < 0)
670                 return r;
671
672         if (!relative && utc <= 0)
673                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
674
675         if (relative && utc == 0)
676                 return sd_bus_reply_method_return(m, NULL);
677
678         if (relative) {
679                 usec_t n, x;
680
681                 n = now(CLOCK_REALTIME);
682                 x = n + utc;
683
684                 if ((utc > 0 && x < n) ||
685                     (utc < 0 && x > n))
686                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
687
688                 timespec_store(&ts, x);
689         } else
690                 timespec_store(&ts, (usec_t) utc);
691
692         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-time", interactive, error, method_set_time, c);
693         if (r < 0)
694                 return r;
695         if (r == 0)
696                 return 1;
697
698         /* Set system clock */
699         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
700                 log_error("Failed to set local time: %m");
701                 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
702         }
703
704         /* Sync down to RTC */
705         if (c->local_rtc)
706                 tm = localtime(&ts.tv_sec);
707         else
708                 tm = gmtime(&ts.tv_sec);
709
710         hwclock_set_time(tm);
711
712         log_struct(LOG_INFO,
713                    MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
714                    "REALTIME=%llu", (unsigned long long) timespec_load(&ts),
715                    "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec),
716                    NULL);
717
718         return sd_bus_reply_method_return(m, NULL);
719 }
720
721 static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
722         int ntp, interactive;
723         Context *c = userdata;
724         int r;
725
726         r = sd_bus_message_read(m, "bb", &ntp, &interactive);
727         if (r < 0)
728                 return r;
729
730         if ((bool)ntp == c->use_ntp)
731                 return sd_bus_reply_method_return(m, NULL);
732
733         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-ntp", interactive, error, method_set_ntp, c);
734         if (r < 0)
735                 return r;
736         if (r == 0)
737                 return 1;
738
739         c->use_ntp = ntp;
740
741         r = context_enable_ntp(c, bus, error);
742         if (r < 0)
743                 return r;
744
745         r = context_start_ntp(c, bus, error);
746         if (r < 0)
747                 return r;
748
749         log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
750
751         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
752
753         return sd_bus_reply_method_return(m, NULL);
754 }
755
756 #include <sys/capability.h>
757
758 static const sd_bus_vtable timedate_vtable[] = {
759         SD_BUS_VTABLE_START(0),
760         SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
761         SD_BUS_PROPERTY("LocalRTC", "b", NULL, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
762         SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_tristate, offsetof(Context, can_ntp), 0),
763         SD_BUS_PROPERTY("NTP", "b", bus_property_get_tristate, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
764         SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
765         SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
766         SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
767         SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
768         SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
769         SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
770         SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
771         SD_BUS_VTABLE_END,
772 };
773
774 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
775         _cleanup_bus_unref_ sd_bus *bus = NULL;
776         int r;
777
778         assert(c);
779         assert(event);
780         assert(_bus);
781
782         r = sd_bus_default_system(&bus);
783         if (r < 0) {
784                 log_error("Failed to get system bus connection: %s", strerror(-r));
785                 return r;
786         }
787
788         r = sd_bus_add_object_vtable(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
789         if (r < 0) {
790                 log_error("Failed to register object: %s", strerror(-r));
791                 return r;
792         }
793
794         r = sd_bus_request_name(bus, "org.freedesktop.timedate1", SD_BUS_NAME_REPLACE_EXISTING);
795         if (r < 0) {
796                 log_error("Failed to register name: %s", strerror(-r));
797                 return r;
798         }
799
800         r = sd_bus_attach_event(bus, event, 0);
801         if (r < 0) {
802                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
803                 return r;
804         }
805
806         *_bus = bus;
807         bus = NULL;
808
809         return 0;
810 }
811
812 int main(int argc, char *argv[]) {
813         Context context = {
814                 .zone = NULL,
815                 .local_rtc = false,
816                 .can_ntp = -1,
817                 .use_ntp = -1,
818         };
819
820         _cleanup_event_unref_ sd_event *event = NULL;
821         _cleanup_bus_unref_ sd_bus *bus = NULL;
822         int r;
823
824         log_set_target(LOG_TARGET_AUTO);
825         log_parse_environment();
826         log_open();
827
828         umask(0022);
829
830         if (argc != 1) {
831                 log_error("This program takes no arguments.");
832                 r = -EINVAL;
833                 goto finish;
834         }
835
836         r = sd_event_default(&event);
837         if (r < 0) {
838                 log_error("Failed to allocate event loop: %s", strerror(-r));
839                 goto finish;
840         }
841
842         sd_event_set_watchdog(event, true);
843
844         r = connect_bus(&context, event, &bus);
845         if (r < 0)
846                 goto finish;
847
848         r = context_read_data(&context);
849         if (r < 0) {
850                 log_error("Failed to read timezone data: %s", strerror(-r));
851                 goto finish;
852         }
853
854         r = context_read_ntp(&context, bus);
855         if (r < 0) {
856                 log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
857                 goto finish;
858         }
859
860         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC);
861         if (r < 0) {
862                 log_error("Failed to run event loop: %s", strerror(-r));
863                 goto finish;
864         }
865
866         r = 0;
867
868 finish:
869         context_free(&context, bus);
870
871         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
872 }