chiark / gitweb /
d507200648426054966b8c136baf30d6c471255d
[elogind.git] / src / timedate / timedated.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "sd-id128.h"
27 #include "sd-messages.h"
28 #include "sd-event.h"
29 #include "sd-bus.h"
30
31 #include "util.h"
32 #include "strv.h"
33 #include "def.h"
34 #include "clock-util.h"
35 #include "conf-files.h"
36 #include "path-util.h"
37 #include "fileio-label.h"
38 #include "label.h"
39 #include "bus-util.h"
40 #include "bus-error.h"
41 #include "bus-common-errors.h"
42 #include "event-util.h"
43
44 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
45 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
46
47 static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = {
48         SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", ENOTSUP),
49         SD_BUS_ERROR_MAP_END
50 };
51
52 typedef struct Context {
53         char *zone;
54         bool local_rtc;
55         bool can_ntp;
56         bool use_ntp;
57         Hashmap *polkit_registry;
58 } Context;
59
60 static void context_free(Context *c) {
61         assert(c);
62
63         free(c->zone);
64         bus_verify_polkit_async_registry_free(c->polkit_registry);
65 }
66
67 static int context_read_data(Context *c) {
68         _cleanup_free_ char *t = NULL;
69         int r;
70
71         assert(c);
72
73         r = readlink_malloc("/etc/localtime", &t);
74         if (r < 0) {
75                 if (r == -EINVAL)
76                         log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
77                 else
78                         log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
79         } else {
80                 const char *e;
81
82                 e = path_startswith(t, "/usr/share/zoneinfo/");
83                 if (!e)
84                         e = path_startswith(t, "../usr/share/zoneinfo/");
85
86                 if (!e)
87                         log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
88                 else {
89                         c->zone = strdup(e);
90                         if (!c->zone)
91                                 return log_oom();
92
93                         goto have_timezone;
94                 }
95         }
96
97 have_timezone:
98         if (isempty(c->zone)) {
99                 free(c->zone);
100                 c->zone = NULL;
101         }
102
103         c->local_rtc = clock_is_localtime() > 0;
104
105         return 0;
106 }
107
108 static int context_write_data_timezone(Context *c) {
109         _cleanup_free_ char *p = NULL;
110         int r = 0;
111
112         assert(c);
113
114         if (isempty(c->zone)) {
115                 if (unlink("/etc/localtime") < 0 && errno != ENOENT)
116                         r = -errno;
117
118                 return r;
119         }
120
121         p = strappend("../usr/share/zoneinfo/", c->zone);
122         if (!p)
123                 return log_oom();
124
125         r = symlink_atomic(p, "/etc/localtime");
126         if (r < 0)
127                 return r;
128
129         return 0;
130 }
131
132 static int context_write_data_local_rtc(Context *c) {
133         int r;
134         _cleanup_free_ char *s = NULL, *w = NULL;
135
136         assert(c);
137
138         r = read_full_file("/etc/adjtime", &s, NULL);
139         if (r < 0) {
140                 if (r != -ENOENT)
141                         return r;
142
143                 if (!c->local_rtc)
144                         return 0;
145
146                 w = strdup(NULL_ADJTIME_LOCAL);
147                 if (!w)
148                         return -ENOMEM;
149         } else {
150                 char *p, *e;
151                 size_t a, b;
152
153                 p = strchr(s, '\n');
154                 if (!p)
155                         return -EIO;
156
157                 p = strchr(p+1, '\n');
158                 if (!p)
159                         return -EIO;
160
161                 p++;
162                 e = strchr(p, '\n');
163                 if (!e)
164                         return -EIO;
165
166                 a = p - s;
167                 b = strlen(e);
168
169                 w = new(char, a + (c->local_rtc ? 5 : 3) + b + 1);
170                 if (!w)
171                         return -ENOMEM;
172
173                 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
174
175                 if (streq(w, NULL_ADJTIME_UTC)) {
176                         if (unlink("/etc/adjtime") < 0)
177                                 if (errno != ENOENT)
178                                         return -errno;
179
180                         return 0;
181                 }
182         }
183
184         mac_selinux_init("/etc");
185         return write_string_file_atomic_label("/etc/adjtime", w);
186 }
187
188 static int context_read_ntp(Context *c, sd_bus *bus) {
189         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
190         sd_bus_message *reply = NULL;
191         const char *s;
192         int r;
193
194         assert(c);
195         assert(bus);
196
197         r = sd_bus_call_method(
198                         bus,
199                         "org.freedesktop.systemd1",
200                         "/org/freedesktop/systemd1",
201                         "org.freedesktop.systemd1.Manager",
202                         "GetUnitFileState",
203                         &error,
204                         &reply,
205                         "s",
206                         "systemd-timesyncd.service");
207
208         if (r < 0) {
209                 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
210                     sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") ||
211                     sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit"))
212                         return 0;
213
214                 return r;
215         }
216
217         r = sd_bus_message_read(reply, "s", &s);
218         if (r < 0)
219                 return r;
220
221         c->can_ntp = true;
222         c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime");
223
224         return 0;
225 }
226
227 static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) {
228         int r;
229
230         assert(c);
231         assert(bus);
232         assert(error);
233
234         if (c->use_ntp)
235                 r = sd_bus_call_method(
236                                 bus,
237                                 "org.freedesktop.systemd1",
238                                 "/org/freedesktop/systemd1",
239                                 "org.freedesktop.systemd1.Manager",
240                                 "StartUnit",
241                                 error,
242                                 NULL,
243                                 "ss",
244                                 "systemd-timesyncd.service",
245                                 "replace");
246         else
247                 r = sd_bus_call_method(
248                                 bus,
249                                 "org.freedesktop.systemd1",
250                                 "/org/freedesktop/systemd1",
251                                 "org.freedesktop.systemd1.Manager",
252                                 "StopUnit",
253                                 error,
254                                 NULL,
255                                 "ss",
256                                 "systemd-timesyncd.service",
257                                 "replace");
258
259         if (r < 0) {
260                 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) ||
261                     sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") ||
262                     sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit"))
263                         return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
264
265                 return r;
266         }
267
268         return 0;
269 }
270
271 static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) {
272         int r;
273
274         assert(c);
275         assert(bus);
276         assert(error);
277
278         if (c->use_ntp)
279                 r = sd_bus_call_method(
280                                 bus,
281                                 "org.freedesktop.systemd1",
282                                 "/org/freedesktop/systemd1",
283                                 "org.freedesktop.systemd1.Manager",
284                                 "EnableUnitFiles",
285                                 error,
286                                 NULL,
287                                 "asbb", 1,
288                                 "systemd-timesyncd.service",
289                                 false, true);
290         else
291                 r = sd_bus_call_method(
292                                 bus,
293                                 "org.freedesktop.systemd1",
294                                 "/org/freedesktop/systemd1",
295                                 "org.freedesktop.systemd1.Manager",
296                                 "DisableUnitFiles",
297                                 error,
298                                 NULL,
299                                 "asb", 1,
300                                 "systemd-timesyncd.service",
301                                 false);
302
303         if (r < 0) {
304                 if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND))
305                         return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported.");
306
307                 return r;
308         }
309
310         r = sd_bus_call_method(
311                         bus,
312                         "org.freedesktop.systemd1",
313                         "/org/freedesktop/systemd1",
314                         "org.freedesktop.systemd1.Manager",
315                         "Reload",
316                         error,
317                         NULL,
318                         NULL);
319         if (r < 0)
320                 return r;
321
322         return 0;
323 }
324
325 static int property_get_rtc_time(
326                 sd_bus *bus,
327                 const char *path,
328                 const char *interface,
329                 const char *property,
330                 sd_bus_message *reply,
331                 void *userdata,
332                 sd_bus_error *error) {
333
334         struct tm tm;
335         usec_t t;
336         int r;
337
338         zero(tm);
339         r = clock_get_hwclock(&tm);
340         if (r == -EBUSY) {
341                 log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
342                 t = 0;
343         } else if (r == -ENOENT) {
344                 log_debug("/dev/rtc not found.");
345                 t = 0; /* no RTC found */
346         } else if (r < 0)
347                 return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %s", strerror(-r));
348         else
349                 t = (usec_t) timegm(&tm) * USEC_PER_SEC;
350
351         return sd_bus_message_append(reply, "t", t);
352 }
353
354 static int property_get_time(
355                 sd_bus *bus,
356                 const char *path,
357                 const char *interface,
358                 const char *property,
359                 sd_bus_message *reply,
360                 void *userdata,
361                 sd_bus_error *error) {
362
363         return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME));
364 }
365
366 static int property_get_ntp_sync(
367                 sd_bus *bus,
368                 const char *path,
369                 const char *interface,
370                 const char *property,
371                 sd_bus_message *reply,
372                 void *userdata,
373                 sd_bus_error *error) {
374
375         return sd_bus_message_append(reply, "b", ntp_synced());
376 }
377
378 static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
379         Context *c = userdata;
380         const char *z;
381         int interactive;
382         char *t;
383         int r;
384
385         assert(bus);
386         assert(m);
387         assert(c);
388
389         r = sd_bus_message_read(m, "sb", &z, &interactive);
390         if (r < 0)
391                 return r;
392
393         if (!timezone_is_valid(z))
394                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z);
395
396         if (streq_ptr(z, c->zone))
397                 return sd_bus_reply_method_return(m, NULL);
398
399         r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-timezone", interactive, &c->polkit_registry, error);
400         if (r < 0)
401                 return r;
402         if (r == 0)
403                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
404
405         t = strdup(z);
406         if (!t)
407                 return -ENOMEM;
408
409         free(c->zone);
410         c->zone = t;
411
412         /* 1. Write new configuration file */
413         r = context_write_data_timezone(c);
414         if (r < 0) {
415                 log_error_errno(r, "Failed to set time zone: %m");
416                 return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %s", strerror(-r));
417         }
418
419         /* 2. Tell the kernel our timezone */
420         clock_set_timezone(NULL);
421
422         if (c->local_rtc) {
423                 struct timespec ts;
424                 struct tm *tm;
425
426                 /* 3. Sync RTC from system clock, with the new delta */
427                 assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
428                 assert_se(tm = localtime(&ts.tv_sec));
429                 clock_set_hwclock(tm);
430         }
431
432         log_struct(LOG_INFO,
433                    LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE),
434                    "TIMEZONE=%s", c->zone,
435                    LOG_MESSAGE("Changed time zone to '%s'.", c->zone),
436                    NULL);
437
438         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL);
439
440         return sd_bus_reply_method_return(m, NULL);
441 }
442
443 static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
444         int lrtc, fix_system, interactive;
445         Context *c = userdata;
446         struct timespec ts;
447         int r;
448
449         assert(bus);
450         assert(m);
451         assert(c);
452
453         r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive);
454         if (r < 0)
455                 return r;
456
457         if (lrtc == c->local_rtc)
458                 return sd_bus_reply_method_return(m, NULL);
459
460         r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-local-rtc", interactive, &c->polkit_registry, error);
461         if (r < 0)
462                 return r;
463         if (r == 0)
464                 return 1;
465
466         c->local_rtc = lrtc;
467
468         /* 1. Write new configuration file */
469         r = context_write_data_local_rtc(c);
470         if (r < 0) {
471                 log_error_errno(r, "Failed to set RTC to local/UTC: %m");
472                 return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %s", strerror(-r));
473         }
474
475         /* 2. Tell the kernel our timezone */
476         clock_set_timezone(NULL);
477
478         /* 3. Synchronize clocks */
479         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
480
481         if (fix_system) {
482                 struct tm tm;
483
484                 /* Sync system clock from RTC; first,
485                  * initialize the timezone fields of
486                  * struct tm. */
487                 if (c->local_rtc)
488                         tm = *localtime(&ts.tv_sec);
489                 else
490                         tm = *gmtime(&ts.tv_sec);
491
492                 /* Override the main fields of
493                  * struct tm, but not the timezone
494                  * fields */
495                 if (clock_get_hwclock(&tm) >= 0) {
496
497                         /* And set the system clock
498                          * with this */
499                         if (c->local_rtc)
500                                 ts.tv_sec = mktime(&tm);
501                         else
502                                 ts.tv_sec = timegm(&tm);
503
504                         clock_settime(CLOCK_REALTIME, &ts);
505                 }
506
507         } else {
508                 struct tm *tm;
509
510                 /* Sync RTC from system clock */
511                 if (c->local_rtc)
512                         tm = localtime(&ts.tv_sec);
513                 else
514                         tm = gmtime(&ts.tv_sec);
515
516                 clock_set_hwclock(tm);
517         }
518
519         log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC");
520
521         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL);
522
523         return sd_bus_reply_method_return(m, NULL);
524 }
525
526 static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
527         int relative, interactive;
528         Context *c = userdata;
529         int64_t utc;
530         struct timespec ts;
531         struct tm* tm;
532         int r;
533
534         assert(bus);
535         assert(m);
536         assert(c);
537
538         if (c->use_ntp)
539                 return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled");
540
541         r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive);
542         if (r < 0)
543                 return r;
544
545         if (!relative && utc <= 0)
546                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time");
547
548         if (relative && utc == 0)
549                 return sd_bus_reply_method_return(m, NULL);
550
551         if (relative) {
552                 usec_t n, x;
553
554                 n = now(CLOCK_REALTIME);
555                 x = n + utc;
556
557                 if ((utc > 0 && x < n) ||
558                     (utc < 0 && x > n))
559                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow");
560
561                 timespec_store(&ts, x);
562         } else
563                 timespec_store(&ts, (usec_t) utc);
564
565         r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-time", interactive, &c->polkit_registry, error);
566         if (r < 0)
567                 return r;
568         if (r == 0)
569                 return 1;
570
571         /* Set system clock */
572         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
573                 log_error_errno(errno, "Failed to set local time: %m");
574                 return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m");
575         }
576
577         /* Sync down to RTC */
578         if (c->local_rtc)
579                 tm = localtime(&ts.tv_sec);
580         else
581                 tm = gmtime(&ts.tv_sec);
582         clock_set_hwclock(tm);
583
584         log_struct(LOG_INFO,
585                    LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
586                    "REALTIME="USEC_FMT, timespec_load(&ts),
587                    LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)),
588                    NULL);
589
590         return sd_bus_reply_method_return(m, NULL);
591 }
592
593 static int method_set_ntp(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
594         int ntp, interactive;
595         Context *c = userdata;
596         int r;
597
598         r = sd_bus_message_read(m, "bb", &ntp, &interactive);
599         if (r < 0)
600                 return r;
601
602         if ((bool)ntp == c->use_ntp)
603                 return sd_bus_reply_method_return(m, NULL);
604
605         r = bus_verify_polkit_async(m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-ntp", interactive, &c->polkit_registry, error);
606         if (r < 0)
607                 return r;
608         if (r == 0)
609                 return 1;
610
611         c->use_ntp = ntp;
612
613         r = context_enable_ntp(c, bus, error);
614         if (r < 0)
615                 return r;
616
617         r = context_start_ntp(c, bus, error);
618         if (r < 0)
619                 return r;
620
621         log_info("Set NTP to %s", c->use_ntp ? "enabled" : "disabled");
622
623         sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL);
624
625         return sd_bus_reply_method_return(m, NULL);
626 }
627
628 static const sd_bus_vtable timedate_vtable[] = {
629         SD_BUS_VTABLE_START(0),
630         SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
631         SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
632         SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0),
633         SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
634         SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
635         SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
636         SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
637         SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
638         SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
639         SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
640         SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
641         SD_BUS_VTABLE_END,
642 };
643
644 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
645         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
646         int r;
647
648         assert(c);
649         assert(event);
650         assert(_bus);
651
652         r = sd_bus_default_system(&bus);
653         if (r < 0)
654                 return log_error_errno(r, "Failed to get system bus connection: %m");
655
656         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
657         if (r < 0)
658                 return log_error_errno(r, "Failed to register object: %m");
659
660         r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0);
661         if (r < 0)
662                 return log_error_errno(r, "Failed to register name: %m");
663
664         r = sd_bus_attach_event(bus, event, 0);
665         if (r < 0)
666                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
667
668         *_bus = bus;
669         bus = NULL;
670
671         return 0;
672 }
673
674 int main(int argc, char *argv[]) {
675         Context context = {};
676         _cleanup_event_unref_ sd_event *event = NULL;
677         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
678         int r;
679
680         log_set_target(LOG_TARGET_AUTO);
681         log_parse_environment();
682         log_open();
683
684         umask(0022);
685
686         if (argc != 1) {
687                 log_error("This program takes no arguments.");
688                 r = -EINVAL;
689                 goto finish;
690         }
691
692         r = sd_event_default(&event);
693         if (r < 0) {
694                 log_error_errno(r, "Failed to allocate event loop: %m");
695                 goto finish;
696         }
697
698         sd_event_set_watchdog(event, true);
699
700         r = connect_bus(&context, event, &bus);
701         if (r < 0)
702                 goto finish;
703
704         r = context_read_data(&context);
705         if (r < 0) {
706                 log_error_errno(r, "Failed to read time zone data: %m");
707                 goto finish;
708         }
709
710         r = context_read_ntp(&context, bus);
711         if (r < 0) {
712                 log_error_errno(r, "Failed to determine whether NTP is enabled: %m");
713                 goto finish;
714         }
715
716         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL);
717         if (r < 0) {
718                 log_error_errno(r, "Failed to run event loop: %m");
719                 goto finish;
720         }
721
722 finish:
723         context_free(&context);
724
725         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
726 }