chiark / gitweb /
shared: rename hwclock.[ch] to clock-util.[ch]
[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 "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 time zone 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 time zone 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 = clock_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                                 if (ferror(f))
267                                         log_error("Failed to read NTP unit file: %m");
268
269                                 break;
270                         }
271
272                         l = strstrip(line);
273                         if (l[0] == 0 || l[0] == '#')
274                                 continue;
275
276                         if (strv_extend(&r, l) < 0) {
277                                 log_oom();
278                                 return NULL;
279                         }
280                 }
281         }
282
283         i = r;
284         r = NULL; /* avoid cleanup */
285
286         return strv_uniq(i);
287 }
288
289 static int context_read_ntp(Context *c, sd_bus *bus) {
290         _cleanup_strv_free_ char **l;
291         char **i;
292         int r;
293
294         assert(c);
295         assert(bus);
296
297         l = get_ntp_services();
298         STRV_FOREACH(i, l) {
299                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
300                 sd_bus_message *reply = NULL;
301                 const char *s;
302
303                 r = sd_bus_call_method(
304                                 bus,
305                                 "org.freedesktop.systemd1",
306                                 "/org/freedesktop/systemd1",
307                                 "org.freedesktop.systemd1.Manager",
308                                 "GetUnitFileState",
309                                 &error,
310                                 &reply,
311                                 "s",
312                                 *i);
313
314                 if (r < 0) {
315                         /* This implementation does not exist. Try the next one. */
316                         if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND))
317                                 continue;
318
319                         return r;
320                 }
321
322                 r = sd_bus_message_read(reply, "s", &s);
323                 if (r < 0)
324                         return r;
325
326                 c->can_ntp = 1;
327                 c->use_ntp =
328                         streq(s, "enabled") ||
329                         streq(s, "enabled-runtime");
330
331                 return 0;
332         }
333
334         /* NTP is not installed. */
335         c->can_ntp = 0;
336         c->use_ntp = 0;
337
338         return 0;
339 }
340
341 static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
342         _cleanup_strv_free_ char **l = NULL;
343         char **i;
344         int r;
345
346         assert(c);
347         assert(bus);
348         assert(error);
349
350         l = get_ntp_services();
351         STRV_FOREACH(i, l) {
352
353                 if (c->use_ntp)
354                         r = sd_bus_call_method(
355                                         bus,
356                                         "org.freedesktop.systemd1",
357                                         "/org/freedesktop/systemd1",
358                                         "org.freedesktop.systemd1.Manager",
359                                         "StartUnit",
360                                         error,
361                                         NULL,
362                                         "ss", *i, "replace");
363                 else
364                         r = sd_bus_call_method(
365                                         bus,
366                                         "org.freedesktop.systemd1",
367                                         "/org/freedesktop/systemd1",
368                                         "org.freedesktop.systemd1.Manager",
369                                         "StopUnit",
370                                         error,
371                                         NULL,
372                                         "ss", *i, "replace");
373
374                 if (r < 0) {
375                         if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
376                             sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
377                             sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) {
378                                 /* This implementation does not exist. Try the next one. */
379                                 sd_bus_error_free(error);
380                                 continue;
381                         }
382
383                         return r;
384                 }
385
386                 return 1;
387         }
388
389         sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
390         return -ENOTSUP;
391 }
392
393 static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) {
394         _cleanup_strv_free_ char **l = NULL;
395         char **i;
396         int r;
397
398         assert(c);
399         assert(bus);
400         assert(error);
401
402         l = get_ntp_services();
403         STRV_FOREACH(i, l) {
404                 if (c->use_ntp)
405                         r = sd_bus_call_method(
406                                         bus,
407                                         "org.freedesktop.systemd1",
408                                         "/org/freedesktop/systemd1",
409                                         "org.freedesktop.systemd1.Manager",
410                                         "EnableUnitFiles",
411                                         error,
412                                         NULL,
413                                         "asbb", 1, *i, false, true);
414                 else
415                         r = sd_bus_call_method(
416                                         bus,
417                                         "org.freedesktop.systemd1",
418                                         "/org/freedesktop/systemd1",
419                                         "org.freedesktop.systemd1.Manager",
420                                         "DisableUnitFiles",
421                                         error,
422                                         NULL,
423                                         "asb", 1, *i, false);
424
425                 if (r < 0) {
426                         if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) {
427                                 /* This implementation does not exist. Try the next one. */
428                                 sd_bus_error_free(error);
429                                 continue;
430                         }
431
432                         return r;
433                 }
434
435                 r = sd_bus_call_method(
436                                 bus,
437                                 "org.freedesktop.systemd1",
438                                 "/org/freedesktop/systemd1",
439                                 "org.freedesktop.systemd1.Manager",
440                                 "Reload",
441                                 error,
442                                 NULL,
443                                 NULL);
444                 if (r < 0)
445                         return r;
446
447                 return 1;
448         }
449
450         sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
451         return -ENOTSUP;
452 }
453
454 static int property_get_rtc_time(
455                 sd_bus *bus,
456                 const char *path,
457                 const char *interface,
458                 const char *property,
459                 sd_bus_message *reply,
460                 void *userdata,
461                 sd_bus_error *error) {
462
463         struct tm tm;
464         usec_t t;
465         int r;
466
467         zero(tm);
468         r = clock_get_time(&tm);
469         if (r == -EBUSY) {
470                 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
471                 t = 0;
472         } else if (r == -ENOENT) {
473                 log_debug("/dev/rtc not found.");
474                 t = 0; /* no RTC found */
475         } else if (r < 0)
476                 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %s", strerror(-r));
477         else
478                 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
479
480         return sd_bus_message_append(reply, "t", t);
481 }
482
483 static int property_get_time(
484                 sd_bus *bus,
485                 const char *path,
486                 const char *interface,
487                 const char *property,
488                 sd_bus_message *reply,
489                 void *userdata,
490                 sd_bus_error *error) {
491
492         return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
493 }
494
495 static int property_get_ntp_sync(
496                 sd_bus *bus,
497                 const char *path,
498                 const char *interface,
499                 const char *property,
500                 sd_bus_message *reply,
501                 void *userdata,
502                 sd_bus_error *error) {
503
504         return sd_bus_message_append(reply, "b", ntp_synced());
505 }
506
507 static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
508         Context *c = userdata;
509         const char *z;
510         int interactive;
511         char *t;
512         int r;
513
514         assert(bus);
515         assert(m);
516         assert(c);
517
518         r = sd_bus_message_read(m, "sb", &z, &interactive);
519         if (r < 0)
520                 return r;
521
522         if (!valid_timezone(z))
523                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
524
525         if (streq_ptr(z, c->zone))
526                 return sd_bus_reply_method_return(m, NULL);
527
528         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-timezone", interactive, error, method_set_timezone, c);
529         if (r < 0)
530                 return r;
531         if (r == 0)
532                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
533
534         t = strdup(z);
535         if (!t)
536                 return -ENOMEM;
537
538         free(c->zone);
539         c->zone = t;
540
541         /* 1. Write new configuration file */
542         r = context_write_data_timezone(c);
543         if (r < 0) {
544                 log_error("Failed to set time zone: %s", strerror(-r));
545                 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %s", strerror(-r));
546         }
547
548         /* 2. Tell the kernel our timezone */
549         clock_set_timezone(NULL);
550
551         if (c->local_rtc) {
552                 struct timespec ts;
553                 struct tm *tm;
554
555                 /* 3. Sync RTC from system clock, with the new delta */
556                 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
557                 assert_se(tm = localtime(&ts.tv_sec));
558                 clock_set_time(tm);
559         }
560
561         log_struct(LOG_INFO,
562                    MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
563                    "TIMEZONE=%s", c->zone,
564                    "MESSAGE=Changed time zone to '%s'.", c->zone,
565                    NULL);
566
567         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
568
569         return sd_bus_reply_method_return(m, NULL);
570 }
571
572 static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
573         int lrtc, fix_system, interactive;
574         Context *c = userdata;
575         struct timespec ts;
576         int r;
577
578         assert(bus);
579         assert(m);
580         assert(c);
581
582         r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
583         if (r < 0)
584                 return r;
585
586         if (lrtc == c->local_rtc)
587                 return sd_bus_reply_method_return(m, NULL);
588
589         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-local-rtc", interactive, error, method_set_local_rtc, c);
590         if (r < 0)
591                 return r;
592         if (r == 0)
593                 return 1;
594
595         c->local_rtc = lrtc;
596
597         /* 1. Write new configuration file */
598         r = context_write_data_local_rtc(c);
599         if (r < 0) {
600                 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
601                 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %s", strerror(-r));
602         }
603
604         /* 2. Tell the kernel our timezone */
605         clock_set_timezone(NULL);
606
607         /* 3. Synchronize clocks */
608         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
609
610         if (fix_system) {
611                 struct tm tm;
612
613                 /* Sync system clock from RTC; first,
614                  * initialize the timezone fields of
615                  * struct tm. */
616                 if (c->local_rtc)
617                         tm = *localtime(&ts.tv_sec);
618                 else
619                         tm = *gmtime(&ts.tv_sec);
620
621                 /* Override the main fields of
622                  * struct tm, but not the timezone
623                  * fields */
624                 if (clock_get_time(&tm) >= 0) {
625
626                         /* And set the system clock
627                          * with this */
628                         if (c->local_rtc)
629                                 ts.tv_sec = mktime(&tm);
630                         else
631                                 ts.tv_sec = timegm(&tm);
632
633                         clock_settime(CLOCK_REALTIME, &ts);
634                 }
635
636         } else {
637                 struct tm *tm;
638
639                 /* Sync RTC from system clock */
640                 if (c->local_rtc)
641                         tm = localtime(&ts.tv_sec);
642                 else
643                         tm = gmtime(&ts.tv_sec);
644
645                 clock_set_time(tm);
646         }
647
648         log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
649
650         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
651
652         return sd_bus_reply_method_return(m, NULL);
653 }
654
655 static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
656         int relative, interactive;
657         Context *c = userdata;
658         int64_t utc;
659         struct timespec ts;
660         struct tm* tm;
661         int r;
662
663         assert(bus);
664         assert(m);
665         assert(c);
666
667         r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
668         if (r < 0)
669                 return r;
670
671         if (!relative && utc <= 0)
672                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
673
674         if (relative && utc == 0)
675                 return sd_bus_reply_method_return(m, NULL);
676
677         if (relative) {
678                 usec_t n, x;
679
680                 n = now(CLOCK_REALTIME);
681                 x = n + utc;
682
683                 if ((utc > 0 && x < n) ||
684                     (utc < 0 && x > n))
685                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
686
687                 timespec_store(&ts, x);
688         } else
689                 timespec_store(&ts, (usec_t) utc);
690
691         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-time", interactive, error, method_set_time, c);
692         if (r < 0)
693                 return r;
694         if (r == 0)
695                 return 1;
696
697         /* Set system clock */
698         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
699                 log_error("Failed to set local time: %m");
700                 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
701         }
702
703         /* Sync down to RTC */
704         if (c->local_rtc)
705                 tm = localtime(&ts.tv_sec);
706         else
707                 tm = gmtime(&ts.tv_sec);
708
709         clock_set_time(tm);
710
711         log_struct(LOG_INFO,
712                    MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
713                    "REALTIME="USEC_FMT, timespec_load(&ts),
714                    "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec),
715                    NULL);
716
717         return sd_bus_reply_method_return(m, NULL);
718 }
719
720 static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
721         int ntp, interactive;
722         Context *c = userdata;
723         int r;
724
725         r = sd_bus_message_read(m, "bb", &ntp, &interactive);
726         if (r < 0)
727                 return r;
728
729         if ((bool)ntp == c->use_ntp)
730                 return sd_bus_reply_method_return(m, NULL);
731
732         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.timedate1.set-ntp", interactive, error, method_set_ntp, c);
733         if (r < 0)
734                 return r;
735         if (r == 0)
736                 return 1;
737
738         c->use_ntp = ntp;
739
740         r = context_enable_ntp(c, bus, error);
741         if (r < 0)
742                 return r;
743
744         r = context_start_ntp(c, bus, error);
745         if (r < 0)
746                 return r;
747
748         log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
749
750         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
751
752         return sd_bus_reply_method_return(m, NULL);
753 }
754
755 #include <sys/capability.h>
756
757 static const sd_bus_vtable timedate_vtable[] = {
758         SD_BUS_VTABLE_START(0),
759         SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
760         SD_BUS_PROPERTY("LocalRTC", "b", NULL, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
761         SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_tristate, offsetof(Context, can_ntp), 0),
762         SD_BUS_PROPERTY("NTP", "b", bus_property_get_tristate, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
763         SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
764         SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
765         SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
766         SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
767         SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
768         SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
769         SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
770         SD_BUS_VTABLE_END,
771 };
772
773 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
774         _cleanup_bus_unref_ sd_bus *bus = NULL;
775         int r;
776
777         assert(c);
778         assert(event);
779         assert(_bus);
780
781         r = sd_bus_default_system(&bus);
782         if (r < 0) {
783                 log_error("Failed to get system bus connection: %s", strerror(-r));
784                 return r;
785         }
786
787         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
788         if (r < 0) {
789                 log_error("Failed to register object: %s", strerror(-r));
790                 return r;
791         }
792
793         r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
794         if (r < 0) {
795                 log_error("Failed to register name: %s", strerror(-r));
796                 return r;
797         }
798
799         r = sd_bus_attach_event(bus, event, 0);
800         if (r < 0) {
801                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
802                 return r;
803         }
804
805         *_bus = bus;
806         bus = NULL;
807
808         return 0;
809 }
810
811 int main(int argc, char *argv[]) {
812         Context context = {
813                 .zone = NULL,
814                 .local_rtc = false,
815                 .can_ntp = -1,
816                 .use_ntp = -1,
817         };
818
819         _cleanup_event_unref_ sd_event *event = NULL;
820         _cleanup_bus_unref_ sd_bus *bus = NULL;
821         int r;
822
823         log_set_target(LOG_TARGET_AUTO);
824         log_parse_environment();
825         log_open();
826
827         umask(0022);
828
829         if (argc != 1) {
830                 log_error("This program takes no arguments.");
831                 r = -EINVAL;
832                 goto finish;
833         }
834
835         r = sd_event_default(&event);
836         if (r < 0) {
837                 log_error("Failed to allocate event loop: %s", strerror(-r));
838                 goto finish;
839         }
840
841         sd_event_set_watchdog(event, true);
842
843         r = connect_bus(&context, event, &bus);
844         if (r < 0)
845                 goto finish;
846
847         r = context_read_data(&context);
848         if (r < 0) {
849                 log_error("Failed to read time zone data: %s", strerror(-r));
850                 goto finish;
851         }
852
853         r = context_read_ntp(&context, bus);
854         if (r < 0) {
855                 log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
856                 goto finish;
857         }
858
859         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
860         if (r < 0) {
861                 log_error("Failed to run event loop: %s", strerror(-r));
862                 goto finish;
863         }
864
865 finish:
866         context_free(&context, bus);
867
868         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
869 }