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