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