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