chiark / gitweb /
os-release: define /usr/lib/os-release as fallback for /etc/os-release
[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                         chassis);
149 }
150
151 static const char* fallback_chassis(void) {
152         int r;
153         char *type;
154         unsigned t;
155         int v;
156
157         v = detect_virtualization(NULL);
158
159         if (v == VIRTUALIZATION_VM)
160                 return "vm";
161         if (v == VIRTUALIZATION_CONTAINER)
162                 return "container";
163
164         r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
165         if (r < 0)
166                 goto try_dmi;
167
168         r = safe_atou(type, &t);
169         free(type);
170         if (r < 0)
171                 goto try_dmi;
172
173         /* We only list the really obvious cases here as the ACPI data
174          * is not really super reliable.
175          *
176          * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
177          *
178          * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
179          */
180
181         switch(t) {
182
183         case 1:
184         case 3:
185         case 6:
186                 return "desktop";
187
188         case 2:
189                 return "laptop";
190
191         case 4:
192         case 5:
193         case 7:
194                 return "server";
195
196         case 8:
197                 return "tablet";
198         }
199
200 try_dmi:
201         r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
202         if (r < 0)
203                 return NULL;
204
205         r = safe_atou(type, &t);
206         free(type);
207         if (r < 0)
208                 return NULL;
209
210         /* We only list the really obvious cases here. The DMI data is
211            unreliable enough, so let's not do any additional guesswork
212            on top of that.
213
214            See the SMBIOS Specification 2.7.1 section 7.4.1 for
215            details about the values listed here:
216
217            http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
218          */
219
220         switch (t) {
221
222         case 0x3:
223         case 0x4:
224         case 0x6:
225         case 0x7:
226                 return "desktop";
227
228         case 0x8:
229         case 0x9:
230         case 0xA:
231         case 0xE:
232                 return "laptop";
233
234         case 0xB:
235                 return "handset";
236
237         case 0x11:
238         case 0x1C:
239                 return "server";
240         }
241
242         return NULL;
243 }
244
245 static char* context_fallback_icon_name(Context *c) {
246         const char *chassis;
247
248         assert(c);
249
250         if (!isempty(c->data[PROP_CHASSIS]))
251                 return strappend("computer-", c->data[PROP_CHASSIS]);
252
253         chassis = fallback_chassis();
254         if (chassis)
255                 return strappend("computer-", chassis);
256
257         return strdup("computer");
258 }
259
260 static bool hostname_is_useful(const char *hn) {
261         return !isempty(hn) && !streq(hn, "localhost");
262 }
263
264 static int context_update_kernel_hostname(Context *c) {
265         const char *static_hn;
266         const char *hn;
267
268         assert(c);
269
270         static_hn = c->data[PROP_STATIC_HOSTNAME];
271
272         /* /etc/hostname with something other than "localhost"
273          * has the highest preference ... */
274         if (hostname_is_useful(static_hn))
275                 hn = static_hn;
276
277         /* ... the transient host name, (ie: DHCP) comes next ...*/
278         else if (!isempty(c->data[PROP_HOSTNAME]))
279                 hn = c->data[PROP_HOSTNAME];
280
281         /* ... fallback to static "localhost.*" ignored above ... */
282         else if (!isempty(static_hn))
283                 hn = static_hn;
284
285         /* ... and the ultimate fallback */
286         else
287                 hn = "localhost";
288
289         if (sethostname(hn, strlen(hn)) < 0)
290                 return -errno;
291
292         return 0;
293 }
294
295 static int context_write_data_static_hostname(Context *c) {
296
297         assert(c);
298
299         if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
300
301                 if (unlink("/etc/hostname") < 0)
302                         return errno == ENOENT ? 0 : -errno;
303
304                 return 0;
305         }
306         return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
307 }
308
309 static int context_write_data_machine_info(Context *c) {
310
311         static const char * const name[_PROP_MAX] = {
312                 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
313                 [PROP_ICON_NAME] = "ICON_NAME",
314                 [PROP_CHASSIS] = "CHASSIS"
315         };
316
317         _cleanup_strv_free_ char **l = NULL;
318         int r, p;
319
320         assert(c);
321
322         r = load_env_file("/etc/machine-info", NULL, &l);
323         if (r < 0 && r != -ENOENT)
324                 return r;
325
326         for (p = PROP_PRETTY_HOSTNAME; p <= PROP_CHASSIS; p++) {
327                 char *t, **u;
328
329                 assert(name[p]);
330
331                 if (isempty(c->data[p]))  {
332                         strv_env_unset(l, name[p]);
333                         continue;
334                 }
335
336                 if (asprintf(&t, "%s=%s", name[p], strempty(c->data[p])) < 0)
337                         return -ENOMEM;
338
339                 u = strv_env_set(l, t);
340                 free(t);
341
342                 if (!u)
343                         return -ENOMEM;
344
345                 strv_free(l);
346                 l = u;
347         }
348
349         if (strv_isempty(l)) {
350
351                 if (unlink("/etc/machine-info") < 0)
352                         return errno == ENOENT ? 0 : -errno;
353
354                 return 0;
355         }
356
357         return write_env_file_label("/etc/machine-info", l);
358 }
359
360 static int property_get_icon_name(
361                 sd_bus *bus,
362                 const char *path,
363                 const char *interface,
364                 const char *property,
365                 sd_bus_message *reply,
366                 void *userdata,
367                 sd_bus_error *error) {
368
369         _cleanup_free_ char *n = NULL;
370         Context *c = userdata;
371         const char *name;
372
373         if (isempty(c->data[PROP_ICON_NAME]))
374                 name = n = context_fallback_icon_name(c);
375         else
376                 name = c->data[PROP_ICON_NAME];
377
378         if (!name)
379                 return -ENOMEM;
380
381         return sd_bus_message_append(reply, "s", name);
382 }
383
384 static int property_get_chassis(
385                 sd_bus *bus,
386                 const char *path,
387                 const char *interface,
388                 const char *property,
389                 sd_bus_message *reply,
390                 void *userdata,
391                 sd_bus_error *error) {
392
393         Context *c = userdata;
394         const char *name;
395
396         if (isempty(c->data[PROP_CHASSIS]))
397                 name = fallback_chassis();
398         else
399                 name = c->data[PROP_CHASSIS];
400
401         return sd_bus_message_append(reply, "s", name);
402 }
403
404 static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
405         Context *c = userdata;
406         const char *name;
407         int interactive;
408         char *h;
409         int r;
410
411         r = sd_bus_message_read(m, "sb", &name, &interactive);
412         if (r < 0)
413                 return r;
414
415         if (isempty(name))
416                 name = c->data[PROP_STATIC_HOSTNAME];
417
418         if (isempty(name))
419                 name = "localhost";
420
421         if (!hostname_is_valid(name))
422                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
423
424         if (streq_ptr(name, c->data[PROP_HOSTNAME]))
425                 return sd_bus_reply_method_return(m, NULL);
426
427         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-hostname", interactive, error, method_set_hostname, c);
428         if (r < 0)
429                 return r;
430         if (r == 0)
431                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
432
433         h = strdup(name);
434         if (!h)
435                 return -ENOMEM;
436
437         free(c->data[PROP_HOSTNAME]);
438         c->data[PROP_HOSTNAME] = h;
439
440         r = context_update_kernel_hostname(c);
441         if (r < 0) {
442                 log_error("Failed to set host name: %s", strerror(-r));
443                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
444         }
445
446         log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
447
448         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
449
450         return sd_bus_reply_method_return(m, NULL);
451 }
452
453 static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
454         Context *c = userdata;
455         const char *name;
456         int interactive;
457         int r;
458
459         r = sd_bus_message_read(m, "sb", &name, &interactive);
460         if (r < 0)
461                 return r;
462
463         if (isempty(name))
464                 name = NULL;
465
466         if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
467                 return sd_bus_reply_method_return(m, NULL);
468
469         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-static-hostname", interactive, error, method_set_static_hostname, c);
470         if (r < 0)
471                 return r;
472         if (r == 0)
473                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
474
475         if (isempty(name)) {
476                 free(c->data[PROP_STATIC_HOSTNAME]);
477                 c->data[PROP_STATIC_HOSTNAME] = NULL;
478         } else {
479                 char *h;
480
481                 if (!hostname_is_valid(name))
482                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
483
484                 h = strdup(name);
485                 if (!h)
486                         return -ENOMEM;
487
488                 free(c->data[PROP_STATIC_HOSTNAME]);
489                 c->data[PROP_STATIC_HOSTNAME] = h;
490         }
491
492         r = context_update_kernel_hostname(c);
493         if (r < 0) {
494                 log_error("Failed to set host name: %s", strerror(-r));
495                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
496         }
497
498         r = context_write_data_static_hostname(c);
499         if (r < 0) {
500                 log_error("Failed to write static host name: %s", strerror(-r));
501                 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r));
502         }
503
504         log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
505
506         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
507
508         return sd_bus_reply_method_return(m, NULL);
509 }
510
511 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) {
512         int interactive;
513         const char *name;
514         int r;
515
516         assert(c);
517         assert(bus);
518         assert(m);
519
520         r = sd_bus_message_read(m, "sb", &name, &interactive);
521         if (r < 0)
522                 return r;
523
524         if (isempty(name))
525                 name = NULL;
526
527         if (streq_ptr(name, c->data[prop]))
528                 return sd_bus_reply_method_return(m, NULL);
529
530         /* Since the pretty hostname should always be changed at the
531          * same time as the static one, use the same policy action for
532          * both... */
533
534         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, prop == PROP_PRETTY_HOSTNAME ?
535                           "org.freedesktop.hostname1.set-static-hostname" :
536                           "org.freedesktop.hostname1.set-machine-info", interactive, error, cb, c);
537         if (r < 0)
538                 return r;
539         if (r == 0)
540                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
541
542         if (isempty(name)) {
543                 free(c->data[prop]);
544                 c->data[prop] = NULL;
545         } else {
546                 char *h;
547
548                 /* The icon name might ultimately be used as file
549                  * name, so better be safe than sorry */
550
551                 if (prop == PROP_ICON_NAME && !filename_is_safe(name))
552                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
553                 if (prop == PROP_PRETTY_HOSTNAME &&
554                     (string_has_cc(name) || chars_intersect(name, "\t")))
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 }