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