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