]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/timedate/timedated.c
bus: return 1 on all calls that send messages
[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
877d54e9
LP
28#include "systemd/sd-id128.h"
29#include "systemd/sd-messages.h"
f401e48c
LP
30#include "util.h"
31#include "strv.h"
32#include "dbus-common.h"
33#include "polkit.h"
ad740100 34#include "def.h"
bbc98d32 35#include "hwclock.h"
b32d1675 36#include "conf-files.h"
424a19f8 37#include "path-util.h"
a5c32cff
HH
38#include "fileio-label.h"
39#include "label.h"
f401e48c
LP
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
91f9dcaf 44#define INTERFACE \
f401e48c
LP
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" \
6ffe5e37 48 " <property name=\"CanNTP\" type=\"b\" access=\"read\"/>\n" \
c5f0532f 49 " <property name=\"NTP\" type=\"b\" access=\"read\"/>\n" \
f401e48c
LP
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" \
91f9dcaf 61 " <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \
f401e48c
LP
62 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
63 " </method>\n" \
c5f0532f
LP
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" \
91f9dcaf
LP
68 " </interface>\n"
69
70#define INTROSPECTION \
71 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
72 "<node>\n" \
73 INTERFACE \
f401e48c
LP
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 \
3417e2c3 81 "org.freedesktop.timedate1\0"
f401e48c 82
91f9dcaf
LP
83const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
84
d200735e
MS
85typedef struct TZ {
86 char *zone;
87 bool local_rtc;
6ffe5e37 88 int can_ntp;
d200735e
MS
89 int use_ntp;
90} TZ;
f401e48c 91
d200735e 92static TZ tz = {
7e7d4da2
HH
93 .zone = NULL,
94 .local_rtc = false,
6ffe5e37 95 .can_ntp = -1,
d200735e
MS
96 .use_ntp = -1,
97};
98
99static usec_t remain_until;
ad740100 100
f401e48c 101static void free_data(void) {
d200735e
MS
102 free(tz.zone);
103 tz.zone = NULL;
f401e48c 104
d200735e 105 tz.local_rtc = false;
f401e48c
LP
106}
107
108static 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
424a19f8 140 t = strappend("/usr/share/zoneinfo/", name);
f401e48c
LP
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
f401e48c
LP
156static int read_data(void) {
157 int r;
424a19f8 158 _cleanup_free_ char *t = NULL;
f401e48c
LP
159
160 free_data();
161
92c4ef2d
SL
162 r = readlink_malloc("/etc/localtime", &t);
163 if (r < 0) {
164 if (r == -EINVAL)
424a19f8 165 log_warning("/etc/localtime should be a symbolic link to a timezone data file in /usr/share/zoneinfo/.");
92c4ef2d 166 else
424a19f8 167 log_warning("Failed to get target of /etc/localtime: %s", strerror(-r));
92c4ef2d 168 } else {
424a19f8 169 const char *e;
92c4ef2d 170
424a19f8
LP
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/.");
92c4ef2d 177 else {
424a19f8 178 tz.zone = strdup(e);
92c4ef2d
SL
179 if (!tz.zone)
180 return log_oom();
181
182 goto have_timezone;
183 }
184 }
185
92c4ef2d 186have_timezone:
d200735e
MS
187 if (isempty(tz.zone)) {
188 free(tz.zone);
189 tz.zone = NULL;
a724d2ed 190 }
f401e48c 191
d200735e 192 tz.local_rtc = hwclock_is_localtime() > 0;
f401e48c
LP
193
194 return 0;
195}
196
197static int write_data_timezone(void) {
198 int r = 0;
424a19f8 199 _cleanup_free_ char *p = NULL;
e19a21a8 200
d200735e 201 if (!tz.zone) {
424a19f8 202 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
f401e48c
LP
203 r = -errno;
204
f401e48c
LP
205 return r;
206 }
207
424a19f8 208 p = strappend("../usr/share/zoneinfo/", tz.zone);
0d0f0c50
SL
209 if (!p)
210 return log_oom();
f401e48c 211
424a19f8 212 r = symlink_atomic(p, "/etc/localtime");
f401e48c 213 if (r < 0)
424a19f8 214 return r;
f401e48c 215
f401e48c
LP
216 return 0;
217}
218
219static int write_data_local_rtc(void) {
220 int r;
7fd1b19b 221 _cleanup_free_ char *s = NULL, *w = NULL;
f401e48c
LP
222
223 r = read_full_file("/etc/adjtime", &s, NULL);
224 if (r < 0) {
225 if (r != -ENOENT)
226 return r;
227
d200735e 228 if (!tz.local_rtc)
f401e48c
LP
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');
d257f05a 239 if (!p)
f401e48c 240 return -EIO;
f401e48c
LP
241
242 p = strchr(p+1, '\n');
d257f05a 243 if (!p)
f401e48c 244 return -EIO;
f401e48c
LP
245
246 p++;
247 e = strchr(p, '\n');
d257f05a 248 if (!e)
f401e48c 249 return -EIO;
f401e48c
LP
250
251 a = p - s;
252 b = strlen(e);
253
d200735e 254 w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1);
d257f05a 255 if (!w)
f401e48c 256 return -ENOMEM;
f401e48c 257
d200735e 258 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
f401e48c
LP
259
260 if (streq(w, NULL_ADJTIME_UTC)) {
d257f05a 261 if (unlink("/etc/adjtime") < 0)
f401e48c
LP
262 if (errno != ENOENT)
263 return -errno;
f401e48c
LP
264
265 return 0;
266 }
267 }
a5c32cff 268 label_init("/etc");
d257f05a 269 return write_string_file_atomic_label("/etc/adjtime", w);
f401e48c
LP
270}
271
ac7019f3 272static char** get_ntp_services(void) {
7fd1b19b 273 _cleanup_strv_free_ char **r = NULL, **files;
d257f05a 274 char **i;
b32d1675
LP
275 int k;
276
7850b3b8 277 k = conf_files_list(&files, ".list", NULL,
b32d1675
LP
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)
ac7019f3
LP
284 return NULL;
285
b32d1675 286 STRV_FOREACH(i, files) {
7fd1b19b 287 _cleanup_fclose_ FILE *f;
ac7019f3 288
b32d1675
LP
289 f = fopen(*i, "re");
290 if (!f)
291 continue;
ac7019f3 292
b32d1675 293 for (;;) {
d257f05a 294 char line[PATH_MAX], *l;
ac7019f3 295
b32d1675 296 if (!fgets(line, sizeof(line), f)) {
ac7019f3 297
b32d1675
LP
298 if (ferror(f))
299 log_error("Failed to read NTP units file: %m");
ac7019f3 300
b32d1675
LP
301 break;
302 }
303
304 l = strstrip(line);
305 if (l[0] == 0 || l[0] == '#')
306 continue;
307
7e7d4da2 308 if (strv_extend(&r, l) < 0) {
0d0f0c50 309 log_oom();
7e7d4da2
HH
310 return NULL;
311 }
ac7019f3 312 }
ac7019f3
LP
313 }
314
d257f05a
ZJS
315 i = r;
316 r = NULL; /* avoid cleanup */
ac7019f3 317
d257f05a 318 return strv_uniq(i);
ac7019f3
LP
319}
320
c5f0532f
LP
321static int read_ntp(DBusConnection *bus) {
322 DBusMessage *m = NULL, *reply = NULL;
c5f0532f
LP
323 DBusError error;
324 int r;
ac7019f3 325 char **i, **l;
c5f0532f
LP
326
327 assert(bus);
328
329 dbus_error_init(&error);
330
ac7019f3
LP
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) {
0d0f0c50 343 r = log_oom();
ac7019f3
LP
344 goto finish;
345 }
c5f0532f 346
ac7019f3
LP
347 if (!dbus_message_append_args(m,
348 DBUS_TYPE_STRING, i,
349 DBUS_TYPE_INVALID)) {
0d0f0c50 350 r = log_oom();
ac7019f3
LP
351 goto finish;
352 }
c5f0532f 353
ac7019f3
LP
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 }
2aa4c315 361
ac7019f3
LP
362 log_error("Failed to issue method call: %s", bus_error_message(&error));
363 r = -EIO;
2aa4c315
LP
364 goto finish;
365 }
366
ac7019f3
LP
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 }
c5f0532f 374
6ffe5e37 375 tz.can_ntp = 1;
ac7019f3
LP
376 tz.use_ntp =
377 streq(s, "enabled") ||
378 streq(s, "enabled-runtime");
379 r = 0;
c5f0532f
LP
380 goto finish;
381 }
382
ac7019f3 383 /* NTP is not installed. */
6ffe5e37 384 tz.can_ntp = 0;
ac7019f3 385 tz.use_ntp = 0;
c5f0532f
LP
386 r = 0;
387
388finish:
389 if (m)
390 dbus_message_unref(m);
391
392 if (reply)
393 dbus_message_unref(reply);
394
ac7019f3
LP
395 strv_free(l);
396
c5f0532f
LP
397 dbus_error_free(&error);
398
399 return r;
400}
401
402static int start_ntp(DBusConnection *bus, DBusError *error) {
403 DBusMessage *m = NULL, *reply = NULL;
ac7019f3
LP
404 const char *mode = "replace";
405 char **i, **l;
c5f0532f
LP
406 int r;
407
408 assert(bus);
409 assert(error);
410
ac7019f3
LP
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 }
c5f0532f 425
ac7019f3
LP
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
ac7019f3
LP
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 }
c5f0532f 444
ac7019f3
LP
445 log_error("Failed to issue method call: %s", bus_error_message(error));
446 r = -EIO;
447 goto finish;
448 }
449
450 r = 0;
c5f0532f
LP
451 goto finish;
452 }
453
ac7019f3
LP
454 /* No implementaiton available... */
455 r = -ENOENT;
c5f0532f
LP
456
457finish:
458 if (m)
459 dbus_message_unref(m);
460
461 if (reply)
462 dbus_message_unref(reply);
463
ac7019f3
LP
464 strv_free(l);
465
c5f0532f
LP
466 return r;
467}
468
469static int enable_ntp(DBusConnection *bus, DBusError *error) {
470 DBusMessage *m = NULL, *reply = NULL;
c5f0532f
LP
471 int r;
472 DBusMessageIter iter;
473 dbus_bool_t f = FALSE, t = TRUE;
ac7019f3 474 char **i, **l;
c5f0532f
LP
475
476 assert(bus);
477 assert(error);
478
ac7019f3
LP
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 }
c5f0532f 495
ac7019f3 496 dbus_message_iter_init_append(m, &iter);
c5f0532f 497
ac7019f3
LP
498 k[0] = *i;
499 k[1] = NULL;
c5f0532f 500
ac7019f3
LP
501 r = bus_append_strv_iter(&iter, k);
502 if (r < 0) {
503 log_error("Failed to append unit files.");
504 goto finish;
505 }
c5f0532f 506
ac7019f3
LP
507 /* send runtime bool */
508 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) {
509 log_error("Failed to append runtime boolean.");
c5f0532f
LP
510 r = -ENOMEM;
511 goto finish;
512 }
c5f0532f 513
ac7019f3
LP
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 }
c5f0532f 522
ac7019f3
LP
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 }
c5f0532f 530
ac7019f3
LP
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;
c5f0532f
LP
557 goto finish;
558 }
559
ac7019f3 560 r = -ENOENT;
c5f0532f
LP
561
562finish:
563 if (m)
564 dbus_message_unref(m);
565
566 if (reply)
567 dbus_message_unref(reply);
568
ac7019f3
LP
569 strv_free(l);
570
c5f0532f
LP
571 return r;
572}
573
6ffe5e37
RL
574static 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
c5f0532f
LP
588static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
589 dbus_bool_t db;
590
591 assert(i);
592 assert(property);
593
d200735e 594 db = tz.use_ntp > 0;
c5f0532f
LP
595
596 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
597 return -ENOMEM;
598
599 return 0;
600}
601
d200735e
MS
602static 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) },
6ffe5e37 605 { "CanNTP", property_append_can_ntp, "b", offsetof(TZ, can_ntp) },
d200735e
MS
606 { "NTP", property_append_ntp, "b", offsetof(TZ, use_ntp) },
607 { NULL, }
608};
609
610static const BusBoundProperties bps[] = {
611 { "org.freedesktop.timedate1", bus_timedate_properties, &tz },
612 { NULL, }
613};
614
f401e48c
LP
615static DBusHandlerResult timedate_message_handler(
616 DBusConnection *connection,
617 DBusMessage *message,
618 void *userdata) {
619
f401e48c
LP
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
d200735e 644 if (!streq_ptr(z, tz.zone)) {
f401e48c
LP
645 char *t;
646
89f13440 647 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, NULL, &error);
f401e48c
LP
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
d200735e
MS
655 free(tz.zone);
656 tz.zone = t;
f401e48c 657
2076cf88 658 /* 1. Write new configuration file */
f401e48c
LP
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
e9dd9f95 665 /* 2. Tell the kernel our timezone */
72edcff5
KS
666 hwclock_set_timezone(NULL);
667
d200735e 668 if (tz.local_rtc) {
2076cf88
LP
669 struct timespec ts;
670 struct tm *tm;
671
2076cf88
LP
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
877d54e9 678 log_struct(LOG_INFO,
1ca6783f 679 MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
877d54e9
LP
680 "TIMEZONE=%s", tz.zone,
681 "MESSAGE=Changed timezone to '%s'.", tz.zone,
682 NULL);
f401e48c
LP
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;
05a4abb9 694 dbus_bool_t fix_system;
f401e48c
LP
695 dbus_bool_t interactive;
696
697 if (!dbus_message_get_args(
698 message,
699 &error,
700 DBUS_TYPE_BOOLEAN, &lrtc,
05a4abb9 701 DBUS_TYPE_BOOLEAN, &fix_system,
f401e48c
LP
702 DBUS_TYPE_BOOLEAN, &interactive,
703 DBUS_TYPE_INVALID))
704 return bus_send_error_reply(connection, message, &error, -EINVAL);
705
d200735e 706 if (lrtc != tz.local_rtc) {
2076cf88
LP
707 struct timespec ts;
708
89f13440 709 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, NULL, &error);
f401e48c
LP
710 if (r < 0)
711 return bus_send_error_reply(connection, message, &error, r);
712
d200735e 713 tz.local_rtc = lrtc;
f401e48c 714
2076cf88 715 /* 1. Write new configuration file */
f401e48c
LP
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
e9dd9f95 722 /* 2. Tell the kernel our timezone */
72edcff5 723 hwclock_set_timezone(NULL);
2076cf88
LP
724
725 /* 3. Synchronize clocks */
726 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
727
05a4abb9 728 if (fix_system) {
2076cf88
LP
729 struct tm tm;
730
731 /* Sync system clock from RTC; first,
732 * initialize the timezone fields of
733 * struct tm. */
d200735e 734 if (tz.local_rtc)
2076cf88
LP
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 */
d200735e 746 if (tz.local_rtc)
2076cf88
LP
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 */
d200735e 758 if (tz.local_rtc)
2076cf88
LP
759 tm = localtime(&ts.tv_sec);
760 else
761 tm = gmtime(&ts.tv_sec);
762
763 hwclock_set_time(tm);
764 }
765
d200735e 766 log_info("RTC configured to %s time.", tz.local_rtc ? "local" : "UTC");
f401e48c
LP
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;
2076cf88 795 struct tm* tm;
f401e48c 796
86e7b6e3
LP
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
89f13440 811 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error);
f401e48c
LP
812 if (r < 0)
813 return bus_send_error_reply(connection, message, &error, r);
814
2076cf88 815 /* Set system clock */
f401e48c
LP
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
2076cf88 821 /* Sync down to RTC */
d200735e 822 if (tz.local_rtc)
2076cf88
LP
823 tm = localtime(&ts.tv_sec);
824 else
825 tm = gmtime(&ts.tv_sec);
826
827 hwclock_set_time(tm);
828
877d54e9 829 log_struct(LOG_INFO,
1ca6783f 830 MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
877d54e9
LP
831 "REALTIME=%llu", (unsigned long long) timespec_load(&ts),
832 "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec),
833 NULL);
f401e48c 834 }
c5f0532f
LP
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
d200735e 847 if (ntp != !!tz.use_ntp) {
c5f0532f 848
89f13440 849 r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, NULL, &error);
c5f0532f
LP
850 if (r < 0)
851 return bus_send_error_reply(connection, message, &error, r);
852
d200735e 853 tz.use_ntp = !!ntp;
c5f0532f
LP
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
d200735e 863 log_info("Set NTP to %s", tz.use_ntp ? "enabled" : "disabled");
c5f0532f
LP
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 }
f401e48c
LP
872
873 } else
d200735e 874 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
f401e48c
LP
875
876 if (!(reply = dbus_message_new_method_return(message)))
877 goto oom;
878
c6a818c8 879 if (!bus_maybe_send_reply(connection, message, reply))
f401e48c
LP
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
895oom:
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
907static 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) {
a2e52832 921 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
f401e48c 922 r = -ECONNREFUSED;
b779821b 923 goto fail2;
f401e48c
LP
924 }
925
ad740100
LP
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)) {
0d0f0c50 930 r = log_oom();
f401e48c
LP
931 goto fail;
932 }
933
add10b5a
LP
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.");
f401e48c
LP
943 r = -EEXIST;
944 goto fail;
945 }
946
947 if (_bus)
948 *_bus = bus;
949
950 return 0;
951
952fail:
953 dbus_connection_close(bus);
954 dbus_connection_unref(bus);
b779821b 955fail2:
f401e48c
LP
956 dbus_error_free(&error);
957
958 return r;
959}
960
961int main(int argc, char *argv[]) {
962 int r;
963 DBusConnection *bus = NULL;
ad740100 964 bool exiting = false;
f401e48c
LP
965
966 log_set_target(LOG_TARGET_AUTO);
967 log_parse_environment();
968 log_open();
969
4c12626c
LP
970 umask(0022);
971
91f9dcaf
LP
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
f401e48c
LP
980 if (argc != 1) {
981 log_error("This program takes no arguments.");
982 r = -EINVAL;
983 goto finish;
984 }
985
f401e48c
LP
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
c5f0532f
LP
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
ad740100
LP
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;
c68df239 1010 bus_async_unregister_and_exit(bus, "org.freedesktop.timedated1");
ad740100
LP
1011 }
1012 }
f401e48c
LP
1013
1014 r = 0;
1015
1016finish:
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}