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