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