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