chiark / gitweb /
journald: fix typo
[elogind.git] / src / timedate / timedated.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dbus/dbus.h>
23
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "util.h"
29 #include "strv.h"
30 #include "dbus-common.h"
31 #include "polkit.h"
32 #include "def.h"
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
37 #define INTERFACE                                                       \
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"  \
41         "  <property name=\"NTP\" type=\"b\" access=\"read\"/>\n"       \
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"    \
53         "   <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n"   \
54         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
55         "  </method>\n"                                                 \
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"                                                 \
60         " </interface>\n"
61
62 #define INTROSPECTION                                                   \
63         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
64         "<node>\n"                                                      \
65         INTERFACE                                                       \
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             \
73         "org.freedesktop.timedate1\0"
74
75 const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
76
77 typedef struct TZ {
78         char *zone;
79         bool local_rtc;
80         int use_ntp;
81 } TZ;
82
83 static TZ tz = {
84         .use_ntp = -1,
85 };
86
87 static usec_t remain_until;
88
89 static void free_data(void) {
90         free(tz.zone);
91         tz.zone = NULL;
92
93         tz.local_rtc = false;
94 }
95
96 static 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
144 static void verify_timezone(void) {
145         char *p, *a = NULL, *b = NULL;
146         size_t l, q;
147         int j, k;
148
149         if (!tz.zone)
150                 return;
151
152         p = strappend("/usr/share/zoneinfo/", tz.zone);
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.");
165                 free(tz.zone);
166                 tz.zone = NULL;
167         }
168
169         free(a);
170         free(b);
171 }
172
173 static int read_data(void) {
174         int r;
175
176         free_data();
177
178         r = read_one_line_file("/etc/timezone", &tz.zone);
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,
185                                    "ZONE", &tz.zone,
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
193         if (isempty(tz.zone)) {
194                 free(tz.zone);
195                 tz.zone = NULL;
196         }
197
198         verify_timezone();
199
200         tz.local_rtc = hwclock_is_localtime() > 0;
201
202         return 0;
203 }
204
205 static int write_data_timezone(void) {
206         int r = 0;
207         char *p;
208
209         if (!tz.zone) {
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
219         p = strappend("/usr/share/zoneinfo/", tz.zone);
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
231         r = write_one_line_file_atomic("/etc/timezone", tz.zone);
232         if (r < 0)
233                 return r;
234
235         return 0;
236 }
237
238 static 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
247                 if (!tz.local_rtc)
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');
271                 if (!e) {
272                         free(s);
273                         return -EIO;
274                 }
275
276                 a = p - s;
277                 b = strlen(e);
278
279                 w = new(char, a + (tz.local_rtc ? 5 : 3) + b + 1);
280                 if (!w) {
281                         free(s);
282                         return -ENOMEM;
283                 }
284
285                 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), tz.local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
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
305 static 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) {
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
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
358         tz.use_ntp =
359                 streq(s, "enabled") ||
360                 streq(s, "enabled-runtime");
361         r = 0;
362
363 finish:
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
375 static 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",
387                         tz.use_ntp ? "StartUnit" : "StopUnit");
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
412 finish:
413         if (m)
414                 dbus_message_unref(m);
415
416         if (reply)
417                 dbus_message_unref(reply);
418
419         return r;
420 }
421
422 static 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",
436                         tz.use_ntp ? "EnableUnitFiles" : "DisableUnitFiles");
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
458         if (tz.use_ntp) {
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
496 finish:
497         if (m)
498                 dbus_message_unref(m);
499
500         if (reply)
501                 dbus_message_unref(reply);
502
503         return r;
504 }
505
506 static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
507         dbus_bool_t db;
508
509         assert(i);
510         assert(property);
511
512         db = tz.use_ntp > 0;
513
514         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
515                 return -ENOMEM;
516
517         return 0;
518 }
519
520 static 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
527 static const BusBoundProperties bps[] = {
528         { "org.freedesktop.timedate1", bus_timedate_properties, &tz },
529         { NULL, }
530 };
531
532 static DBusHandlerResult timedate_message_handler(
533                 DBusConnection *connection,
534                 DBusMessage *message,
535                 void *userdata) {
536
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
561                 if (!streq_ptr(z, tz.zone)) {
562                         char *t;
563
564                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, NULL, &error);
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
572                         free(tz.zone);
573                         tz.zone = t;
574
575                         /* 1. Write new configuration file */
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
582                         if (tz.local_rtc) {
583                                 struct timespec ts;
584                                 struct tm *tm;
585
586                                 /* 2. Teach kernel new timezone */
587                                 hwclock_apply_localtime_delta(NULL);
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
595                         log_info("Changed timezone to '%s'.", tz.zone);
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;
607                 dbus_bool_t fix_system;
608                 dbus_bool_t interactive;
609
610                 if (!dbus_message_get_args(
611                                     message,
612                                     &error,
613                                     DBUS_TYPE_BOOLEAN, &lrtc,
614                                     DBUS_TYPE_BOOLEAN, &fix_system,
615                                     DBUS_TYPE_BOOLEAN, &interactive,
616                                     DBUS_TYPE_INVALID))
617                         return bus_send_error_reply(connection, message, &error, -EINVAL);
618
619                 if (lrtc != tz.local_rtc) {
620                         struct timespec ts;
621
622                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, NULL, &error);
623                         if (r < 0)
624                                 return bus_send_error_reply(connection, message, &error, r);
625
626                         tz.local_rtc = lrtc;
627
628                         /* 1. Write new configuration file */
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
635                         /* 2. Teach kernel new timezone */
636                         if (tz.local_rtc)
637                                 hwclock_apply_localtime_delta(NULL);
638                         else
639                                 hwclock_reset_localtime_delta();
640
641                         /* 3. Synchronize clocks */
642                         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
643
644                         if (fix_system) {
645                                 struct tm tm;
646
647                                 /* Sync system clock from RTC; first,
648                                  * initialize the timezone fields of
649                                  * struct tm. */
650                                 if (tz.local_rtc)
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 */
662                                         if (tz.local_rtc)
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 */
674                                 if (tz.local_rtc)
675                                         tm = localtime(&ts.tv_sec);
676                                 else
677                                         tm = gmtime(&ts.tv_sec);
678
679                                 hwclock_set_time(tm);
680                         }
681
682                         log_info("RTC configured to %s time.", tz.local_rtc ? "local" : "UTC");
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;
711                         struct tm* tm;
712
713                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, NULL, &error);
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
722                         /* Set system clock */
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
728                         /* Sync down to RTC */
729                         if (tz.local_rtc)
730                                 tm = localtime(&ts.tv_sec);
731                         else
732                                 tm = gmtime(&ts.tv_sec);
733
734                         hwclock_set_time(tm);
735
736                         log_info("Changed local time to %s", ctime(&ts.tv_sec));
737                 }
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
750                 if (ntp != !!tz.use_ntp) {
751
752                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, NULL, &error);
753                         if (r < 0)
754                                 return bus_send_error_reply(connection, message, &error, r);
755
756                         tz.use_ntp = !!ntp;
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
766                         log_info("Set NTP to %s", tz.use_ntp ? "enabled" : "disabled");
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                 }
775
776         } else
777                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
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
798 oom:
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
810 static 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) {
824                 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
825                 r = -ECONNREFUSED;
826                 goto fail;
827         }
828
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)) {
833                 log_error("Not enough memory");
834                 r = -ENOMEM;
835                 goto fail;
836         }
837
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.");
847                 r = -EEXIST;
848                 goto fail;
849         }
850
851         if (_bus)
852                 *_bus = bus;
853
854         return 0;
855
856 fail:
857         dbus_connection_close(bus);
858         dbus_connection_unref(bus);
859
860         dbus_error_free(&error);
861
862         return r;
863 }
864
865 int main(int argc, char *argv[]) {
866         int r;
867         DBusConnection *bus = NULL;
868         bool exiting = false;
869
870         log_set_target(LOG_TARGET_AUTO);
871         log_parse_environment();
872         log_open();
873
874         umask(0022);
875
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
884         if (argc != 1) {
885                 log_error("This program takes no arguments.");
886                 r = -EINVAL;
887                 goto finish;
888         }
889
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
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
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         }
917
918         r = 0;
919
920 finish:
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 }