]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamed.c
util: generalize is_localhost() and use it everywhere where applicable
[thirdparty/systemd.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) && !is_localhost(hn);
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 }