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