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