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