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