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