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