]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamed.c
hostnamed: expose the fallback-hostname setting as a const dbus property
[thirdparty/systemd.git] / src / hostname / hostnamed.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <sys/utsname.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8
9 #include "alloc-util.h"
10 #include "bus-common-errors.h"
11 #include "bus-get-properties.h"
12 #include "bus-log-control-api.h"
13 #include "bus-polkit.h"
14 #include "def.h"
15 #include "env-file-label.h"
16 #include "env-file.h"
17 #include "env-util.h"
18 #include "fileio-label.h"
19 #include "fileio.h"
20 #include "hostname-util.h"
21 #include "id128-util.h"
22 #include "main-func.h"
23 #include "missing_capability.h"
24 #include "nscd-flush.h"
25 #include "nulstr-util.h"
26 #include "os-util.h"
27 #include "parse-util.h"
28 #include "path-util.h"
29 #include "selinux-util.h"
30 #include "service-util.h"
31 #include "signal-util.h"
32 #include "stat-util.h"
33 #include "strv.h"
34 #include "user-util.h"
35 #include "util.h"
36 #include "virt.h"
37
38 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
39
40 enum {
41 /* Read from /etc/hostname */
42 PROP_STATIC_HOSTNAME,
43
44 /* Read from /etc/machine-info */
45 PROP_PRETTY_HOSTNAME,
46 PROP_ICON_NAME,
47 PROP_CHASSIS,
48 PROP_DEPLOYMENT,
49 PROP_LOCATION,
50
51 /* Read from /etc/os-release (or /usr/lib/os-release) */
52 PROP_OS_PRETTY_NAME,
53 PROP_OS_CPE_NAME,
54 PROP_OS_HOME_URL,
55 _PROP_MAX,
56 _PROP_INVALID = -1,
57 };
58
59 typedef struct Context {
60 char *data[_PROP_MAX];
61
62 struct stat etc_hostname_stat;
63 struct stat etc_os_release_stat;
64 struct stat etc_machine_info_stat;
65
66 Hashmap *polkit_registry;
67 } Context;
68
69 static void context_reset(Context *c, uint64_t mask) {
70 int p;
71
72 assert(c);
73
74 for (p = 0; p < _PROP_MAX; p++) {
75 if (!FLAGS_SET(mask, UINT64_C(1) << p))
76 continue;
77
78 c->data[p] = mfree(c->data[p]);
79 }
80 }
81
82 static void context_destroy(Context *c) {
83 assert(c);
84
85 context_reset(c, UINT64_MAX);
86 bus_verify_polkit_async_registry_free(c->polkit_registry);
87 }
88
89 static void context_read_etc_hostname(Context *c) {
90 struct stat current_stat = {};
91 int r;
92
93 assert(c);
94
95 if (stat("/etc/hostname", &current_stat) >= 0 &&
96 stat_inode_unmodified(&c->etc_hostname_stat, &current_stat))
97 return;
98
99 context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME);
100
101 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
102 if (r < 0 && r != -ENOENT)
103 log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m");
104
105 c->etc_hostname_stat = current_stat;
106 }
107
108 static void context_read_machine_info(Context *c) {
109 struct stat current_stat = {};
110 int r;
111
112 assert(c);
113
114 if (stat("/etc/machine-info", &current_stat) >= 0 &&
115 stat_inode_unmodified(&c->etc_machine_info_stat, &current_stat))
116 return;
117
118 context_reset(c,
119 (UINT64_C(1) << PROP_PRETTY_HOSTNAME) |
120 (UINT64_C(1) << PROP_ICON_NAME) |
121 (UINT64_C(1) << PROP_CHASSIS) |
122 (UINT64_C(1) << PROP_DEPLOYMENT) |
123 (UINT64_C(1) << PROP_LOCATION));
124
125 r = parse_env_file(NULL, "/etc/machine-info",
126 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
127 "ICON_NAME", &c->data[PROP_ICON_NAME],
128 "CHASSIS", &c->data[PROP_CHASSIS],
129 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
130 "LOCATION", &c->data[PROP_LOCATION]);
131 if (r < 0 && r != -ENOENT)
132 log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
133
134 c->etc_machine_info_stat = current_stat;
135 }
136
137 static void context_read_os_release(Context *c) {
138 struct stat current_stat = {};
139 int r;
140
141 assert(c);
142
143 if ((stat("/etc/os-release", &current_stat) >= 0 ||
144 stat("/usr/lib/os-release", &current_stat) >= 0) &&
145 stat_inode_unmodified(&c->etc_os_release_stat, &current_stat))
146 return;
147
148 context_reset(c,
149 (UINT64_C(1) << PROP_OS_PRETTY_NAME) |
150 (UINT64_C(1) << PROP_OS_CPE_NAME) |
151 (UINT64_C(1) << PROP_OS_HOME_URL));
152
153 r = parse_os_release(NULL,
154 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
155 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
156 "HOME_URL", &c->data[PROP_OS_HOME_URL],
157 NULL);
158 if (r < 0 && r != -ENOENT)
159 log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
160
161 c->etc_os_release_stat = current_stat;
162 }
163
164 static bool valid_chassis(const char *chassis) {
165 assert(chassis);
166
167 return nulstr_contains(
168 "vm\0"
169 "container\0"
170 "desktop\0"
171 "laptop\0"
172 "convertible\0"
173 "server\0"
174 "tablet\0"
175 "handset\0"
176 "watch\0"
177 "embedded\0",
178 chassis);
179 }
180
181 static bool valid_deployment(const char *deployment) {
182 assert(deployment);
183
184 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
185 }
186
187 static const char* fallback_chassis(void) {
188 char *type;
189 unsigned t;
190 int v, r;
191
192 v = detect_virtualization();
193 if (v < 0)
194 log_debug_errno(v, "Failed to detect virtualization, ignoring: %m");
195 else if (VIRTUALIZATION_IS_VM(v))
196 return "vm";
197 else if (VIRTUALIZATION_IS_CONTAINER(v))
198 return "container";
199
200 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
201 if (r < 0) {
202 log_debug_errno(v, "Failed to read DMI chassis type, ignoring: %m");
203 goto try_acpi;
204 }
205
206 r = safe_atou(type, &t);
207 free(type);
208 if (r < 0) {
209 log_debug_errno(v, "Failed to parse DMI chassis type, ignoring: %m");
210 goto try_acpi;
211 }
212
213 /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
214 additional guesswork on top of that.
215
216 See the SMBIOS Specification 3.0 section 7.4.1 for 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: /* Desktop */
224 case 0x4: /* Low Profile Desktop */
225 case 0x6: /* Mini Tower */
226 case 0x7: /* Tower */
227 case 0xD: /* All in one (i.e. PC built into monitor) */
228 return "desktop";
229
230 case 0x8: /* Portable */
231 case 0x9: /* Laptop */
232 case 0xA: /* Notebook */
233 case 0xE: /* Sub Notebook */
234 return "laptop";
235
236 case 0xB: /* Hand Held */
237 return "handset";
238
239 case 0x11: /* Main Server Chassis */
240 case 0x1C: /* Blade */
241 case 0x1D: /* Blade Enclosure */
242 return "server";
243
244 case 0x1E: /* Tablet */
245 return "tablet";
246
247 case 0x1F: /* Convertible */
248 case 0x20: /* Detachable */
249 return "convertible";
250
251 default:
252 log_debug("Unhandled DMI chassis type 0x%02x, ignoring.", t);
253 }
254
255 try_acpi:
256 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
257 if (r < 0) {
258 log_debug_errno(v, "Failed read ACPI PM profile, ignoring: %m");
259 return NULL;
260 }
261
262 r = safe_atou(type, &t);
263 free(type);
264 if (r < 0) {
265 log_debug_errno(v, "Failed parse ACPI PM profile, ignoring: %m");
266 return NULL;
267 }
268
269 /* We only list the really obvious cases here as the ACPI data is not really super reliable.
270 *
271 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
272 *
273 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
274 */
275
276 switch(t) {
277
278 case 1: /* Desktop */
279 case 3: /* Workstation */
280 case 6: /* Appliance PC */
281 return "desktop";
282
283 case 2: /* Mobile */
284 return "laptop";
285
286 case 4: /* Enterprise Server */
287 case 5: /* SOHO Server */
288 case 7: /* Performance Server */
289 return "server";
290
291 case 8: /* Tablet */
292 return "tablet";
293
294 default:
295 log_debug("Unhandled ACPI PM profile 0x%02x, ignoring.", t);
296 }
297
298 return NULL;
299 }
300
301 static char* context_fallback_icon_name(Context *c) {
302 const char *chassis;
303
304 assert(c);
305
306 if (!isempty(c->data[PROP_CHASSIS]))
307 return strjoin("computer-", c->data[PROP_CHASSIS]);
308
309 chassis = fallback_chassis();
310 if (chassis)
311 return strjoin("computer-", chassis);
312
313 return strdup("computer");
314 }
315
316 static bool hostname_is_useful(const char *hn) {
317 return !isempty(hn) && !is_localhost(hn);
318 }
319
320 static int context_update_kernel_hostname(
321 Context *c,
322 const char *transient_hn) {
323
324 const char *static_hn, *hn;
325 struct utsname u;
326
327 assert(c);
328
329 if (!transient_hn) {
330 /* If no transient hostname is passed in, then let's check what is currently set. */
331 assert_se(uname(&u) >= 0);
332 transient_hn =
333 isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
334 }
335
336 static_hn = c->data[PROP_STATIC_HOSTNAME];
337
338 /* /etc/hostname with something other than "localhost"
339 * has the highest preference ... */
340 if (hostname_is_useful(static_hn))
341 hn = static_hn;
342
343 /* ... the transient hostname, (ie: DHCP) comes next ... */
344 else if (!isempty(transient_hn))
345 hn = transient_hn;
346
347 /* ... fallback to static "localhost.*" ignored above ... */
348 else if (!isempty(static_hn))
349 hn = static_hn;
350
351 /* ... and the ultimate fallback */
352 else
353 hn = FALLBACK_HOSTNAME;
354
355 if (sethostname_idempotent(hn) < 0)
356 return -errno;
357
358 (void) nscd_flush_cache(STRV_MAKE("hosts"));
359
360 return 0;
361 }
362
363 static int context_write_data_static_hostname(Context *c) {
364 assert(c);
365
366 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
367
368 if (unlink("/etc/hostname") < 0)
369 return errno == ENOENT ? 0 : -errno;
370
371 return 0;
372 }
373 return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
374 }
375
376 static int context_write_data_machine_info(Context *c) {
377
378 static const char * const name[_PROP_MAX] = {
379 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
380 [PROP_ICON_NAME] = "ICON_NAME",
381 [PROP_CHASSIS] = "CHASSIS",
382 [PROP_DEPLOYMENT] = "DEPLOYMENT",
383 [PROP_LOCATION] = "LOCATION",
384 };
385
386 _cleanup_strv_free_ char **l = NULL;
387 int r, p;
388
389 assert(c);
390
391 r = load_env_file(NULL, "/etc/machine-info", &l);
392 if (r < 0 && r != -ENOENT)
393 return r;
394
395 for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
396 _cleanup_free_ char *t = NULL;
397 char **u;
398
399 assert(name[p]);
400
401 if (isempty(c->data[p])) {
402 strv_env_unset(l, name[p]);
403 continue;
404 }
405
406 t = strjoin(name[p], "=", c->data[p]);
407 if (!t)
408 return -ENOMEM;
409
410 u = strv_env_set(l, t);
411 if (!u)
412 return -ENOMEM;
413
414 strv_free_and_replace(l, u);
415 }
416
417 if (strv_isempty(l)) {
418 if (unlink("/etc/machine-info") < 0)
419 return errno == ENOENT ? 0 : -errno;
420
421 return 0;
422 }
423
424 return write_env_file_label("/etc/machine-info", l);
425 }
426
427 static int property_get_hostname(
428 sd_bus *bus,
429 const char *path,
430 const char *interface,
431 const char *property,
432 sd_bus_message *reply,
433 void *userdata,
434 sd_bus_error *error) {
435
436 _cleanup_free_ char *current = NULL;
437 int r;
438
439 r = gethostname_strict(&current);
440 if (r == -ENXIO)
441 return sd_bus_message_append(reply, "s", FALLBACK_HOSTNAME);
442 if (r < 0)
443 return r;
444
445 return sd_bus_message_append(reply, "s", current);
446 }
447
448 static int property_get_static_hostname(
449 sd_bus *bus,
450 const char *path,
451 const char *interface,
452 const char *property,
453 sd_bus_message *reply,
454 void *userdata,
455 sd_bus_error *error) {
456
457 Context *c = userdata;
458 assert(c);
459
460 context_read_etc_hostname(c);
461
462 return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]);
463 }
464
465 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_fallback_hostname, "s", FALLBACK_HOSTNAME);
466
467 static int property_get_machine_info_field(
468 sd_bus *bus,
469 const char *path,
470 const char *interface,
471 const char *property,
472 sd_bus_message *reply,
473 void *userdata,
474 sd_bus_error *error) {
475
476 sd_bus_slot *slot;
477 Context *c;
478
479 /* Acquire the context object without this property's userdata offset added. Explanation: we want
480 * access to two pointers here: a) the main context object we cache all properties in, and b) the
481 * pointer to the property field inside the context object that we are supposed to update and
482 * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us
483 * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we
484 * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from
485 * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot
486 * object is (which encapsulates the vtable registration), and then query the 'userdata' field
487 * directly off it. */
488 assert_se(slot = sd_bus_get_current_slot(bus));
489 assert_se(c = sd_bus_slot_get_userdata(slot));
490
491 context_read_machine_info(c);
492
493 return sd_bus_message_append(reply, "s", *(char**) userdata);
494 }
495
496 static int property_get_os_release_field(
497 sd_bus *bus,
498 const char *path,
499 const char *interface,
500 const char *property,
501 sd_bus_message *reply,
502 void *userdata,
503 sd_bus_error *error) {
504
505 sd_bus_slot *slot;
506 Context *c;
507
508 /* As above, acquire the current context without this property's userdata offset added. */
509 assert_se(slot = sd_bus_get_current_slot(bus));
510 assert_se(c = sd_bus_slot_get_userdata(slot));
511
512 context_read_os_release(c);
513
514 return sd_bus_message_append(reply, "s", *(char**) userdata);
515 }
516
517 static int property_get_icon_name(
518 sd_bus *bus,
519 const char *path,
520 const char *interface,
521 const char *property,
522 sd_bus_message *reply,
523 void *userdata,
524 sd_bus_error *error) {
525
526 _cleanup_free_ char *n = NULL;
527 Context *c = userdata;
528 const char *name;
529
530 context_read_machine_info(c);
531
532 if (isempty(c->data[PROP_ICON_NAME]))
533 name = n = context_fallback_icon_name(c);
534 else
535 name = c->data[PROP_ICON_NAME];
536
537 if (!name)
538 return -ENOMEM;
539
540 return sd_bus_message_append(reply, "s", name);
541 }
542
543 static int property_get_chassis(
544 sd_bus *bus,
545 const char *path,
546 const char *interface,
547 const char *property,
548 sd_bus_message *reply,
549 void *userdata,
550 sd_bus_error *error) {
551
552 Context *c = userdata;
553 const char *name;
554
555 context_read_machine_info(c);
556
557 if (isempty(c->data[PROP_CHASSIS]))
558 name = fallback_chassis();
559 else
560 name = c->data[PROP_CHASSIS];
561
562 return sd_bus_message_append(reply, "s", name);
563 }
564
565 static int property_get_uname_field(
566 sd_bus *bus,
567 const char *path,
568 const char *interface,
569 const char *property,
570 sd_bus_message *reply,
571 void *userdata,
572 sd_bus_error *error) {
573
574 struct utsname u;
575
576 assert_se(uname(&u) >= 0);
577
578 return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
579 }
580
581 static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
582 Context *c = userdata;
583 const char *name;
584 int interactive, r;
585 struct utsname u;
586
587 assert(m);
588 assert(c);
589
590 r = sd_bus_message_read(m, "sb", &name, &interactive);
591 if (r < 0)
592 return r;
593
594 context_read_etc_hostname(c);
595
596 if (isempty(name))
597 name = c->data[PROP_STATIC_HOSTNAME];
598
599 if (isempty(name))
600 name = FALLBACK_HOSTNAME;
601
602 if (!hostname_is_valid(name, 0))
603 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
604
605 assert_se(uname(&u) >= 0);
606 if (streq_ptr(name, u.nodename))
607 return sd_bus_reply_method_return(m, NULL);
608
609 r = bus_verify_polkit_async(
610 m,
611 CAP_SYS_ADMIN,
612 "org.freedesktop.hostname1.set-hostname",
613 NULL,
614 interactive,
615 UID_INVALID,
616 &c->polkit_registry,
617 error);
618 if (r < 0)
619 return r;
620 if (r == 0)
621 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
622
623 r = context_update_kernel_hostname(c, name);
624 if (r < 0) {
625 log_error_errno(r, "Failed to set hostname: %m");
626 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
627 }
628
629 log_info("Changed hostname to '%s'", name);
630
631 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
632
633 return sd_bus_reply_method_return(m, NULL);
634 }
635
636 static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
637 Context *c = userdata;
638 const char *name;
639 int interactive;
640 int r;
641
642 assert(m);
643 assert(c);
644
645 r = sd_bus_message_read(m, "sb", &name, &interactive);
646 if (r < 0)
647 return r;
648
649 name = empty_to_null(name);
650
651 context_read_etc_hostname(c);
652
653 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
654 return sd_bus_reply_method_return(m, NULL);
655
656 if (!isempty(name) && !hostname_is_valid(name, 0))
657 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
658
659 r = bus_verify_polkit_async(
660 m,
661 CAP_SYS_ADMIN,
662 "org.freedesktop.hostname1.set-static-hostname",
663 NULL,
664 interactive,
665 UID_INVALID,
666 &c->polkit_registry,
667 error);
668 if (r < 0)
669 return r;
670 if (r == 0)
671 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
672
673 r = free_and_strdup(&c->data[PROP_STATIC_HOSTNAME], name);
674 if (r < 0)
675 return r;
676
677 r = context_update_kernel_hostname(c, NULL);
678 if (r < 0) {
679 log_error_errno(r, "Failed to set hostname: %m");
680 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
681 }
682
683 r = context_write_data_static_hostname(c);
684 if (r < 0) {
685 log_error_errno(r, "Failed to write static hostname: %m");
686 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
687 }
688
689 log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
690
691 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
692
693 return sd_bus_reply_method_return(m, NULL);
694 }
695
696 static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
697 int interactive;
698 const char *name;
699 int r;
700
701 assert(c);
702 assert(m);
703
704 r = sd_bus_message_read(m, "sb", &name, &interactive);
705 if (r < 0)
706 return r;
707
708 name = empty_to_null(name);
709
710 context_read_machine_info(c);
711
712 if (streq_ptr(name, c->data[prop]))
713 return sd_bus_reply_method_return(m, NULL);
714
715 if (!isempty(name)) {
716 /* The icon name might ultimately be used as file
717 * name, so better be safe than sorry */
718
719 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
720 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
721 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
722 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
723 if (prop == PROP_CHASSIS && !valid_chassis(name))
724 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
725 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
726 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
727 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
728 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
729 }
730
731 /* Since the pretty hostname should always be changed at the
732 * same time as the static one, use the same policy action for
733 * both... */
734
735 r = bus_verify_polkit_async(
736 m,
737 CAP_SYS_ADMIN,
738 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
739 NULL,
740 interactive,
741 UID_INVALID,
742 &c->polkit_registry,
743 error);
744 if (r < 0)
745 return r;
746 if (r == 0)
747 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
748
749 r = free_and_strdup(&c->data[prop], name);
750 if (r < 0)
751 return r;
752
753 r = context_write_data_machine_info(c);
754 if (r < 0) {
755 log_error_errno(r, "Failed to write machine info: %m");
756 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
757 }
758
759 log_info("Changed %s to '%s'",
760 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
761 prop == PROP_DEPLOYMENT ? "deployment" :
762 prop == PROP_LOCATION ? "location" :
763 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
764
765 (void) sd_bus_emit_properties_changed(
766 sd_bus_message_get_bus(m),
767 "/org/freedesktop/hostname1",
768 "org.freedesktop.hostname1",
769 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
770 prop == PROP_DEPLOYMENT ? "Deployment" :
771 prop == PROP_LOCATION ? "Location" :
772 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
773
774 return sd_bus_reply_method_return(m, NULL);
775 }
776
777 static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
778 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
779 }
780
781 static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
782 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
783 }
784
785 static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
786 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
787 }
788
789 static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
790 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
791 }
792
793 static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
794 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
795 }
796
797 static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
798 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
799 Context *c = userdata;
800 bool has_uuid = false;
801 int interactive, r;
802 sd_id128_t uuid;
803
804 assert(m);
805 assert(c);
806
807 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
808 if (r == -ENOENT)
809 r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
810 if (r < 0)
811 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
812 "Failed to read product UUID, ignoring: %m");
813 else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
814 log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid));
815 else
816 has_uuid = true;
817
818 if (!has_uuid)
819 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
820 "Failed to read product UUID from firmware.");
821
822 r = sd_bus_message_read(m, "b", &interactive);
823 if (r < 0)
824 return r;
825
826 r = bus_verify_polkit_async(
827 m,
828 CAP_SYS_ADMIN,
829 "org.freedesktop.hostname1.get-product-uuid",
830 NULL,
831 interactive,
832 UID_INVALID,
833 &c->polkit_registry,
834 error);
835 if (r < 0)
836 return r;
837 if (r == 0)
838 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
839
840 r = sd_bus_message_new_method_return(m, &reply);
841 if (r < 0)
842 return r;
843
844 r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid));
845 if (r < 0)
846 return r;
847
848 return sd_bus_send(NULL, reply, NULL);
849 }
850
851 static const sd_bus_vtable hostname_vtable[] = {
852 SD_BUS_VTABLE_START(0),
853 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
854 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
855 SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
856 SD_BUS_PROPERTY("FallbackHostname", "s", property_get_fallback_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
857 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
858 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
859 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
860 SD_BUS_PROPERTY("Location", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
861 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
862 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
863 SD_BUS_PROPERTY("KernelVersion", "s", property_get_uname_field, offsetof(struct utsname, version), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
864 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
865 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
866 SD_BUS_PROPERTY("HomeURL", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
867
868 SD_BUS_METHOD_WITH_NAMES("SetHostname",
869 "sb",
870 SD_BUS_PARAM(hostname)
871 SD_BUS_PARAM(interactive),
872 NULL,,
873 method_set_hostname,
874 SD_BUS_VTABLE_UNPRIVILEGED),
875 SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
876 "sb",
877 SD_BUS_PARAM(hostname)
878 SD_BUS_PARAM(interactive),
879 NULL,,
880 method_set_static_hostname,
881 SD_BUS_VTABLE_UNPRIVILEGED),
882 SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
883 "sb",
884 SD_BUS_PARAM(hostname)
885 SD_BUS_PARAM(interactive),
886 NULL,,
887 method_set_pretty_hostname,
888 SD_BUS_VTABLE_UNPRIVILEGED),
889 SD_BUS_METHOD_WITH_NAMES("SetIconName",
890 "sb",
891 SD_BUS_PARAM(icon)
892 SD_BUS_PARAM(interactive),
893 NULL,,
894 method_set_icon_name,
895 SD_BUS_VTABLE_UNPRIVILEGED),
896 SD_BUS_METHOD_WITH_NAMES("SetChassis",
897 "sb",
898 SD_BUS_PARAM(chassis)
899 SD_BUS_PARAM(interactive),
900 NULL,,
901 method_set_chassis,
902 SD_BUS_VTABLE_UNPRIVILEGED),
903 SD_BUS_METHOD_WITH_NAMES("SetDeployment",
904 "sb",
905 SD_BUS_PARAM(deployment)
906 SD_BUS_PARAM(interactive),
907 NULL,,
908 method_set_deployment,
909 SD_BUS_VTABLE_UNPRIVILEGED),
910 SD_BUS_METHOD_WITH_NAMES("SetLocation",
911 "sb",
912 SD_BUS_PARAM(location)
913 SD_BUS_PARAM(interactive),
914 NULL,,
915 method_set_location,
916 SD_BUS_VTABLE_UNPRIVILEGED),
917 SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
918 "b",
919 SD_BUS_PARAM(interactive),
920 "ay",
921 SD_BUS_PARAM(uuid),
922 method_get_product_uuid,
923 SD_BUS_VTABLE_UNPRIVILEGED),
924
925 SD_BUS_VTABLE_END,
926 };
927
928 static const BusObjectImplementation manager_object = {
929 "/org/freedesktop/hostname1",
930 "org.freedesktop.hostname1",
931 .vtables = BUS_VTABLES(hostname_vtable),
932 };
933
934 static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
935 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
936 int r;
937
938 assert(c);
939 assert(event);
940 assert(ret);
941
942 r = sd_bus_default_system(&bus);
943 if (r < 0)
944 return log_error_errno(r, "Failed to get system bus connection: %m");
945
946 r = bus_add_implementation(bus, &manager_object, c);
947 if (r < 0)
948 return r;
949
950 r = bus_log_control_api_register(bus);
951 if (r < 0)
952 return r;
953
954 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
955 if (r < 0)
956 return log_error_errno(r, "Failed to request name: %m");
957
958 r = sd_bus_attach_event(bus, event, 0);
959 if (r < 0)
960 return log_error_errno(r, "Failed to attach bus to event loop: %m");
961
962 *ret = TAKE_PTR(bus);
963 return 0;
964 }
965
966 static int run(int argc, char *argv[]) {
967 _cleanup_(context_destroy) Context context = {};
968 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
969 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
970 int r;
971
972 log_setup_service();
973
974 r = service_parse_argv("systemd-hostnamed.service",
975 "Manage the system hostname and related metadata.",
976 BUS_IMPLEMENTATIONS(&manager_object,
977 &log_control_object),
978 argc, argv);
979 if (r <= 0)
980 return r;
981
982 umask(0022);
983
984 r = mac_selinux_init();
985 if (r < 0)
986 return r;
987
988 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
989
990 r = sd_event_default(&event);
991 if (r < 0)
992 return log_error_errno(r, "Failed to allocate event loop: %m");
993
994 (void) sd_event_set_watchdog(event, true);
995
996 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
997 if (r < 0)
998 return log_error_errno(r, "Failed to install SIGINT handler: %m");
999
1000 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1001 if (r < 0)
1002 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
1003
1004 r = connect_bus(&context, event, &bus);
1005 if (r < 0)
1006 return r;
1007
1008 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
1009 if (r < 0)
1010 return log_error_errno(r, "Failed to run event loop: %m");
1011
1012 return 0;
1013 }
1014
1015 DEFINE_MAIN_FUNCTION(run);