chiark / gitweb /
nss-myhostname: only export the NSS entry point symbols, nothing else
[elogind.git] / src / hostname / hostnamed.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 <dlfcn.h>
26 #include <sys/utsname.h>
27
28 #include "util.h"
29 #include "strv.h"
30 #include "def.h"
31 #include "virt.h"
32 #include "env-util.h"
33 #include "fileio-label.h"
34 #include "label.h"
35 #include "bus-util.h"
36 #include "event-util.h"
37
38 enum {
39         PROP_HOSTNAME,
40         PROP_STATIC_HOSTNAME,
41         PROP_PRETTY_HOSTNAME,
42         PROP_ICON_NAME,
43         PROP_CHASSIS,
44         PROP_KERNEL_NAME,
45         PROP_KERNEL_RELEASE,
46         PROP_KERNEL_VERSION,
47         PROP_OS_PRETTY_NAME,
48         PROP_OS_CPE_NAME,
49         _PROP_MAX
50 };
51
52 typedef struct Context {
53         char *data[_PROP_MAX];
54         Hashmap *polkit_registry;
55 } Context;
56
57 static void context_reset(Context *c) {
58         int p;
59
60         assert(c);
61
62         for (p = 0; p < _PROP_MAX; p++) {
63                 free(c->data[p]);
64                 c->data[p] = NULL;
65         }
66 }
67
68 static void context_free(Context *c, sd_bus *bus) {
69         assert(c);
70
71         context_reset(c);
72         bus_verify_polkit_async_registry_free(bus, c->polkit_registry);
73 }
74
75 static int context_read_data(Context *c) {
76         int r;
77         struct utsname u;
78
79         assert(c);
80
81         context_reset(c);
82
83         assert_se(uname(&u) >= 0);
84         c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
85         c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
86         c->data[PROP_KERNEL_VERSION] = strdup(u.version);
87         if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] ||
88             !c->data[PROP_KERNEL_VERSION])
89                 return -ENOMEM;
90
91         c->data[PROP_HOSTNAME] = gethostname_malloc();
92         if (!c->data[PROP_HOSTNAME])
93                 return -ENOMEM;
94
95         r = read_one_line_file("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
96         if (r < 0 && r != -ENOENT)
97                 return r;
98
99         r = parse_env_file("/etc/machine-info", NEWLINE,
100                            "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
101                            "ICON_NAME", &c->data[PROP_ICON_NAME],
102                            "CHASSIS", &c->data[PROP_CHASSIS],
103                            NULL);
104         if (r < 0 && r != -ENOENT)
105                 return r;
106
107         r = parse_env_file("/etc/os-release", NEWLINE,
108                            "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
109                            "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
110                            NULL);
111         if (r == -ENOENT) {
112                 r = parse_env_file("/usr/lib/os-release", NEWLINE,
113                                    "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
114                                    "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
115                                    NULL);
116         }
117
118         if (r < 0 && r != -ENOENT)
119                 return r;
120
121         return 0;
122 }
123
124 static bool check_nss(void) {
125         void *dl;
126
127         dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY);
128         if (dl) {
129                 dlclose(dl);
130                 return true;
131         }
132
133         return false;
134 }
135
136 static bool valid_chassis(const char *chassis) {
137
138         assert(chassis);
139
140         return nulstr_contains(
141                         "vm\0"
142                         "container\0"
143                         "desktop\0"
144                         "laptop\0"
145                         "server\0"
146                         "tablet\0"
147                         "handset\0"
148                         "watch\0",
149                         chassis);
150 }
151
152 static const char* fallback_chassis(void) {
153         int r;
154         char *type;
155         unsigned t;
156         int v;
157
158         v = detect_virtualization(NULL);
159
160         if (v == VIRTUALIZATION_VM)
161                 return "vm";
162         if (v == VIRTUALIZATION_CONTAINER)
163                 return "container";
164
165         r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
166         if (r < 0)
167                 goto try_dmi;
168
169         r = safe_atou(type, &t);
170         free(type);
171         if (r < 0)
172                 goto try_dmi;
173
174         /* We only list the really obvious cases here as the ACPI data
175          * is not really super reliable.
176          *
177          * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
178          *
179          * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
180          */
181
182         switch(t) {
183
184         case 1:
185         case 3:
186         case 6:
187                 return "desktop";
188
189         case 2:
190                 return "laptop";
191
192         case 4:
193         case 5:
194         case 7:
195                 return "server";
196
197         case 8:
198                 return "tablet";
199         }
200
201 try_dmi:
202         r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
203         if (r < 0)
204                 return NULL;
205
206         r = safe_atou(type, &t);
207         free(type);
208         if (r < 0)
209                 return NULL;
210
211         /* We only list the really obvious cases here. The DMI data is
212            unreliable enough, so let's not do any additional guesswork
213            on top of that.
214
215            See the SMBIOS Specification 2.7.1 section 7.4.1 for
216            details about the values listed here:
217
218            http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
219          */
220
221         switch (t) {
222
223         case 0x3:
224         case 0x4:
225         case 0x6:
226         case 0x7:
227                 return "desktop";
228
229         case 0x8:
230         case 0x9:
231         case 0xA:
232         case 0xE:
233                 return "laptop";
234
235         case 0xB:
236                 return "handset";
237
238         case 0x11:
239         case 0x1C:
240                 return "server";
241         }
242
243         return NULL;
244 }
245
246 static char* context_fallback_icon_name(Context *c) {
247         const char *chassis;
248
249         assert(c);
250
251         if (!isempty(c->data[PROP_CHASSIS]))
252                 return strappend("computer-", c->data[PROP_CHASSIS]);
253
254         chassis = fallback_chassis();
255         if (chassis)
256                 return strappend("computer-", chassis);
257
258         return strdup("computer");
259 }
260
261 static bool hostname_is_useful(const char *hn) {
262         return !isempty(hn) && !is_localhost(hn);
263 }
264
265 static int context_update_kernel_hostname(Context *c) {
266         const char *static_hn;
267         const char *hn;
268
269         assert(c);
270
271         static_hn = c->data[PROP_STATIC_HOSTNAME];
272
273         /* /etc/hostname with something other than "localhost"
274          * has the highest preference ... */
275         if (hostname_is_useful(static_hn))
276                 hn = static_hn;
277
278         /* ... the transient host name, (ie: DHCP) comes next ...*/
279         else if (!isempty(c->data[PROP_HOSTNAME]))
280                 hn = c->data[PROP_HOSTNAME];
281
282         /* ... fallback to static "localhost.*" ignored above ... */
283         else if (!isempty(static_hn))
284                 hn = static_hn;
285
286         /* ... and the ultimate fallback */
287         else
288                 hn = "localhost";
289
290         if (sethostname(hn, strlen(hn)) < 0)
291                 return -errno;
292
293         return 0;
294 }
295
296 static int context_write_data_static_hostname(Context *c) {
297
298         assert(c);
299
300         if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
301
302                 if (unlink("/etc/hostname") < 0)
303                         return errno == ENOENT ? 0 : -errno;
304
305                 return 0;
306         }
307         return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
308 }
309
310 static int context_write_data_machine_info(Context *c) {
311
312         static const char * const name[_PROP_MAX] = {
313                 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
314                 [PROP_ICON_NAME] = "ICON_NAME",
315                 [PROP_CHASSIS] = "CHASSIS"
316         };
317
318         _cleanup_strv_free_ char **l = NULL;
319         int r, p;
320
321         assert(c);
322
323         r = load_env_file(NULL, "/etc/machine-info", NULL, &l);
324         if (r < 0 && r != -ENOENT)
325                 return r;
326
327         for (p = PROP_PRETTY_HOSTNAME; p <= PROP_CHASSIS; p++) {
328                 char *t, **u;
329
330                 assert(name[p]);
331
332                 if (isempty(c->data[p]))  {
333                         strv_env_unset(l, name[p]);
334                         continue;
335                 }
336
337                 if (asprintf(&t, "%s=%s", name[p], strempty(c->data[p])) < 0)
338                         return -ENOMEM;
339
340                 u = strv_env_set(l, t);
341                 free(t);
342
343                 if (!u)
344                         return -ENOMEM;
345
346                 strv_free(l);
347                 l = u;
348         }
349
350         if (strv_isempty(l)) {
351
352                 if (unlink("/etc/machine-info") < 0)
353                         return errno == ENOENT ? 0 : -errno;
354
355                 return 0;
356         }
357
358         return write_env_file_label("/etc/machine-info", l);
359 }
360
361 static int property_get_icon_name(
362                 sd_bus *bus,
363                 const char *path,
364                 const char *interface,
365                 const char *property,
366                 sd_bus_message *reply,
367                 void *userdata,
368                 sd_bus_error *error) {
369
370         _cleanup_free_ char *n = NULL;
371         Context *c = userdata;
372         const char *name;
373
374         if (isempty(c->data[PROP_ICON_NAME]))
375                 name = n = context_fallback_icon_name(c);
376         else
377                 name = c->data[PROP_ICON_NAME];
378
379         if (!name)
380                 return -ENOMEM;
381
382         return sd_bus_message_append(reply, "s", name);
383 }
384
385 static int property_get_chassis(
386                 sd_bus *bus,
387                 const char *path,
388                 const char *interface,
389                 const char *property,
390                 sd_bus_message *reply,
391                 void *userdata,
392                 sd_bus_error *error) {
393
394         Context *c = userdata;
395         const char *name;
396
397         if (isempty(c->data[PROP_CHASSIS]))
398                 name = fallback_chassis();
399         else
400                 name = c->data[PROP_CHASSIS];
401
402         return sd_bus_message_append(reply, "s", name);
403 }
404
405 static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
406         Context *c = userdata;
407         const char *name;
408         int interactive;
409         char *h;
410         int r;
411
412         r = sd_bus_message_read(m, "sb", &name, &interactive);
413         if (r < 0)
414                 return r;
415
416         if (isempty(name))
417                 name = c->data[PROP_STATIC_HOSTNAME];
418
419         if (isempty(name))
420                 name = "localhost";
421
422         if (!hostname_is_valid(name))
423                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
424
425         if (streq_ptr(name, c->data[PROP_HOSTNAME]))
426                 return sd_bus_reply_method_return(m, NULL);
427
428         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-hostname", interactive, error, method_set_hostname, c);
429         if (r < 0)
430                 return r;
431         if (r == 0)
432                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
433
434         h = strdup(name);
435         if (!h)
436                 return -ENOMEM;
437
438         free(c->data[PROP_HOSTNAME]);
439         c->data[PROP_HOSTNAME] = h;
440
441         r = context_update_kernel_hostname(c);
442         if (r < 0) {
443                 log_error("Failed to set host name: %s", strerror(-r));
444                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
445         }
446
447         log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
448
449         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
450
451         return sd_bus_reply_method_return(m, NULL);
452 }
453
454 static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
455         Context *c = userdata;
456         const char *name;
457         int interactive;
458         int r;
459
460         r = sd_bus_message_read(m, "sb", &name, &interactive);
461         if (r < 0)
462                 return r;
463
464         if (isempty(name))
465                 name = NULL;
466
467         if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
468                 return sd_bus_reply_method_return(m, NULL);
469
470         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-static-hostname", interactive, error, method_set_static_hostname, c);
471         if (r < 0)
472                 return r;
473         if (r == 0)
474                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
475
476         if (isempty(name)) {
477                 free(c->data[PROP_STATIC_HOSTNAME]);
478                 c->data[PROP_STATIC_HOSTNAME] = NULL;
479         } else {
480                 char *h;
481
482                 if (!hostname_is_valid(name))
483                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
484
485                 h = strdup(name);
486                 if (!h)
487                         return -ENOMEM;
488
489                 free(c->data[PROP_STATIC_HOSTNAME]);
490                 c->data[PROP_STATIC_HOSTNAME] = h;
491         }
492
493         r = context_update_kernel_hostname(c);
494         if (r < 0) {
495                 log_error("Failed to set host name: %s", strerror(-r));
496                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
497         }
498
499         r = context_write_data_static_hostname(c);
500         if (r < 0) {
501                 log_error("Failed to write static host name: %s", strerror(-r));
502                 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r));
503         }
504
505         log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
506
507         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
508
509         return sd_bus_reply_method_return(m, NULL);
510 }
511
512 static int set_machine_info(Context *c, sd_bus *bus, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
513         int interactive;
514         const char *name;
515         int r;
516
517         assert(c);
518         assert(bus);
519         assert(m);
520
521         r = sd_bus_message_read(m, "sb", &name, &interactive);
522         if (r < 0)
523                 return r;
524
525         if (isempty(name))
526                 name = NULL;
527
528         if (streq_ptr(name, c->data[prop]))
529                 return sd_bus_reply_method_return(m, NULL);
530
531         /* Since the pretty hostname should always be changed at the
532          * same time as the static one, use the same policy action for
533          * both... */
534
535         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, prop == PROP_PRETTY_HOSTNAME ?
536                           "org.freedesktop.hostname1.set-static-hostname" :
537                           "org.freedesktop.hostname1.set-machine-info", interactive, error, cb, c);
538         if (r < 0)
539                 return r;
540         if (r == 0)
541                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
542
543         if (isempty(name)) {
544                 free(c->data[prop]);
545                 c->data[prop] = NULL;
546         } else {
547                 char *h;
548
549                 /* The icon name might ultimately be used as file
550                  * name, so better be safe than sorry */
551
552                 if (prop == PROP_ICON_NAME && !filename_is_safe(name))
553                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
554                 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
555                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
556                 if (prop == PROP_CHASSIS && !valid_chassis(name))
557                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
558
559                 h = strdup(name);
560                 if (!h)
561                         return -ENOMEM;
562
563                 free(c->data[prop]);
564                 c->data[prop] = h;
565         }
566
567         r = context_write_data_machine_info(c);
568         if (r < 0) {
569                 log_error("Failed to write machine info: %s", strerror(-r));
570                 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r));
571         }
572
573         log_info("Changed %s to '%s'",
574                  prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
575                  prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
576
577         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
578                                        prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
579                                        prop == PROP_CHASSIS ? "Chassis" : "IconName", NULL);
580
581         return sd_bus_reply_method_return(m, NULL);
582 }
583
584 static int method_set_pretty_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
585         return set_machine_info(userdata, bus, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
586 }
587
588 static int method_set_icon_name(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
589         return set_machine_info(userdata, bus, m, PROP_ICON_NAME, method_set_icon_name, error);
590 }
591
592 static int method_set_chassis(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
593         return set_machine_info(userdata, bus, m, PROP_CHASSIS, method_set_chassis, error);
594 }
595
596 static const sd_bus_vtable hostname_vtable[] = {
597         SD_BUS_VTABLE_START(0),
598         SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, 0),
599         SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
600         SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
601         SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
602         SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
603         SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
604         SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
605         SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
606         SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
607         SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
608         SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
609         SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
610         SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
611         SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
612         SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
613         SD_BUS_VTABLE_END,
614 };
615
616 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
617         _cleanup_bus_unref_ sd_bus *bus = NULL;
618         int r;
619
620         assert(c);
621         assert(event);
622         assert(_bus);
623
624         r = sd_bus_default_system(&bus);
625         if (r < 0) {
626                 log_error("Failed to get system bus connection: %s", strerror(-r));
627                 return r;
628         }
629
630         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
631         if (r < 0) {
632                 log_error("Failed to register object: %s", strerror(-r));
633                 return r;
634         }
635
636         r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0);
637         if (r < 0) {
638                 log_error("Failed to register name: %s", strerror(-r));
639                 return r;
640         }
641
642         r = sd_bus_attach_event(bus, event, 0);
643         if (r < 0) {
644                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
645                 return r;
646         }
647
648         *_bus = bus;
649         bus = NULL;
650
651         return 0;
652 }
653
654 int main(int argc, char *argv[]) {
655         Context context = {};
656
657         _cleanup_event_unref_ sd_event *event = NULL;
658         _cleanup_bus_unref_ sd_bus *bus = NULL;
659         int r;
660
661         log_set_target(LOG_TARGET_AUTO);
662         log_parse_environment();
663         log_open();
664
665         umask(0022);
666         label_init("/etc");
667
668         if (argc != 1) {
669                 log_error("This program takes no arguments.");
670                 r = -EINVAL;
671                 goto finish;
672         }
673
674         if (!check_nss())
675                 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
676
677         if (argc != 1) {
678                 log_error("This program takes no arguments.");
679                 r = -EINVAL;
680                 goto finish;
681         }
682
683         r = sd_event_default(&event);
684         if (r < 0) {
685                 log_error("Failed to allocate event loop: %s", strerror(-r));
686                 goto finish;
687         }
688
689         sd_event_set_watchdog(event, true);
690
691         r = connect_bus(&context, event, &bus);
692         if (r < 0)
693                 goto finish;
694
695         r = context_read_data(&context);
696         if (r < 0) {
697                 log_error("Failed to read hostname and machine information: %s", strerror(-r));
698                 goto finish;
699         }
700
701         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
702         if (r < 0) {
703                 log_error("Failed to run event loop: %s", strerror(-r));
704                 goto finish;
705         }
706
707 finish:
708         context_free(&context, bus);
709
710         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
711 }