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