]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timedate/timedated.c
shared: conf-files - add root parameter
[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 <dbus/dbus.h>
23
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "systemd/sd-id128.h"
29 #include "systemd/sd-messages.h"
30 #include "util.h"
31 #include "strv.h"
32 #include "dbus-common.h"
33 #include "polkit.h"
34 #include "def.h"
35 #include "hwclock.h"
36 #include "conf-files.h"
37 #include "path-util.h"
38
39 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
40 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
41
42 #define INTERFACE \
43 " <interface name=\"org.freedesktop.timedate1\">\n" \
44 " <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n" \
45 " <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n" \
46 " <property name=\"NTP\" type=\"b\" access=\"read\"/>\n" \
47 " <method name=\"SetTime\">\n" \
48 " <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n" \
49 " <arg name=\"relative\" type=\"b\" direction=\"in\"/>\n" \
50 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
51 " </method>\n" \
52 " <method name=\"SetTimezone\">\n" \
53 " <arg name=\"timezone\" type=\"s\" direction=\"in\"/>\n" \
54 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
55 " </method>\n" \
56 " <method name=\"SetLocalRTC\">\n" \
57 " <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n" \
58 " <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \
59 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
60 " </method>\n" \
61 " <method name=\"SetNTP\">\n" \
62 " <arg name=\"use_ntp\" type=\"b\" direction=\"in\"/>\n" \
63 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
64 " </method>\n" \
65 " </interface>\n"
66
67 #define INTROSPECTION \
68 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
69 "<node>\n" \
70 INTERFACE \
71 BUS_PROPERTIES_INTERFACE \
72 BUS_INTROSPECTABLE_INTERFACE \
73 BUS_PEER_INTERFACE \
74 "</node>\n"
75
76 #define INTERFACES_LIST \
77 BUS_GENERIC_INTERFACES_LIST \
78 "org.freedesktop.timedate1\0"
79
80 const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
81
82 typedef struct TZ {
83 char *zone;
84 bool local_rtc;
85 int use_ntp;
86 } TZ;
87
88 static TZ tz = {
89 .use_ntp = -1,
90 };
91
92 static usec_t remain_until;
93
94 static void free_data(void) {
95 free(tz.zone);
96 tz.zone = NULL;
97
98 tz.local_rtc = false;
99 }
100
101 static bool valid_timezone(const char *name) {
102 const char *p;
103 char *t;
104 bool slash = false;
105 int r;
106 struct stat st;
107
108 assert(name);
109
110 if (*name == '/' || *name == 0)
111 return false;
112
113 for (p = name; *p; p++) {
114 if (!(*p >= '0' && *p <= '9') &&
115 !(*p >= 'a' && *p <= 'z') &&
116 !(*p >= 'A' && *p <= 'Z') &&
117 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
118 return false;
119
120 if (*p == '/') {
121
122 if (slash)
123 return false;
124
125 slash = true;
126 } else
127 slash = false;
128 }
129
130 if (slash)
131 return false;
132
133 t = strappend("/usr/share/zoneinfo/", name);
134 if (!t)
135 return false;
136
137 r = stat(t, &st);
138 free(t);
139
140 if (r < 0)
141 return false;
142
143 if (!S_ISREG(st.st_mode))
144 return false;
145
146 return true;
147 }
148
149 static int read_data(void) {
150 int r;
151 _cleanup_free_ char *t = NULL;
152
153 free_data();
154
155 r = readlink_malloc("/etc/localtime", &t);
156 if (r < 0) {
157 if (r == -EINVAL)
158 log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
159 else
160 log_warning("Failed to get target of /etc/localtime: %s", strerror(-r));
161 } else {
162 const char *e;
163
164 e = path_startswith(t, "/usr/share/zoneinfo/");
165 if (!e)
166 e = path_startswith(t, "../usr/share/zoneinfo/");
167
168 if (!e)
169 log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
170 else {
171 tz.zone = strdup(e);
172 if (!tz.zone)
173 return log_oom();
174
175 goto have_timezone;
176 }
177 }
178
179 have_timezone:
180 if (isempty(tz.zone)) {
181 free(tz.zone);
182 tz.zone = NULL;
183 }
184
185 tz.local_rtc = hwclock_is_localtime() > 0;
186
187 return 0;
188 }
189
190 static int write_data_timezone(void) {
191 int r = 0;
192 _cleanup_free_ char *p = NULL;
193
194 if (!tz.zone) {
195 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
196 r = -errno;
197
198 return r;
199 }
200
201 p = strappend("../usr/share/zoneinfo/", tz.zone);
202 if (!p)
203 return log_oom();
204
205 r = symlink_atomic(p, "/etc/localtime");
206 if (r < 0)
207 return r;
208
209 return 0;
210 }
211
212 static int write_data_local_rtc(void) {
213 int r;
214 char *s, *w;
215
216 r = read_full_file("/etc/adjtime", &s, NULL);
217 if (r < 0) {
218 if (r != -ENOENT)
219 return r;
220
221 if (!tz.local_rtc)
222 return 0;
223
224 w = strdup(NULL_ADJTIME_LOCAL);
225 if (!w)
226 return -ENOMEM;
227 } else {
228 char *p, *e;
229 size_t a, b;
230
231 p = strchr(s, '\n');
232 if (!p) {
233 free(s);
234 return -EIO;
235 }
236
237 p = strchr(p+1, '\n');
238 if (!p) {
239 free(s);
240 return -EIO;
241 }
242
243 p++;
244 e = strchr(p, '\n');
245 if (!e) {
246 free(s);
247 return -EIO;
248 }
249
250 a = p - s;
251 b = strlen(e);
252
253 w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1);
254 if (!w) {
255 free(s);
256 return -ENOMEM;
257 }
258
259 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
260
261 if (streq(w, NULL_ADJTIME_UTC)) {
262 free(w);
263
264 if (unlink("/etc/adjtime") < 0) {
265 if (errno != ENOENT)
266 return -errno;
267 }
268
269 return 0;
270 }
271 }
272
273 r = write_one_line_file_atomic("/etc/adjtime", w);
274 free(w);
275
276 return r;
277 }
278
279 static char** get_ntp_services(void) {
280 char **r = NULL, **files, **i;
281 int k;
282
283 k = conf_files_list(&files, ".list", NULL,
284 "/etc/systemd/ntp-units.d",
285 "/run/systemd/ntp-units.d",
286 "/usr/local/lib/systemd/ntp-units.d",
287 "/usr/lib/systemd/ntp-units.d",
288 NULL);
289 if (k < 0)
290 return NULL;
291
292 STRV_FOREACH(i, files) {
293 FILE *f;
294
295 f = fopen(*i, "re");
296 if (!f)
297 continue;
298
299 for (;;) {
300 char line[PATH_MAX], *l, **q;
301
302 if (!fgets(line, sizeof(line), f)) {
303
304 if (ferror(f))
305 log_error("Failed to read NTP units file: %m");
306
307 break;
308 }
309
310 l = strstrip(line);
311 if (l[0] == 0 || l[0] == '#')
312 continue;
313
314 q = strv_append(r, l);
315 if (!q) {
316 log_oom();
317 break;
318 }
319
320 strv_free(r);
321 r = q;
322 }
323
324 fclose(f);
325 }
326
327 strv_free(files);
328
329 return strv_uniq(r);
330 }
331
332 static int read_ntp(DBusConnection *bus) {
333 DBusMessage *m = NULL, *reply = NULL;
334 DBusError error;
335 int r;
336 char **i, **l;
337
338 assert(bus);
339
340 dbus_error_init(&error);
341
342 l = get_ntp_services();
343 STRV_FOREACH(i, l) {
344 const char *s;
345
346 if (m)
347 dbus_message_unref(m);
348 m = dbus_message_new_method_call(
349 "org.freedesktop.systemd1",
350 "/org/freedesktop/systemd1",
351 "org.freedesktop.systemd1.Manager",
352 "GetUnitFileState");
353 if (!m) {
354 r = log_oom();
355 goto finish;
356 }
357
358 if (!dbus_message_append_args(m,
359 DBUS_TYPE_STRING, i,
360 DBUS_TYPE_INVALID)) {
361 r = log_oom();
362 goto finish;
363 }
364
365 if (reply)
366 dbus_message_unref(reply);
367 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
368 if (!reply) {
369 if (streq(error.name, "org.freedesktop.DBus.Error.FileNotFound")) {
370 /* This implementation does not exist, try next one */
371 dbus_error_free(&error);
372 continue;
373 }
374
375 log_error("Failed to issue method call: %s", bus_error_message(&error));
376 r = -EIO;
377 goto finish;
378 }
379
380 if (!dbus_message_get_args(reply, &error,
381 DBUS_TYPE_STRING, &s,
382 DBUS_TYPE_INVALID)) {
383 log_error("Failed to parse reply: %s", bus_error_message(&error));
384 r = -EIO;
385 goto finish;
386 }
387
388 tz.use_ntp =
389 streq(s, "enabled") ||
390 streq(s, "enabled-runtime");
391 r = 0;
392 goto finish;
393 }
394
395 /* NTP is not installed. */
396 tz.use_ntp = 0;
397 r = 0;
398
399 finish:
400 if (m)
401 dbus_message_unref(m);
402
403 if (reply)
404 dbus_message_unref(reply);
405
406 strv_free(l);
407
408 dbus_error_free(&error);
409
410 return r;
411 }
412
413 static int start_ntp(DBusConnection *bus, DBusError *error) {
414 DBusMessage *m = NULL, *reply = NULL;
415 const char *mode = "replace";
416 char **i, **l;
417 int r;
418
419 assert(bus);
420 assert(error);
421
422 l = get_ntp_services();
423 STRV_FOREACH(i, l) {
424 if (m)
425 dbus_message_unref(m);
426 m = dbus_message_new_method_call(
427 "org.freedesktop.systemd1",
428 "/org/freedesktop/systemd1",
429 "org.freedesktop.systemd1.Manager",
430 tz.use_ntp ? "StartUnit" : "StopUnit");
431 if (!m) {
432 log_error("Could not allocate message.");
433 r = -ENOMEM;
434 goto finish;
435 }
436
437 if (!dbus_message_append_args(m,
438 DBUS_TYPE_STRING, i,
439 DBUS_TYPE_STRING, &mode,
440 DBUS_TYPE_INVALID)) {
441 log_error("Could not append arguments to message.");
442 r = -ENOMEM;
443 goto finish;
444 }
445
446 if (reply)
447 dbus_message_unref(reply);
448 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
449 if (!reply) {
450 if (streq(error->name, "org.freedesktop.DBus.Error.FileNotFound") ||
451 streq(error->name, "org.freedesktop.systemd1.LoadFailed") ||
452 streq(error->name, "org.freedesktop.systemd1.NoSuchUnit")) {
453 /* This implementation does not exist, try next one */
454 dbus_error_free(error);
455 continue;
456 }
457
458 log_error("Failed to issue method call: %s", bus_error_message(error));
459 r = -EIO;
460 goto finish;
461 }
462
463 r = 0;
464 goto finish;
465 }
466
467 /* No implementaiton available... */
468 r = -ENOENT;
469
470 finish:
471 if (m)
472 dbus_message_unref(m);
473
474 if (reply)
475 dbus_message_unref(reply);
476
477 strv_free(l);
478
479 return r;
480 }
481
482 static int enable_ntp(DBusConnection *bus, DBusError *error) {
483 DBusMessage *m = NULL, *reply = NULL;
484 int r;
485 DBusMessageIter iter;
486 dbus_bool_t f = FALSE, t = TRUE;
487 char **i, **l;
488
489 assert(bus);
490 assert(error);
491
492 l = get_ntp_services();
493 STRV_FOREACH(i, l) {
494 char* k[2];
495
496 if (m)
497 dbus_message_unref(m);
498 m = dbus_message_new_method_call(
499 "org.freedesktop.systemd1",
500 "/org/freedesktop/systemd1",
501 "org.freedesktop.systemd1.Manager",
502 tz.use_ntp ? "EnableUnitFiles" : "DisableUnitFiles");
503 if (!m) {
504 log_error("Could not allocate message.");
505 r = -ENOMEM;
506 goto finish;
507 }
508
509 dbus_message_iter_init_append(m, &iter);
510
511 k[0] = *i;
512 k[1] = NULL;
513
514 r = bus_append_strv_iter(&iter, k);
515 if (r < 0) {
516 log_error("Failed to append unit files.");
517 goto finish;
518 }
519
520 /* send runtime bool */
521 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) {
522 log_error("Failed to append runtime boolean.");
523 r = -ENOMEM;
524 goto finish;
525 }
526
527 if (tz.use_ntp) {
528 /* send force bool */
529 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) {
530 log_error("Failed to append force boolean.");
531 r = -ENOMEM;
532 goto finish;
533 }
534 }
535
536 if (reply)
537 dbus_message_unref(reply);
538 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
539 if (!reply) {
540 if (streq(error->name, "org.freedesktop.DBus.Error.FileNotFound")) {
541 /* This implementation does not exist, try next one */
542 dbus_error_free(error);
543 continue;
544 }
545
546 log_error("Failed to issue method call: %s", bus_error_message(error));
547 r = -EIO;
548 goto finish;
549 }
550
551 dbus_message_unref(m);
552 m = dbus_message_new_method_call(
553 "org.freedesktop.systemd1",
554 "/org/freedesktop/systemd1",
555 "org.freedesktop.systemd1.Manager",
556 "Reload");
557 if (!m) {
558 log_error("Could not allocate message.");
559 r = -ENOMEM;
560 goto finish;
561 }
562
563 dbus_message_unref(reply);
564 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
565 if (!reply) {
566 log_error("Failed to issue method call: %s", bus_error_message(error));
567 r = -EIO;
568 goto finish;
569 }
570
571 r = 0;
572 goto finish;
573 }
574
575 r = -ENOENT;
576
577 finish:
578 if (m)
579 dbus_message_unref(m);
580
581 if (reply)
582 dbus_message_unref(reply);
583
584 strv_free(l);
585
586 return r;
587 }
588
589 static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
590 dbus_bool_t db;
591
592 assert(i);
593 assert(property);
594
595 db = tz.use_ntp > 0;
596
597 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
598 return -ENOMEM;
599
600 return 0;
601 }
602
603 static const BusProperty bus_timedate_properties[] = {
604 { "Timezone", bus_property_append_string, "s", offsetof(TZ, zone), true },
605 { "LocalRTC", bus_property_append_bool, "b", offsetof(TZ, local_rtc) },
606 { "NTP", property_append_ntp, "b", offsetof(TZ, use_ntp) },
607 { NULL, }
608 };
609
610 static const BusBoundProperties bps[] = {
611 { "org.freedesktop.timedate1", bus_timedate_properties, &tz },
612 { NULL, }
613 };
614
615 static DBusHandlerResult timedate_message_handler(
616 DBusConnection *connection,
617 DBusMessage *message,
618 void *userdata) {
619
620 DBusMessage *reply = NULL, *changed = NULL;
621 DBusError error;
622 int r;
623
624 assert(connection);
625 assert(message);
626
627 dbus_error_init(&error);
628
629 if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) {
630 const char *z;
631 dbus_bool_t interactive;
632
633 if (!dbus_message_get_args(
634 message,
635 &error,
636 DBUS_TYPE_STRING, &z,
637 DBUS_TYPE_BOOLEAN, &interactive,
638 DBUS_TYPE_INVALID))
639 return bus_send_error_reply(connection, message, &error, -EINVAL);
640
641 if (!valid_timezone(z))
642 return bus_send_error_reply(connection, message, NULL, -EINVAL);
643
644 if (!streq_ptr(z, tz.zone)) {
645 char *t;
646
647 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, NULL, &error);
648 if (r < 0)
649 return bus_send_error_reply(connection, message, &error, r);
650
651 t = strdup(z);
652 if (!t)
653 goto oom;
654
655 free(tz.zone);
656 tz.zone = t;
657
658 /* 1. Write new configuration file */
659 r = write_data_timezone();
660 if (r < 0) {
661 log_error("Failed to set timezone: %s", strerror(-r));
662 return bus_send_error_reply(connection, message, NULL, r);
663 }
664
665 /* 2. Tell the kernel our time zone */
666 hwclock_set_timezone(NULL);
667
668 if (tz.local_rtc) {
669 struct timespec ts;
670 struct tm *tm;
671
672 /* 3. Sync RTC from system clock, with the new delta */
673 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
674 assert_se(tm = localtime(&ts.tv_sec));
675 hwclock_set_time(tm);
676 }
677
678 log_struct(LOG_INFO,
679 MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
680 "TIMEZONE=%s", tz.zone,
681 "MESSAGE=Changed timezone to '%s'.", tz.zone,
682 NULL);
683
684 changed = bus_properties_changed_new(
685 "/org/freedesktop/timedate1",
686 "org.freedesktop.timedate1",
687 "Timezone\0");
688 if (!changed)
689 goto oom;
690 }
691
692 } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
693 dbus_bool_t lrtc;
694 dbus_bool_t fix_system;
695 dbus_bool_t interactive;
696
697 if (!dbus_message_get_args(
698 message,
699 &error,
700 DBUS_TYPE_BOOLEAN, &lrtc,
701 DBUS_TYPE_BOOLEAN, &fix_system,
702 DBUS_TYPE_BOOLEAN, &interactive,
703 DBUS_TYPE_INVALID))
704 return bus_send_error_reply(connection, message, &error, -EINVAL);
705
706 if (lrtc != tz.local_rtc) {
707 struct timespec ts;
708
709 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, NULL, &error);
710 if (r < 0)
711 return bus_send_error_reply(connection, message, &error, r);
712
713 tz.local_rtc = lrtc;
714
715 /* 1. Write new configuration file */
716 r = write_data_local_rtc();
717 if (r < 0) {
718 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
719 return bus_send_error_reply(connection, message, NULL, r);
720 }
721
722 /* 2. Tell the kernel our time zone */
723 hwclock_set_timezone(NULL);
724
725 /* 3. Synchronize clocks */
726 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
727
728 if (fix_system) {
729 struct tm tm;
730
731 /* Sync system clock from RTC; first,
732 * initialize the timezone fields of
733 * struct tm. */
734 if (tz.local_rtc)
735 tm = *localtime(&ts.tv_sec);
736 else
737 tm = *gmtime(&ts.tv_sec);
738
739 /* Override the main fields of
740 * struct tm, but not the timezone
741 * fields */
742 if (hwclock_get_time(&tm) >= 0) {
743
744 /* And set the system clock
745 * with this */
746 if (tz.local_rtc)
747 ts.tv_sec = mktime(&tm);
748 else
749 ts.tv_sec = timegm(&tm);
750
751 clock_settime(CLOCK_REALTIME, &ts);
752 }
753
754 } else {
755 struct tm *tm;
756
757 /* Sync RTC from system clock */
758 if (tz.local_rtc)
759 tm = localtime(&ts.tv_sec);
760 else
761 tm = gmtime(&ts.tv_sec);
762
763 hwclock_set_time(tm);
764 }
765
766 log_info("RTC configured to %s time.", tz.local_rtc ? "local" : "UTC");
767
768 changed = bus_properties_changed_new(
769 "/org/freedesktop/timedate1",
770 "org.freedesktop.timedate1",
771 "LocalRTC\0");
772 if (!changed)
773 goto oom;
774 }
775
776 } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) {
777 int64_t utc;
778 dbus_bool_t relative;
779 dbus_bool_t interactive;
780
781 if (!dbus_message_get_args(
782 message,
783 &error,
784 DBUS_TYPE_INT64, &utc,
785 DBUS_TYPE_BOOLEAN, &relative,
786 DBUS_TYPE_BOOLEAN, &interactive,
787 DBUS_TYPE_INVALID))
788 return bus_send_error_reply(connection, message, &error, -EINVAL);
789
790 if (!relative && utc <= 0)
791 return bus_send_error_reply(connection, message, NULL, -EINVAL);
792
793 if (!relative || utc != 0) {
794 struct timespec ts;
795 struct tm* tm;
796
797 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error);
798 if (r < 0)
799 return bus_send_error_reply(connection, message, &error, r);
800
801 if (relative)
802 timespec_store(&ts, now(CLOCK_REALTIME) + utc);
803 else
804 timespec_store(&ts, utc);
805
806 /* Set system clock */
807 if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
808 log_error("Failed to set local time: %m");
809 return bus_send_error_reply(connection, message, NULL, -errno);
810 }
811
812 /* Sync down to RTC */
813 if (tz.local_rtc)
814 tm = localtime(&ts.tv_sec);
815 else
816 tm = gmtime(&ts.tv_sec);
817
818 hwclock_set_time(tm);
819
820 log_struct(LOG_INFO,
821 MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
822 "REALTIME=%llu", (unsigned long long) timespec_load(&ts),
823 "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec),
824 NULL);
825 }
826 } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) {
827 dbus_bool_t ntp;
828 dbus_bool_t interactive;
829
830 if (!dbus_message_get_args(
831 message,
832 &error,
833 DBUS_TYPE_BOOLEAN, &ntp,
834 DBUS_TYPE_BOOLEAN, &interactive,
835 DBUS_TYPE_INVALID))
836 return bus_send_error_reply(connection, message, &error, -EINVAL);
837
838 if (ntp != !!tz.use_ntp) {
839
840 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, NULL, &error);
841 if (r < 0)
842 return bus_send_error_reply(connection, message, &error, r);
843
844 tz.use_ntp = !!ntp;
845
846 r = enable_ntp(connection, &error);
847 if (r < 0)
848 return bus_send_error_reply(connection, message, &error, r);
849
850 r = start_ntp(connection, &error);
851 if (r < 0)
852 return bus_send_error_reply(connection, message, &error, r);
853
854 log_info("Set NTP to %s", tz.use_ntp ? "enabled" : "disabled");
855
856 changed = bus_properties_changed_new(
857 "/org/freedesktop/timedate1",
858 "org.freedesktop.timedate1",
859 "NTP\0");
860 if (!changed)
861 goto oom;
862 }
863
864 } else
865 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
866
867 if (!(reply = dbus_message_new_method_return(message)))
868 goto oom;
869
870 if (!dbus_connection_send(connection, reply, NULL))
871 goto oom;
872
873 dbus_message_unref(reply);
874 reply = NULL;
875
876 if (changed) {
877
878 if (!dbus_connection_send(connection, changed, NULL))
879 goto oom;
880
881 dbus_message_unref(changed);
882 }
883
884 return DBUS_HANDLER_RESULT_HANDLED;
885
886 oom:
887 if (reply)
888 dbus_message_unref(reply);
889
890 if (changed)
891 dbus_message_unref(changed);
892
893 dbus_error_free(&error);
894
895 return DBUS_HANDLER_RESULT_NEED_MEMORY;
896 }
897
898 static int connect_bus(DBusConnection **_bus) {
899 static const DBusObjectPathVTable timedate_vtable = {
900 .message_function = timedate_message_handler
901 };
902 DBusError error;
903 DBusConnection *bus = NULL;
904 int r;
905
906 assert(_bus);
907
908 dbus_error_init(&error);
909
910 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
911 if (!bus) {
912 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
913 r = -ECONNREFUSED;
914 goto fail2;
915 }
916
917 dbus_connection_set_exit_on_disconnect(bus, FALSE);
918
919 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) ||
920 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
921 r = log_oom();
922 goto fail;
923 }
924
925 r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
926 if (dbus_error_is_set(&error)) {
927 log_error("Failed to register name on bus: %s", bus_error_message(&error));
928 r = -EEXIST;
929 goto fail;
930 }
931
932 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
933 log_error("Failed to acquire name.");
934 r = -EEXIST;
935 goto fail;
936 }
937
938 if (_bus)
939 *_bus = bus;
940
941 return 0;
942
943 fail:
944 dbus_connection_close(bus);
945 dbus_connection_unref(bus);
946 fail2:
947 dbus_error_free(&error);
948
949 return r;
950 }
951
952 int main(int argc, char *argv[]) {
953 int r;
954 DBusConnection *bus = NULL;
955 bool exiting = false;
956
957 log_set_target(LOG_TARGET_AUTO);
958 log_parse_environment();
959 log_open();
960
961 umask(0022);
962
963 if (argc == 2 && streq(argv[1], "--introspect")) {
964 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
965 "<node>\n", stdout);
966 fputs(timedate_interface, stdout);
967 fputs("</node>\n", stdout);
968 return 0;
969 }
970
971 if (argc != 1) {
972 log_error("This program takes no arguments.");
973 r = -EINVAL;
974 goto finish;
975 }
976
977 r = read_data();
978 if (r < 0) {
979 log_error("Failed to read timezone data: %s", strerror(-r));
980 goto finish;
981 }
982
983 r = connect_bus(&bus);
984 if (r < 0)
985 goto finish;
986
987 r = read_ntp(bus);
988 if (r < 0) {
989 log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
990 goto finish;
991 }
992
993 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
994 for (;;) {
995
996 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
997 break;
998
999 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1000 exiting = true;
1001 bus_async_unregister_and_exit(bus, "org.freedesktop.timedated1");
1002 }
1003 }
1004
1005 r = 0;
1006
1007 finish:
1008 free_data();
1009
1010 if (bus) {
1011 dbus_connection_flush(bus);
1012 dbus_connection_close(bus);
1013 dbus_connection_unref(bus);
1014 }
1015
1016 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1017 }