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