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