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