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