chiark / gitweb /
unit: check for unneeded dependencies even when unit stop was expected
[elogind.git] / src / 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 static char *zone = NULL;
78 static bool local_rtc = false;
79 static int use_ntp = -1;
80
81 static usec_t remain_until = 0;
82
83 static void free_data(void) {
84         free(zone);
85         zone = NULL;
86
87         local_rtc = false;
88 }
89
90 static bool valid_timezone(const char *name) {
91         const char *p;
92         char *t;
93         bool slash = false;
94         int r;
95         struct stat st;
96
97         assert(name);
98
99         if (*name == '/' || *name == 0)
100                 return false;
101
102         for (p = name; *p; p++) {
103                 if (!(*p >= '0' && *p <= '9') &&
104                     !(*p >= 'a' && *p <= 'z') &&
105                     !(*p >= 'A' && *p <= 'Z') &&
106                     !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
107                         return false;
108
109                 if (*p == '/') {
110
111                         if (slash)
112                                 return false;
113
114                         slash = true;
115                 } else
116                         slash = false;
117         }
118
119         if (slash)
120                 return false;
121
122         t = strappend("/usr/share/zoneinfo/", name);
123         if (!t)
124                 return false;
125
126         r = stat(t, &st);
127         free(t);
128
129         if (r < 0)
130                 return false;
131
132         if (!S_ISREG(st.st_mode))
133                 return false;
134
135         return true;
136 }
137
138 static void verify_timezone(void) {
139         char *p, *a = NULL, *b = NULL;
140         size_t l, q;
141         int j, k;
142
143         if (!zone)
144                 return;
145
146         p = strappend("/usr/share/zoneinfo/", zone);
147         if (!p) {
148                 log_error("Out of memory");
149                 return;
150         }
151
152         j = read_full_file("/etc/localtime", &a, &l);
153         k = read_full_file(p, &b, &q);
154
155         free(p);
156
157         if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) {
158                 log_warning("/etc/localtime and /etc/timezone out of sync.");
159                 free(zone);
160                 zone = NULL;
161         }
162
163         free(a);
164         free(b);
165 }
166
167 static int read_data(void) {
168         int r;
169
170         free_data();
171
172         r = read_one_line_file("/etc/timezone", &zone);
173         if (r < 0) {
174                 if (r != -ENOENT)
175                         log_warning("Failed to read /etc/timezone: %s", strerror(-r));
176
177 #ifdef TARGET_FEDORA
178                 r = parse_env_file("/etc/sysconfig/clock", NEWLINE,
179                                    "ZONE", &zone,
180                                    NULL);
181
182                 if (r < 0 && r != -ENOENT)
183                         log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r));
184 #endif
185         }
186
187         if (isempty(zone)) {
188                 free(zone);
189                 zone = NULL;
190         }
191
192         verify_timezone();
193
194         local_rtc = hwclock_is_localtime() > 0;
195
196         return 0;
197 }
198
199 static int write_data_timezone(void) {
200         int r = 0;
201         char *p;
202
203         if (!zone) {
204                 if (unlink("/etc/timezone") < 0 && errno != ENOENT)
205                         r = -errno;
206
207                 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
208                         r = -errno;
209
210                 return r;
211         }
212
213         p = strappend("/usr/share/zoneinfo/", zone);
214         if (!p) {
215                 log_error("Out of memory");
216                 return -ENOMEM;
217         }
218
219         r = symlink_or_copy_atomic(p, "/etc/localtime");
220         free(p);
221
222         if (r < 0)
223                 return r;
224
225         r = write_one_line_file_atomic("/etc/timezone", zone);
226         if (r < 0)
227                 return r;
228
229         return 0;
230 }
231
232 static int write_data_local_rtc(void) {
233         int r;
234         char *s, *w;
235
236         r = read_full_file("/etc/adjtime", &s, NULL);
237         if (r < 0) {
238                 if (r != -ENOENT)
239                         return r;
240
241                 if (!local_rtc)
242                         return 0;
243
244                 w = strdup(NULL_ADJTIME_LOCAL);
245                 if (!w)
246                         return -ENOMEM;
247         } else {
248                 char *p, *e;
249                 size_t a, b;
250
251                 p = strchr(s, '\n');
252                 if (!p) {
253                         free(s);
254                         return -EIO;
255                 }
256
257                 p = strchr(p+1, '\n');
258                 if (!p) {
259                         free(s);
260                         return -EIO;
261                 }
262
263                 p++;
264                 e = strchr(p, '\n');
265                 if (!e) {
266                         free(s);
267                         return -EIO;
268                 }
269
270                 a = p - s;
271                 b = strlen(e);
272
273                 w = new(char, a + (local_rtc ? 5 : 3) + b + 1);
274                 if (!w) {
275                         free(s);
276                         return -ENOMEM;
277                 }
278
279                 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
280
281                 if (streq(w, NULL_ADJTIME_UTC)) {
282                         free(w);
283
284                         if (unlink("/etc/adjtime") < 0) {
285                                 if (errno != ENOENT)
286                                         return -errno;
287                         }
288
289                         return 0;
290                 }
291         }
292
293         r = write_one_line_file_atomic("/etc/adjtime", w);
294         free(w);
295
296         return r;
297 }
298
299 static int read_ntp(DBusConnection *bus) {
300         DBusMessage *m = NULL, *reply = NULL;
301         const char *name = "ntpd.service", *s;
302         DBusError error;
303         int r;
304
305         assert(bus);
306
307         dbus_error_init(&error);
308
309         m = dbus_message_new_method_call(
310                         "org.freedesktop.systemd1",
311                         "/org/freedesktop/systemd1",
312                         "org.freedesktop.systemd1.Manager",
313                         "GetUnitFileState");
314
315         if (!m) {
316                 log_error("Out of memory");
317                 r = -ENOMEM;
318                 goto finish;
319         }
320
321         if (!dbus_message_append_args(m,
322                                       DBUS_TYPE_STRING, &name,
323                                       DBUS_TYPE_INVALID)) {
324                 log_error("Could not append arguments to message.");
325                 r = -ENOMEM;
326                 goto finish;
327         }
328
329         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
330         if (!reply) {
331                 log_error("Failed to issue method call: %s", bus_error_message(&error));
332                 r = -EIO;
333                 goto finish;
334         }
335
336         if (!dbus_message_get_args(reply, &error,
337                                    DBUS_TYPE_STRING, &s,
338                                    DBUS_TYPE_INVALID)) {
339                 log_error("Failed to parse reply: %s", bus_error_message(&error));
340                 r = -EIO;
341                 goto finish;
342         }
343
344         use_ntp =
345                 streq(s, "enabled") ||
346                 streq(s, "enabled-runtime");
347         r = 0;
348
349 finish:
350         if (m)
351                 dbus_message_unref(m);
352
353         if (reply)
354                 dbus_message_unref(reply);
355
356         dbus_error_free(&error);
357
358         return r;
359 }
360
361 static int start_ntp(DBusConnection *bus, DBusError *error) {
362         DBusMessage *m = NULL, *reply = NULL;
363         const char *name = "ntpd.service", *mode = "replace";
364         int r;
365
366         assert(bus);
367         assert(error);
368
369         m = dbus_message_new_method_call(
370                         "org.freedesktop.systemd1",
371                         "/org/freedesktop/systemd1",
372                         "org.freedesktop.systemd1.Manager",
373                         use_ntp ? "StartUnit" : "StopUnit");
374         if (!m) {
375                 log_error("Could not allocate message.");
376                 r = -ENOMEM;
377                 goto finish;
378         }
379
380         if (!dbus_message_append_args(m,
381                                       DBUS_TYPE_STRING, &name,
382                                       DBUS_TYPE_STRING, &mode,
383                                       DBUS_TYPE_INVALID)) {
384                 log_error("Could not append arguments to message.");
385                 r = -ENOMEM;
386                 goto finish;
387         }
388
389         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
390         if (!reply) {
391                 log_error("Failed to issue method call: %s", bus_error_message(error));
392                 r = -EIO;
393                 goto finish;
394         }
395
396         r = 0;
397
398 finish:
399         if (m)
400                 dbus_message_unref(m);
401
402         if (reply)
403                 dbus_message_unref(reply);
404
405         return r;
406 }
407
408 static int enable_ntp(DBusConnection *bus, DBusError *error) {
409         DBusMessage *m = NULL, *reply = NULL;
410         const char * const names[] = { "ntpd.service", NULL };
411         int r;
412         DBusMessageIter iter;
413         dbus_bool_t f = FALSE, t = TRUE;
414
415         assert(bus);
416         assert(error);
417
418         m = dbus_message_new_method_call(
419                         "org.freedesktop.systemd1",
420                         "/org/freedesktop/systemd1",
421                         "org.freedesktop.systemd1.Manager",
422                         use_ntp ? "EnableUnitFiles" : "DisableUnitFiles");
423
424         if (!m) {
425                 log_error("Could not allocate message.");
426                 r = -ENOMEM;
427                 goto finish;
428         }
429
430         dbus_message_iter_init_append(m, &iter);
431
432         r = bus_append_strv_iter(&iter, (char**) names);
433         if (r < 0) {
434                 log_error("Failed to append unit files.");
435                 goto finish;
436         }
437         /* send runtime bool */
438         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) {
439                 log_error("Failed to append runtime boolean.");
440                 r = -ENOMEM;
441                 goto finish;
442         }
443
444         if (use_ntp) {
445                 /* send force bool */
446                 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) {
447                         log_error("Failed to append force boolean.");
448                         r = -ENOMEM;
449                         goto finish;
450                 }
451         }
452
453         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
454         if (!reply) {
455                 log_error("Failed to issue method call: %s", bus_error_message(error));
456                 r = -EIO;
457                 goto finish;
458         }
459
460         dbus_message_unref(m);
461         m = dbus_message_new_method_call(
462                         "org.freedesktop.systemd1",
463                         "/org/freedesktop/systemd1",
464                         "org.freedesktop.systemd1.Manager",
465                         "Reload");
466         if (!m) {
467                 log_error("Could not allocate message.");
468                 r = -ENOMEM;
469                 goto finish;
470         }
471
472         dbus_message_unref(reply);
473         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
474         if (!reply) {
475                 log_error("Failed to issue method call: %s", bus_error_message(error));
476                 r = -EIO;
477                 goto finish;
478         }
479
480         r = 0;
481
482 finish:
483         if (m)
484                 dbus_message_unref(m);
485
486         if (reply)
487                 dbus_message_unref(reply);
488
489         return r;
490 }
491
492 static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) {
493         dbus_bool_t db;
494
495         assert(i);
496         assert(property);
497
498         db = use_ntp > 0;
499
500         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
501                 return -ENOMEM;
502
503         return 0;
504 }
505
506 static DBusHandlerResult timedate_message_handler(
507                 DBusConnection *connection,
508                 DBusMessage *message,
509                 void *userdata) {
510
511         const BusProperty properties[] = {
512                 { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone       },
513                 { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool,   "b", &local_rtc },
514                 { "org.freedesktop.timedate1", "NTP",      property_append_ntp,        "b", NULL       },
515                 { NULL, NULL, NULL, NULL, NULL }
516         };
517
518         DBusMessage *reply = NULL, *changed = NULL;
519         DBusError error;
520         int r;
521
522         assert(connection);
523         assert(message);
524
525         dbus_error_init(&error);
526
527         if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) {
528                 const char *z;
529                 dbus_bool_t interactive;
530
531                 if (!dbus_message_get_args(
532                                     message,
533                                     &error,
534                                     DBUS_TYPE_STRING, &z,
535                                     DBUS_TYPE_BOOLEAN, &interactive,
536                                     DBUS_TYPE_INVALID))
537                         return bus_send_error_reply(connection, message, &error, -EINVAL);
538
539                 if (!valid_timezone(z))
540                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
541
542                 if (!streq_ptr(z, zone)) {
543                         char *t;
544
545                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, &error);
546                         if (r < 0)
547                                 return bus_send_error_reply(connection, message, &error, r);
548
549                         t = strdup(z);
550                         if (!t)
551                                 goto oom;
552
553                         free(zone);
554                         zone = t;
555
556                         /* 1. Write new configuration file */
557                         r = write_data_timezone();
558                         if (r < 0) {
559                                 log_error("Failed to set timezone: %s", strerror(-r));
560                                 return bus_send_error_reply(connection, message, NULL, r);
561                         }
562
563                         if (local_rtc) {
564                                 struct timespec ts;
565                                 struct tm *tm;
566
567                                 /* 2. Teach kernel new timezone */
568                                 hwclock_apply_localtime_delta(NULL);
569
570                                 /* 3. Sync RTC from system clock, with the new delta */
571                                 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
572                                 assert_se(tm = localtime(&ts.tv_sec));
573                                 hwclock_set_time(tm);
574                         }
575
576                         log_info("Changed timezone to '%s'.", zone);
577
578                         changed = bus_properties_changed_new(
579                                         "/org/freedesktop/timedate1",
580                                         "org.freedesktop.timedate1",
581                                         "Timezone\0");
582                         if (!changed)
583                                 goto oom;
584                 }
585
586         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
587                 dbus_bool_t lrtc;
588                 dbus_bool_t fix_system;
589                 dbus_bool_t interactive;
590
591                 if (!dbus_message_get_args(
592                                     message,
593                                     &error,
594                                     DBUS_TYPE_BOOLEAN, &lrtc,
595                                     DBUS_TYPE_BOOLEAN, &fix_system,
596                                     DBUS_TYPE_BOOLEAN, &interactive,
597                                     DBUS_TYPE_INVALID))
598                         return bus_send_error_reply(connection, message, &error, -EINVAL);
599
600                 if (lrtc != local_rtc) {
601                         struct timespec ts;
602
603                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error);
604                         if (r < 0)
605                                 return bus_send_error_reply(connection, message, &error, r);
606
607                         local_rtc = lrtc;
608
609                         /* 1. Write new configuration file */
610                         r = write_data_local_rtc();
611                         if (r < 0) {
612                                 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
613                                 return bus_send_error_reply(connection, message, NULL, r);
614                         }
615
616                         /* 2. Teach kernel new timezone */
617                         if (local_rtc)
618                                 hwclock_apply_localtime_delta(NULL);
619                         else
620                                 hwclock_reset_localtime_delta();
621
622                         /* 3. Synchronize clocks */
623                         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
624
625                         if (fix_system) {
626                                 struct tm tm;
627
628                                 /* Sync system clock from RTC; first,
629                                  * initialize the timezone fields of
630                                  * struct tm. */
631                                 if (local_rtc)
632                                         tm = *localtime(&ts.tv_sec);
633                                 else
634                                         tm = *gmtime(&ts.tv_sec);
635
636                                 /* Override the main fields of
637                                  * struct tm, but not the timezone
638                                  * fields */
639                                 if (hwclock_get_time(&tm) >= 0) {
640
641                                         /* And set the system clock
642                                          * with this */
643                                         if (local_rtc)
644                                                 ts.tv_sec = mktime(&tm);
645                                         else
646                                                 ts.tv_sec = timegm(&tm);
647
648                                         clock_settime(CLOCK_REALTIME, &ts);
649                                 }
650
651                         } else {
652                                 struct tm *tm;
653
654                                 /* Sync RTC from system clock */
655                                 if (local_rtc)
656                                         tm = localtime(&ts.tv_sec);
657                                 else
658                                         tm = gmtime(&ts.tv_sec);
659
660                                 hwclock_set_time(tm);
661                         }
662
663                         log_info("RTC configured to %s time.", local_rtc ? "local" : "UTC");
664
665                         changed = bus_properties_changed_new(
666                                         "/org/freedesktop/timedate1",
667                                         "org.freedesktop.timedate1",
668                                         "LocalRTC\0");
669                         if (!changed)
670                                 goto oom;
671                 }
672
673         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) {
674                 int64_t utc;
675                 dbus_bool_t relative;
676                 dbus_bool_t interactive;
677
678                 if (!dbus_message_get_args(
679                                     message,
680                                     &error,
681                                     DBUS_TYPE_INT64, &utc,
682                                     DBUS_TYPE_BOOLEAN, &relative,
683                                     DBUS_TYPE_BOOLEAN, &interactive,
684                                     DBUS_TYPE_INVALID))
685                         return bus_send_error_reply(connection, message, &error, -EINVAL);
686
687                 if (!relative && utc <= 0)
688                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
689
690                 if (!relative || utc != 0) {
691                         struct timespec ts;
692                         struct tm* tm;
693
694                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error);
695                         if (r < 0)
696                                 return bus_send_error_reply(connection, message, &error, r);
697
698                         if (relative)
699                                 timespec_store(&ts, now(CLOCK_REALTIME) + utc);
700                         else
701                                 timespec_store(&ts, utc);
702
703                         /* Set system clock */
704                         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
705                                 log_error("Failed to set local time: %m");
706                                 return bus_send_error_reply(connection, message, NULL, -errno);
707                         }
708
709                         /* Sync down to RTC */
710                         if (local_rtc)
711                                 tm = localtime(&ts.tv_sec);
712                         else
713                                 tm = gmtime(&ts.tv_sec);
714
715                         hwclock_set_time(tm);
716
717                         log_info("Changed local time to %s", ctime(&ts.tv_sec));
718                 }
719         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) {
720                 dbus_bool_t ntp;
721                 dbus_bool_t interactive;
722
723                 if (!dbus_message_get_args(
724                                     message,
725                                     &error,
726                                     DBUS_TYPE_BOOLEAN, &ntp,
727                                     DBUS_TYPE_BOOLEAN, &interactive,
728                                     DBUS_TYPE_INVALID))
729                         return bus_send_error_reply(connection, message, &error, -EINVAL);
730
731                 if (ntp != !!use_ntp) {
732
733                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, &error);
734                         if (r < 0)
735                                 return bus_send_error_reply(connection, message, &error, r);
736
737                         use_ntp = !!ntp;
738
739                         r = enable_ntp(connection, &error);
740                         if (r < 0)
741                                 return bus_send_error_reply(connection, message, &error, r);
742
743                         r = start_ntp(connection, &error);
744                         if (r < 0)
745                                 return bus_send_error_reply(connection, message, &error, r);
746
747                         log_info("Set NTP to %s", use_ntp ? "enabled" : "disabled");
748
749                         changed = bus_properties_changed_new(
750                                         "/org/freedesktop/timedate1",
751                                         "org.freedesktop.timedate1",
752                                         "NTP\0");
753                         if (!changed)
754                                 goto oom;
755                 }
756
757         } else
758                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
759
760         if (!(reply = dbus_message_new_method_return(message)))
761                 goto oom;
762
763         if (!dbus_connection_send(connection, reply, NULL))
764                 goto oom;
765
766         dbus_message_unref(reply);
767         reply = NULL;
768
769         if (changed) {
770
771                 if (!dbus_connection_send(connection, changed, NULL))
772                         goto oom;
773
774                 dbus_message_unref(changed);
775         }
776
777         return DBUS_HANDLER_RESULT_HANDLED;
778
779 oom:
780         if (reply)
781                 dbus_message_unref(reply);
782
783         if (changed)
784                 dbus_message_unref(changed);
785
786         dbus_error_free(&error);
787
788         return DBUS_HANDLER_RESULT_NEED_MEMORY;
789 }
790
791 static int connect_bus(DBusConnection **_bus) {
792         static const DBusObjectPathVTable timedate_vtable = {
793                 .message_function = timedate_message_handler
794         };
795         DBusError error;
796         DBusConnection *bus = NULL;
797         int r;
798
799         assert(_bus);
800
801         dbus_error_init(&error);
802
803         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
804         if (!bus) {
805                 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
806                 r = -ECONNREFUSED;
807                 goto fail;
808         }
809
810         dbus_connection_set_exit_on_disconnect(bus, FALSE);
811
812         if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) ||
813             !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
814                 log_error("Not enough memory");
815                 r = -ENOMEM;
816                 goto fail;
817         }
818
819         r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
820         if (dbus_error_is_set(&error)) {
821                 log_error("Failed to register name on bus: %s", bus_error_message(&error));
822                 r = -EEXIST;
823                 goto fail;
824         }
825
826         if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)  {
827                 log_error("Failed to acquire name.");
828                 r = -EEXIST;
829                 goto fail;
830         }
831
832         if (_bus)
833                 *_bus = bus;
834
835         return 0;
836
837 fail:
838         dbus_connection_close(bus);
839         dbus_connection_unref(bus);
840
841         dbus_error_free(&error);
842
843         return r;
844 }
845
846 int main(int argc, char *argv[]) {
847         int r;
848         DBusConnection *bus = NULL;
849         bool exiting = false;
850
851         log_set_target(LOG_TARGET_AUTO);
852         log_parse_environment();
853         log_open();
854
855         umask(0022);
856
857         if (argc == 2 && streq(argv[1], "--introspect")) {
858                 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
859                       "<node>\n", stdout);
860                 fputs(timedate_interface, stdout);
861                 fputs("</node>\n", stdout);
862                 return 0;
863         }
864
865         if (argc != 1) {
866                 log_error("This program takes no arguments.");
867                 r = -EINVAL;
868                 goto finish;
869         }
870
871         r = read_data();
872         if (r < 0) {
873                 log_error("Failed to read timezone data: %s", strerror(-r));
874                 goto finish;
875         }
876
877         r = connect_bus(&bus);
878         if (r < 0)
879                 goto finish;
880
881         r = read_ntp(bus);
882         if (r < 0) {
883                 log_error("Failed to determine whether NTP is enabled: %s", strerror(-r));
884                 goto finish;
885         }
886
887         remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
888         for (;;) {
889
890                 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
891                         break;
892
893                 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
894                         exiting = true;
895                         bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
896                 }
897         }
898
899         r = 0;
900
901 finish:
902         free_data();
903
904         if (bus) {
905                 dbus_connection_flush(bus);
906                 dbus_connection_close(bus);
907                 dbus_connection_unref(bus);
908         }
909
910         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
911 }