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