]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
relicense to LGPLv2.1 (with exceptions)
[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
5430f7f2
LP
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
f401e48c
LP
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
5430f7f2 16 Lesser General Public License for more details.
f401e48c 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
f401e48c
LP
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}