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