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