]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
Merge pull request #24379 from mrc0mmand/llvm-15
[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"
7ecead8f 23#include "json.h"
85ae63ee 24#include "main-func.h"
36dd5ffd 25#include "missing_capability.h"
7782e0a0 26#include "nscd-flush.h"
d8b4d14d 27#include "nulstr-util.h"
d58ad743 28#include "os-util.h"
6bedfcbb 29#include "parse-util.h"
bb15fafe 30#include "path-util.h"
b9d80698 31#include "sd-device.h"
6bedfcbb 32#include "selinux-util.h"
fc021a5b 33#include "service-util.h"
b22c8bfc 34#include "signal-util.h"
d7f4ad20 35#include "stat-util.h"
60e4fb42 36#include "string-table.h"
6bedfcbb 37#include "strv.h"
ee104e11 38#include "user-util.h"
6bedfcbb
LP
39#include "util.h"
40#include "virt.h"
91f9dcaf 41
799298d6
JG
42#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
43
f8da67cd
LP
44/* Properties we cache are indexed by an enum, to make invalidation easy and systematic (as we can iterate
45 * through them all, and they are uniformly strings). */
4fc7e4f3 46typedef enum {
d7f4ad20 47 /* Read from /etc/hostname */
7640a5de 48 PROP_STATIC_HOSTNAME,
d7f4ad20
LP
49
50 /* Read from /etc/machine-info */
7640a5de
LP
51 PROP_PRETTY_HOSTNAME,
52 PROP_ICON_NAME,
7871c8e9 53 PROP_CHASSIS,
799298d6 54 PROP_DEPLOYMENT,
ce0f1493 55 PROP_LOCATION,
0924ea2b
LP
56 PROP_HARDWARE_VENDOR,
57 PROP_HARDWARE_MODEL,
d7f4ad20
LP
58
59 /* Read from /etc/os-release (or /usr/lib/os-release) */
44c32988
DH
60 PROP_OS_PRETTY_NAME,
61 PROP_OS_CPE_NAME,
d7f4ad20
LP
62 PROP_OS_HOME_URL,
63 _PROP_MAX,
2d93c20e 64 _PROP_INVALID = -EINVAL,
4fc7e4f3 65} HostProperty;
7640a5de 66
66a4c743
LP
67typedef struct Context {
68 char *data[_PROP_MAX];
d7f4ad20 69
60e4fb42
ZJS
70 HostnameSource hostname_source;
71
d7f4ad20
LP
72 struct stat etc_hostname_stat;
73 struct stat etc_os_release_stat;
74 struct stat etc_machine_info_stat;
75
66a4c743
LP
76 Hashmap *polkit_registry;
77} Context;
ad740100 78
d7f4ad20 79static void context_reset(Context *c, uint64_t mask) {
66a4c743
LP
80 assert(c);
81
536970d4 82 for (int p = 0; p < _PROP_MAX; p++) {
d7f4ad20
LP
83 if (!FLAGS_SET(mask, UINT64_C(1) << p))
84 continue;
85
a1e58e8e 86 c->data[p] = mfree(c->data[p]);
d7f4ad20 87 }
7640a5de
LP
88}
89
cfb9433d 90static void context_destroy(Context *c) {
66a4c743
LP
91 assert(c);
92
d7f4ad20 93 context_reset(c, UINT64_MAX);
36e34057 94 bus_verify_polkit_async_registry_free(c->polkit_registry);
66a4c743
LP
95}
96
d7f4ad20
LP
97static void context_read_etc_hostname(Context *c) {
98 struct stat current_stat = {};
7640a5de
LP
99 int r;
100
66a4c743
LP
101 assert(c);
102
d7f4ad20
LP
103 if (stat("/etc/hostname", &current_stat) >= 0 &&
104 stat_inode_unmodified(&c->etc_hostname_stat, &current_stat))
105 return;
106
107 context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME);
7640a5de 108
f35cb39e 109 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
7640a5de 110 if (r < 0 && r != -ENOENT)
d7f4ad20
LP
111 log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m");
112
113 c->etc_hostname_stat = current_stat;
114}
115
116static void context_read_machine_info(Context *c) {
117 struct stat current_stat = {};
118 int r;
119
120 assert(c);
121
122 if (stat("/etc/machine-info", &current_stat) >= 0 &&
123 stat_inode_unmodified(&c->etc_machine_info_stat, &current_stat))
124 return;
125
126 context_reset(c,
127 (UINT64_C(1) << PROP_PRETTY_HOSTNAME) |
128 (UINT64_C(1) << PROP_ICON_NAME) |
129 (UINT64_C(1) << PROP_CHASSIS) |
130 (UINT64_C(1) << PROP_DEPLOYMENT) |
38639aa2
LP
131 (UINT64_C(1) << PROP_LOCATION) |
132 (UINT64_C(1) << PROP_HARDWARE_VENDOR) |
133 (UINT64_C(1) << PROP_HARDWARE_MODEL));
7640a5de 134
aa8fbc74 135 r = parse_env_file(NULL, "/etc/machine-info",
66a4c743
LP
136 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
137 "ICON_NAME", &c->data[PROP_ICON_NAME],
138 "CHASSIS", &c->data[PROP_CHASSIS],
799298d6 139 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
4fc7e4f3 140 "LOCATION", &c->data[PROP_LOCATION],
0924ea2b
LP
141 "HARDWARE_VENDOR", &c->data[PROP_HARDWARE_VENDOR],
142 "HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL]);
7640a5de 143 if (r < 0 && r != -ENOENT)
d7f4ad20
LP
144 log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
145
146 c->etc_machine_info_stat = current_stat;
147}
148
149static void context_read_os_release(Context *c) {
150 struct stat current_stat = {};
151 int r;
152
153 assert(c);
154
155 if ((stat("/etc/os-release", &current_stat) >= 0 ||
156 stat("/usr/lib/os-release", &current_stat) >= 0) &&
157 stat_inode_unmodified(&c->etc_os_release_stat, &current_stat))
158 return;
159
160 context_reset(c,
161 (UINT64_C(1) << PROP_OS_PRETTY_NAME) |
162 (UINT64_C(1) << PROP_OS_CPE_NAME) |
163 (UINT64_C(1) << PROP_OS_HOME_URL));
7640a5de 164
d58ad743
LP
165 r = parse_os_release(NULL,
166 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
167 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
209c1470 168 "HOME_URL", &c->data[PROP_OS_HOME_URL]);
44c32988 169 if (r < 0 && r != -ENOENT)
d7f4ad20 170 log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
44c32988 171
d7f4ad20 172 c->etc_os_release_stat = current_stat;
7640a5de
LP
173}
174
8c8b1800
YW
175static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) {
176 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
177 _cleanup_free_ char *b = NULL;
178 const char *s = NULL;
179 int r;
180
181 r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
182 if (r < 0)
183 return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
184
185 if (database_key)
186 (void) sd_device_get_property_value(device, database_key, &s);
187 if (!s && regular_key)
188 (void) sd_device_get_property_value(device, regular_key, &s);
189
e6fccf02
LP
190 if (!ret)
191 return !!s;
192
8c8b1800
YW
193 if (s) {
194 b = strdup(s);
195 if (!b)
196 return -ENOMEM;
197 }
198
e6fccf02 199 *ret = TAKE_PTR(b);
8c8b1800
YW
200 return !!s;
201}
202
203static int get_hardware_vendor(char **ret) {
204 return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret);
205}
206
207static int get_hardware_model(char **ret) {
208 return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret);
209}
210
c52950c2 211static int get_hardware_firmware_data(const char *sysattr, char **ret) {
96976629
YW
212 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
213 _cleanup_free_ char *b = NULL;
214 const char *s = NULL;
215 int r;
216
c52950c2
SS
217 assert(sysattr);
218
96976629
YW
219 r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
220 if (r < 0)
221 return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
222
c52950c2 223 (void) sd_device_get_sysattr_value(device, sysattr, &s);
96976629
YW
224 if (!isempty(s)) {
225 b = strdup(s);
226 if (!b)
227 return -ENOMEM;
228 }
229
230 if (ret)
231 *ret = TAKE_PTR(b);
232
233 return !isempty(s);
234}
235
c52950c2
SS
236static int get_hardware_serial(char **ret) {
237 int r;
238
239 r = get_hardware_firmware_data("product_serial", ret);
240 if (r <= 0)
241 return get_hardware_firmware_data("board_serial", ret);
242
243 return r;
244}
245
246static int get_firmware_version(char **ret) {
247 return get_hardware_firmware_data("bios_version", ret);
248}
249
e6e6ca82 250static const char* valid_chassis(const char *chassis) {
7871c8e9
LP
251 assert(chassis);
252
e6e6ca82 253 return nulstr_get(
7871c8e9
LP
254 "vm\0"
255 "container\0"
256 "desktop\0"
257 "laptop\0"
34b52450 258 "convertible\0"
7871c8e9
LP
259 "server\0"
260 "tablet\0"
c49e59c1 261 "handset\0"
25fa306e
LP
262 "watch\0"
263 "embedded\0",
7871c8e9
LP
264 chassis);
265}
266
799298d6
JG
267static bool valid_deployment(const char *deployment) {
268 assert(deployment);
269
c2142cf1 270 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
799298d6
JG
271}
272
7871c8e9 273static const char* fallback_chassis(void) {
e6e6ca82 274 const char *chassis;
68cdeab3 275 _cleanup_free_ char *type = NULL;
1b86c7c5 276 Virtualization v;
7640a5de 277 unsigned t;
1b86c7c5 278 int r;
7871c8e9 279
75f86906 280 v = detect_virtualization();
2ac4d1d4
LP
281 if (v < 0)
282 log_debug_errno(v, "Failed to detect virtualization, ignoring: %m");
283 else if (VIRTUALIZATION_IS_VM(v))
7871c8e9 284 return "vm";
2ac4d1d4 285 else if (VIRTUALIZATION_IS_CONTAINER(v))
7871c8e9
LP
286 return "container";
287
219bfe38 288 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
2ac4d1d4 289 if (r < 0) {
105a4245 290 log_debug_errno(r, "Failed to read DMI chassis type, ignoring: %m");
219bfe38 291 goto try_acpi;
2ac4d1d4 292 }
7871c8e9
LP
293
294 r = safe_atou(type, &t);
2ac4d1d4 295 if (r < 0) {
68cdeab3 296 log_debug_errno(r, "Failed to parse DMI chassis type \"%s\", ignoring: %m", type);
219bfe38 297 goto try_acpi;
2ac4d1d4 298 }
7871c8e9 299
219bfe38 300 /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
68cdeab3
ZJS
301 * additional guesswork on top of that.
302 *
67840dbf 303 * See the SMBIOS Specification 3.5.0 section 7.4.1 for details about the values listed here:
68cdeab3 304 *
67840dbf 305 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.5.0.pdf
7871c8e9
LP
306 */
307
219bfe38 308 switch (t) {
7871c8e9 309
67840dbf
LP
310 case 0x03: /* Desktop */
311 case 0x04: /* Low Profile Desktop */
312 case 0x06: /* Mini Tower */
313 case 0x07: /* Tower */
314 case 0x0D: /* All in one (i.e. PC built into monitor) */
315 case 0x23: /* Mini PC */
316 case 0x24: /* Stick PC */
7871c8e9
LP
317 return "desktop";
318
219bfe38
LP
319 case 0x8: /* Portable */
320 case 0x9: /* Laptop */
321 case 0xA: /* Notebook */
322 case 0xE: /* Sub Notebook */
7871c8e9 323 return "laptop";
7640a5de 324
219bfe38
LP
325 case 0xB: /* Hand Held */
326 return "handset";
327
328 case 0x11: /* Main Server Chassis */
329 case 0x1C: /* Blade */
330 case 0x1D: /* Blade Enclosure */
7871c8e9 331 return "server";
7640a5de 332
219bfe38 333 case 0x1E: /* Tablet */
7871c8e9 334 return "tablet";
b70af833
DH
335
336 case 0x1F: /* Convertible */
b4227dbb 337 case 0x20: /* Detachable */
b70af833 338 return "convertible";
2ac4d1d4 339
67840dbf
LP
340 case 0x21: /* IoT Gateway */
341 case 0x22: /* Embedded PC */
342 return "embedded";
343
2ac4d1d4
LP
344 default:
345 log_debug("Unhandled DMI chassis type 0x%02x, ignoring.", t);
7871c8e9
LP
346 }
347
219bfe38 348try_acpi:
68cdeab3 349 type = mfree(type);
219bfe38 350 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
2ac4d1d4 351 if (r < 0) {
105a4245 352 log_debug_errno(r, "Failed read ACPI PM profile, ignoring: %m");
e6e6ca82 353 goto try_devicetree;
2ac4d1d4 354 }
7640a5de
LP
355
356 r = safe_atou(type, &t);
2ac4d1d4 357 if (r < 0) {
68cdeab3 358 log_debug_errno(r, "Failed parse ACPI PM profile \"%s\", ignoring: %m", type);
e6e6ca82 359 goto try_devicetree;
2ac4d1d4 360 }
7640a5de 361
219bfe38
LP
362 /* We only list the really obvious cases here as the ACPI data is not really super reliable.
363 *
364 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
365 *
366 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
41550d40 367 */
7640a5de 368
79893116 369 switch (t) {
7640a5de 370
219bfe38
LP
371 case 1: /* Desktop */
372 case 3: /* Workstation */
373 case 6: /* Appliance PC */
7871c8e9 374 return "desktop";
7640a5de 375
219bfe38 376 case 2: /* Mobile */
7871c8e9
LP
377 return "laptop";
378
219bfe38
LP
379 case 4: /* Enterprise Server */
380 case 5: /* SOHO Server */
381 case 7: /* Performance Server */
7871c8e9 382 return "server";
f3f4f008 383
219bfe38 384 case 8: /* Tablet */
f3f4f008 385 return "tablet";
2ac4d1d4
LP
386
387 default:
388 log_debug("Unhandled ACPI PM profile 0x%02x, ignoring.", t);
7640a5de
LP
389 }
390
e6e6ca82 391try_devicetree:
68cdeab3 392 type = mfree(type);
e6cbe697 393 r = read_one_line_file("/proc/device-tree/chassis-type", &type);
e6e6ca82
AF
394 if (r < 0) {
395 log_debug_errno(r, "Failed to read device-tree chassis type, ignoring: %m");
396 return NULL;
397 }
398
399 /* Note that the Devicetree specification uses the very same vocabulary
400 * of chassis types as we do, hence we do not need to translate these types:
401 *
402 * https://github.com/devicetree-org/devicetree-specification/blob/master/source/chapter3-devicenodes.rst */
403 chassis = valid_chassis(type);
404 if (!chassis)
68cdeab3 405 log_debug("Invalid device-tree chassis type \"%s\", ignoring.", type);
e6e6ca82 406 return chassis;
7640a5de
LP
407}
408
8c8b1800
YW
409static char* context_get_chassis(Context *c) {
410 const char *fallback;
411 char *dmi;
7871c8e9 412
66a4c743
LP
413 assert(c);
414
415 if (!isempty(c->data[PROP_CHASSIS]))
8c8b1800
YW
416 return strdup(c->data[PROP_CHASSIS]);
417
418 if (get_dmi_data("ID_CHASSIS", NULL, &dmi) >= 0)
419 return dmi;
420
421 fallback = fallback_chassis();
422 if (fallback)
423 return strdup(fallback);
424
425 return NULL;
426}
427
428static char* context_fallback_icon_name(Context *c) {
429 _cleanup_free_ char *chassis = NULL;
430
431 assert(c);
7871c8e9 432
8c8b1800 433 chassis = context_get_chassis(c);
7871c8e9 434 if (chassis)
b910cc72 435 return strjoin("computer-", chassis);
7871c8e9
LP
436
437 return strdup("computer");
438}
439
aa994368
LP
440static int context_update_kernel_hostname(
441 Context *c,
442 const char *transient_hn) {
443
05c6f341 444 _cleanup_free_ char *_hn_free = NULL;
d39079fc 445 const char *hn;
60e4fb42 446 HostnameSource hns;
7d9ec609 447 int r;
7640a5de 448
66a4c743
LP
449 assert(c);
450
d39079fc 451 /* /etc/hostname has the highest preference ... */
60e4fb42 452 if (c->data[PROP_STATIC_HOSTNAME]) {
d39079fc 453 hn = c->data[PROP_STATIC_HOSTNAME];
60e4fb42 454 hns = HOSTNAME_STATIC;
c779a442 455
38b38500 456 /* ... the transient hostname, (ie: DHCP) comes next ... */
60e4fb42 457 } else if (transient_hn) {
aa994368 458 hn = transient_hn;
60e4fb42 459 hns = HOSTNAME_TRANSIENT;
7640a5de 460
c779a442 461 /* ... and the ultimate fallback */
60e4fb42 462 } else {
05c6f341
ZJS
463 hn = _hn_free = get_default_hostname();
464 if (!hn)
465 return log_oom();
466
8770c813 467 hns = HOSTNAME_DEFAULT;
60e4fb42 468 }
c779a442 469
7d9ec609
ZJS
470 r = sethostname_idempotent(hn);
471 if (r < 0)
60e4fb42
ZJS
472 return log_error_errno(r, "Failed to set hostname: %m");
473
474 if (c->hostname_source != hns) {
475 c->hostname_source = hns;
476 r = 1;
477 }
7640a5de 478
7782e0a0 479 (void) nscd_flush_cache(STRV_MAKE("hosts"));
60e4fb42
ZJS
480
481 if (r == 0)
482 log_debug("Hostname was already set to <%s>.", hn);
483 else {
484 log_info("Hostname set to <%s> (%s)", hn, hostname_source_to_string(hns));
485
486 hostname_update_source_hint(hn, hns);
487 }
488
efda832d 489 return r; /* 0 if no change, 1 if something was done */
7640a5de
LP
490}
491
ba12e41d
YW
492static void unset_statp(struct stat **p) {
493 if (!*p)
494 return;
495
496 **p = (struct stat) {};
497}
498
66a4c743 499static int context_write_data_static_hostname(Context *c) {
ba12e41d
YW
500 _cleanup_(unset_statp) struct stat *s = NULL;
501 int r;
502
66a4c743
LP
503 assert(c);
504
ba12e41d
YW
505 /* Make sure that if we fail here, we invalidate the cached information, since it was updated
506 * already, even if we can't make it hit the disk. */
507 s = &c->etc_hostname_stat;
508
66a4c743 509 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
ba12e41d
YW
510 if (unlink("/etc/hostname") < 0 && errno != ENOENT)
511 return -errno;
512
513 TAKE_PTR(s);
7640a5de
LP
514 return 0;
515 }
536970d4 516
ba12e41d
YW
517 r = write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
518 if (r < 0)
519 return r;
520
521 TAKE_PTR(s);
522 return 0;
7640a5de
LP
523}
524
f200e8bb 525static int context_write_data_machine_info(Context *c) {
ba12e41d 526 _cleanup_(unset_statp) struct stat *s = NULL;
7640a5de
LP
527 static const char * const name[_PROP_MAX] = {
528 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
7871c8e9 529 [PROP_ICON_NAME] = "ICON_NAME",
799298d6
JG
530 [PROP_CHASSIS] = "CHASSIS",
531 [PROP_DEPLOYMENT] = "DEPLOYMENT",
ce0f1493 532 [PROP_LOCATION] = "LOCATION",
7640a5de 533 };
0ccad099 534 _cleanup_strv_free_ char **l = NULL;
536970d4 535 int r;
7640a5de 536
66a4c743
LP
537 assert(c);
538
ba12e41d
YW
539 /* Make sure that if we fail here, we invalidate the cached information, since it was updated
540 * already, even if we can't make it hit the disk. */
541 s = &c->etc_machine_info_stat;
542
aa8fbc74 543 r = load_env_file(NULL, "/etc/machine-info", &l);
7640a5de
LP
544 if (r < 0 && r != -ENOENT)
545 return r;
546
536970d4 547 for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
7640a5de
LP
548 assert(name[p]);
549
f08231fe
ZJS
550 r = strv_env_assign(&l, name[p], empty_to_null(c->data[p]));
551 if (r < 0)
552 return r;
7640a5de
LP
553 }
554
555 if (strv_isempty(l)) {
ba12e41d
YW
556 if (unlink("/etc/machine-info") < 0 && errno != ENOENT)
557 return -errno;
7640a5de 558
ba12e41d 559 TAKE_PTR(s);
7640a5de
LP
560 return 0;
561 }
562
ba12e41d
YW
563 r = write_env_file_label("/etc/machine-info", l);
564 if (r < 0)
565 return r;
566
567 TAKE_PTR(s);
568 return 0;
7640a5de
LP
569}
570
4fc7e4f3
YW
571static int property_get_hardware_property(
572 sd_bus_message *reply,
573 Context *c,
574 HostProperty prop,
575 int (*getter)(char **)) {
576
577 _cleanup_free_ char *from_dmi = NULL;
578
579 assert(reply);
580 assert(c);
0924ea2b 581 assert(IN_SET(prop, PROP_HARDWARE_VENDOR, PROP_HARDWARE_MODEL));
4fc7e4f3
YW
582 assert(getter);
583
584 context_read_machine_info(c);
585
586 if (isempty(c->data[prop]))
587 (void) getter(&from_dmi);
588
589 return sd_bus_message_append(reply, "s", from_dmi ?: c->data[prop]);
590}
591
b9d80698
FB
592static int property_get_hardware_vendor(
593 sd_bus *bus,
594 const char *path,
595 const char *interface,
596 const char *property,
597 sd_bus_message *reply,
598 void *userdata,
599 sd_bus_error *error) {
b9d80698 600
0924ea2b 601 return property_get_hardware_property(reply, userdata, PROP_HARDWARE_VENDOR, get_hardware_vendor);
b9d80698
FB
602}
603
604static int property_get_hardware_model(
605 sd_bus *bus,
606 const char *path,
607 const char *interface,
608 const char *property,
609 sd_bus_message *reply,
610 void *userdata,
611 sd_bus_error *error) {
b9d80698 612
0924ea2b 613 return property_get_hardware_property(reply, userdata, PROP_HARDWARE_MODEL, get_hardware_model);
b9d80698
FB
614}
615
c52950c2
SS
616static int property_get_firmware_version(
617 sd_bus *bus,
618 const char *path,
619 const char *interface,
620 const char *property,
621 sd_bus_message *reply,
622 void *userdata,
623 sd_bus_error *error) {
624
625 _cleanup_free_ char *firmware_version = NULL;
626
627 (void) get_firmware_version(&firmware_version);
628
629 return sd_bus_message_append(reply, "s", firmware_version);
630}
631
aa994368
LP
632static int property_get_hostname(
633 sd_bus *bus,
634 const char *path,
635 const char *interface,
636 const char *property,
637 sd_bus_message *reply,
638 void *userdata,
639 sd_bus_error *error) {
640
05c6f341 641 _cleanup_free_ char *hn = NULL;
aa994368
LP
642 int r;
643
05c6f341
ZJS
644 r = gethostname_strict(&hn);
645 if (r < 0) {
646 if (r != -ENXIO)
647 return r;
aa994368 648
05c6f341
ZJS
649 hn = get_default_hostname();
650 if (!hn)
651 return -ENOMEM;
652 }
653
654 return sd_bus_message_append(reply, "s", hn);
aa994368
LP
655}
656
d7f4ad20
LP
657static int property_get_static_hostname(
658 sd_bus *bus,
659 const char *path,
660 const char *interface,
661 const char *property,
662 sd_bus_message *reply,
663 void *userdata,
664 sd_bus_error *error) {
665
666 Context *c = userdata;
667 assert(c);
668
669 context_read_etc_hostname(c);
670
671 return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]);
672}
673
8770c813 674static int property_get_default_hostname(
05c6f341
ZJS
675 sd_bus *bus,
676 const char *path,
677 const char *interface,
678 const char *property,
679 sd_bus_message *reply,
680 void *userdata,
681 sd_bus_error *error) {
682
61d44b6b
LP
683 _cleanup_free_ char *hn = NULL;
684
685 hn = get_default_hostname();
05c6f341
ZJS
686 if (!hn)
687 return log_oom();
688
689 return sd_bus_message_append(reply, "s", hn);
690}
ce6b138c 691
f2a434a5 692static void context_determine_hostname_source(Context *c) {
0995accd 693 _cleanup_free_ char *hostname = NULL;
f2a434a5
LP
694 int r;
695
696 assert(c);
697
698 if (c->hostname_source >= 0)
699 return;
700
0995accd 701 (void) gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hostname);
f2a434a5
LP
702
703 if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME]))
704 c->hostname_source = HOSTNAME_STATIC;
705 else {
0995accd
YW
706 _cleanup_free_ char *fallback = NULL;
707
f2a434a5
LP
708 /* If the hostname was not set by us, try to figure out where it came from. If we set it to
709 * the default hostname, the file will tell us. We compare the string because it is possible
710 * that the hostname was set by an older version that had a different fallback, in the
711 * initramfs or before we reexecuted. */
712
713 r = read_one_line_file("/run/systemd/default-hostname", &fallback);
714 if (r < 0 && r != -ENOENT)
715 log_warning_errno(r, "Failed to read /run/systemd/default-hostname, ignoring: %m");
716
717 if (streq_ptr(fallback, hostname))
718 c->hostname_source = HOSTNAME_DEFAULT;
719 else
720 c->hostname_source = HOSTNAME_TRANSIENT;
721 }
722}
723
60e4fb42
ZJS
724static int property_get_hostname_source(
725 sd_bus *bus,
726 const char *path,
727 const char *interface,
728 const char *property,
729 sd_bus_message *reply,
730 void *userdata,
731 sd_bus_error *error) {
732
733 Context *c = userdata;
60e4fb42
ZJS
734 assert(c);
735
736 context_read_etc_hostname(c);
f2a434a5 737 context_determine_hostname_source(c);
60e4fb42
ZJS
738
739 return sd_bus_message_append(reply, "s", hostname_source_to_string(c->hostname_source));
740}
741
d7f4ad20
LP
742static int property_get_machine_info_field(
743 sd_bus *bus,
744 const char *path,
745 const char *interface,
746 const char *property,
747 sd_bus_message *reply,
748 void *userdata,
749 sd_bus_error *error) {
750
751 sd_bus_slot *slot;
752 Context *c;
753
754 /* Acquire the context object without this property's userdata offset added. Explanation: we want
755 * access to two pointers here: a) the main context object we cache all properties in, and b) the
756 * pointer to the property field inside the context object that we are supposed to update and
757 * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us
758 * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we
759 * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from
760 * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot
761 * object is (which encapsulates the vtable registration), and then query the 'userdata' field
762 * directly off it. */
763 assert_se(slot = sd_bus_get_current_slot(bus));
764 assert_se(c = sd_bus_slot_get_userdata(slot));
765
766 context_read_machine_info(c);
767
768 return sd_bus_message_append(reply, "s", *(char**) userdata);
769}
770
771static int property_get_os_release_field(
772 sd_bus *bus,
773 const char *path,
774 const char *interface,
775 const char *property,
776 sd_bus_message *reply,
777 void *userdata,
778 sd_bus_error *error) {
779
780 sd_bus_slot *slot;
781 Context *c;
782
783 /* As above, acquire the current context without this property's userdata offset added. */
784 assert_se(slot = sd_bus_get_current_slot(bus));
785 assert_se(c = sd_bus_slot_get_userdata(slot));
786
787 context_read_os_release(c);
788
789 return sd_bus_message_append(reply, "s", *(char**) userdata);
790}
791
66a4c743
LP
792static int property_get_icon_name(
793 sd_bus *bus,
794 const char *path,
795 const char *interface,
796 const char *property,
797 sd_bus_message *reply,
ebcf1f97
LP
798 void *userdata,
799 sd_bus_error *error) {
7640a5de 800
66a4c743
LP
801 _cleanup_free_ char *n = NULL;
802 Context *c = userdata;
803 const char *name;
7640a5de 804
d7f4ad20
LP
805 context_read_machine_info(c);
806
66a4c743
LP
807 if (isempty(c->data[PROP_ICON_NAME]))
808 name = n = context_fallback_icon_name(c);
7640a5de 809 else
66a4c743
LP
810 name = c->data[PROP_ICON_NAME];
811
812 if (!name)
813 return -ENOMEM;
7640a5de 814
ebcf1f97 815 return sd_bus_message_append(reply, "s", name);
7640a5de
LP
816}
817
66a4c743
LP
818static int property_get_chassis(
819 sd_bus *bus,
820 const char *path,
821 const char *interface,
822 const char *property,
823 sd_bus_message *reply,
ebcf1f97
LP
824 void *userdata,
825 sd_bus_error *error) {
7871c8e9 826
8c8b1800 827 _cleanup_free_ char *chassis = NULL;
66a4c743 828 Context *c = userdata;
7871c8e9 829
d7f4ad20
LP
830 context_read_machine_info(c);
831
8c8b1800 832 chassis = context_get_chassis(c);
7871c8e9 833
8c8b1800 834 return sd_bus_message_append(reply, "s", chassis);
7871c8e9
LP
835}
836
72f48cd3
LP
837static int property_get_uname_field(
838 sd_bus *bus,
839 const char *path,
840 const char *interface,
841 const char *property,
842 sd_bus_message *reply,
843 void *userdata,
844 sd_bus_error *error) {
845
846 struct utsname u;
847
848 assert_se(uname(&u) >= 0);
849
850 return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
851}
852
19070062 853static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
854 Context *c = userdata;
855 const char *name;
aa994368 856 int interactive, r;
d200735e 857
19070062
LP
858 assert(m);
859 assert(c);
860
66a4c743
LP
861 r = sd_bus_message_read(m, "sb", &name, &interactive);
862 if (r < 0)
ebcf1f97 863 return r;
d200735e 864
60e4fb42 865 name = empty_to_null(name);
7640a5de 866
60e4fb42
ZJS
867 /* We always go through with the procedure below without comparing to the current hostname, because
868 * we might want to adjust hostname source information even if the actual hostname is unchanged. */
7640a5de 869
f190ac48 870 if (name && !hostname_is_valid(name, 0))
ebcf1f97 871 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
7640a5de 872
60e4fb42 873 context_read_etc_hostname(c);
7640a5de 874
c529695e
LP
875 r = bus_verify_polkit_async(
876 m,
877 CAP_SYS_ADMIN,
878 "org.freedesktop.hostname1.set-hostname",
403ed0e5 879 NULL,
c529695e
LP
880 interactive,
881 UID_INVALID,
882 &c->polkit_registry,
883 error);
66a4c743 884 if (r < 0)
ebcf1f97 885 return r;
66a4c743
LP
886 if (r == 0)
887 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 888
aa994368 889 r = context_update_kernel_hostname(c, name);
60e4fb42 890 if (r < 0)
1b4cd646 891 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
60e4fb42 892 else if (r > 0)
efda832d
ZJS
893 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
894 "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
895 "Hostname", "HostnameSource", NULL);
7640a5de 896
df2d202e 897 return sd_bus_reply_method_return(m, NULL);
66a4c743 898}
7640a5de 899
19070062 900static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
901 Context *c = userdata;
902 const char *name;
102d8f81 903 int interactive;
66a4c743 904 int r;
7640a5de 905
19070062
LP
906 assert(m);
907 assert(c);
908
66a4c743
LP
909 r = sd_bus_message_read(m, "sb", &name, &interactive);
910 if (r < 0)
ebcf1f97 911 return r;
7640a5de 912
3c6f7c34 913 name = empty_to_null(name);
7640a5de 914
d7f4ad20
LP
915 context_read_etc_hostname(c);
916
66a4c743 917 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 918 return sd_bus_reply_method_return(m, NULL);
7640a5de 919
60e4fb42 920 if (name && !hostname_is_valid(name, 0))
c650f207
YW
921 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
922
c529695e
LP
923 r = bus_verify_polkit_async(
924 m,
925 CAP_SYS_ADMIN,
926 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 927 NULL,
c529695e
LP
928 interactive,
929 UID_INVALID,
930 &c->polkit_registry,
931 error);
66a4c743 932 if (r < 0)
ebcf1f97 933 return r;
66a4c743
LP
934 if (r == 0)
935 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 936
e8acf091 937 r = free_and_strdup_warn(&c->data[PROP_STATIC_HOSTNAME], name);
c650f207
YW
938 if (r < 0)
939 return r;
7640a5de 940
66a4c743
LP
941 r = context_write_data_static_hostname(c);
942 if (r < 0) {
38b38500 943 log_error_errno(r, "Failed to write static hostname: %m");
957991b7
YW
944 if (ERRNO_IS_PRIVILEGE(r))
945 return sd_bus_error_set(error, BUS_ERROR_FILE_IS_PROTECTED, "Not allowed to update /etc/hostname.");
946 if (r == -EROFS)
947 return sd_bus_error_set(error, BUS_ERROR_READ_ONLY_FILESYSTEM, "/etc/hostname is in a read-only filesystem.");
1b4cd646 948 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 949 }
7640a5de 950
60e4fb42
ZJS
951 r = context_update_kernel_hostname(c, NULL);
952 if (r < 0) {
953 log_error_errno(r, "Failed to set hostname: %m");
954 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
955 }
7640a5de 956
efda832d 957 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
60e4fb42
ZJS
958 "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
959 "StaticHostname", "Hostname", "HostnameSource", NULL);
7640a5de 960
df2d202e 961 return sd_bus_reply_method_return(m, NULL);
66a4c743 962}
7640a5de 963
19070062 964static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 965 int interactive;
66a4c743
LP
966 const char *name;
967 int r;
968
969 assert(c);
66a4c743
LP
970 assert(m);
971
972 r = sd_bus_message_read(m, "sb", &name, &interactive);
973 if (r < 0)
ebcf1f97 974 return r;
66a4c743 975
3c6f7c34 976 name = empty_to_null(name);
66a4c743 977
d7f4ad20
LP
978 context_read_machine_info(c);
979
66a4c743 980 if (streq_ptr(name, c->data[prop]))
df2d202e 981 return sd_bus_reply_method_return(m, NULL);
7640a5de 982
c650f207
YW
983 if (!isempty(name)) {
984 /* The icon name might ultimately be used as file
985 * name, so better be safe than sorry */
986
987 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
988 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
989 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
38b38500 990 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
c650f207
YW
991 if (prop == PROP_CHASSIS && !valid_chassis(name))
992 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
993 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
994 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
995 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
996 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
997 }
998
66a4c743
LP
999 /* Since the pretty hostname should always be changed at the
1000 * same time as the static one, use the same policy action for
1001 * both... */
1002
c529695e
LP
1003 r = bus_verify_polkit_async(
1004 m,
1005 CAP_SYS_ADMIN,
1006 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 1007 NULL,
c529695e
LP
1008 interactive,
1009 UID_INVALID,
1010 &c->polkit_registry,
1011 error);
66a4c743 1012 if (r < 0)
ebcf1f97 1013 return r;
66a4c743
LP
1014 if (r == 0)
1015 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1016
e8acf091 1017 r = free_and_strdup_warn(&c->data[prop], name);
c650f207
YW
1018 if (r < 0)
1019 return r;
7640a5de 1020
f200e8bb 1021 r = context_write_data_machine_info(c);
66a4c743 1022 if (r < 0) {
da927ba9 1023 log_error_errno(r, "Failed to write machine info: %m");
957991b7
YW
1024 if (ERRNO_IS_PRIVILEGE(r))
1025 return sd_bus_error_set(error, BUS_ERROR_FILE_IS_PROTECTED, "Not allowed to update /etc/machine-info.");
1026 if (r == -EROFS)
1027 return sd_bus_error_set(error, BUS_ERROR_READ_ONLY_FILESYSTEM, "/etc/machine-info is in a read-only filesystem.");
1b4cd646 1028 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 1029 }
7640a5de 1030
66a4c743 1031 log_info("Changed %s to '%s'",
38b38500 1032 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
799298d6 1033 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 1034 prop == PROP_LOCATION ? "location" :
66a4c743 1035 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 1036
19070062
LP
1037 (void) sd_bus_emit_properties_changed(
1038 sd_bus_message_get_bus(m),
1039 "/org/freedesktop/hostname1",
1040 "org.freedesktop.hostname1",
1041 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
1042 prop == PROP_DEPLOYMENT ? "Deployment" :
1043 prop == PROP_LOCATION ? "Location" :
1044 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 1045
df2d202e 1046 return sd_bus_reply_method_return(m, NULL);
66a4c743 1047}
7640a5de 1048
19070062
LP
1049static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1050 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
1051}
1052
19070062
LP
1053static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1054 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
1055}
1056
19070062
LP
1057static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1058 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
1059}
1060
19070062
LP
1061static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1062 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
1063}
1064
19070062
LP
1065static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1066 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
1067}
1068
21e627da
YW
1069static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1070 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1071 Context *c = userdata;
1072 int interactive, r;
5704cd73 1073 sd_id128_t uuid;
21e627da
YW
1074
1075 assert(m);
1076 assert(c);
1077
21e627da
YW
1078 r = sd_bus_message_read(m, "b", &interactive);
1079 if (r < 0)
1080 return r;
1081
1082 r = bus_verify_polkit_async(
1083 m,
1084 CAP_SYS_ADMIN,
1085 "org.freedesktop.hostname1.get-product-uuid",
1086 NULL,
1087 interactive,
1088 UID_INVALID,
1089 &c->polkit_registry,
1090 error);
1091 if (r < 0)
1092 return r;
1093 if (r == 0)
1094 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1095
66ee2298
LP
1096 r = id128_get_product(&uuid);
1097 if (r < 0) {
1098 if (r == -EADDRNOTAVAIL)
1099 log_debug_errno(r, "DMI product UUID is all 0x00 or all 0xFF, ignoring.");
1100 else
1101 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
1102 "Failed to read product UUID, ignoring: %m");
1103
1104 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
1105 "Failed to read product UUID from firmware.");
1106 }
1107
21e627da
YW
1108 r = sd_bus_message_new_method_return(m, &reply);
1109 if (r < 0)
1110 return r;
1111
c52e295d 1112 r = sd_bus_message_append_array(reply, 'y', uuid.bytes, sizeof(uuid.bytes));
21e627da
YW
1113 if (r < 0)
1114 return r;
1115
1116 return sd_bus_send(NULL, reply, NULL);
1117}
1118
96976629
YW
1119static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1120 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1121 _cleanup_free_ char *serial = NULL;
1122 Context *c = userdata;
ff28d259 1123 int r;
96976629
YW
1124
1125 assert(m);
1126 assert(c);
1127
96976629
YW
1128 r = bus_verify_polkit_async(
1129 m,
1130 CAP_SYS_ADMIN,
1131 "org.freedesktop.hostname1.get-hardware-serial",
1132 NULL,
ff28d259 1133 false,
96976629
YW
1134 UID_INVALID,
1135 &c->polkit_registry,
1136 error);
1137 if (r < 0)
1138 return r;
1139 if (r == 0)
1140 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1141
1142 r = get_hardware_serial(&serial);
1143 if (r < 0)
1144 return r;
1145
1146 r = sd_bus_message_new_method_return(m, &reply);
1147 if (r < 0)
1148 return r;
1149
1150 r = sd_bus_message_append(reply, "s", serial);
1151 if (r < 0)
1152 return r;
1153
1154 return sd_bus_send(NULL, reply, NULL);
1155}
1156
7ecead8f 1157static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
96976629 1158 _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
c52950c2 1159 *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL;
7ecead8f
LP
1160 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1161 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1162 sd_id128_t product_uuid = SD_ID128_NULL;
7ecead8f
LP
1163 Context *c = userdata;
1164 bool privileged;
1165 struct utsname u;
1166 int r;
1167
1168 assert(m);
1169 assert(c);
1170
1171 r = bus_verify_polkit_async(
1172 m,
1173 CAP_SYS_ADMIN,
96976629 1174 "org.freedesktop.hostname1.get-description",
7ecead8f
LP
1175 NULL,
1176 false,
1177 UID_INVALID,
1178 &c->polkit_registry,
1179 NULL);
1180 if (r == 0)
1181 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1182
1183 /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
1184 * the product ID which we'll check explicitly. */
1185 privileged = r > 0;
1186
1187 context_read_etc_hostname(c);
1188 context_read_machine_info(c);
1189 context_read_os_release(c);
1190 context_determine_hostname_source(c);
1191
1192 r = gethostname_strict(&hn);
1193 if (r < 0) {
1194 if (r != -ENXIO)
1195 return log_error_errno(r, "Failed to read local host name: %m");
1196
1197 hn = get_default_hostname();
1198 if (!hn)
1199 return log_oom();
1200 }
1201
1202 dhn = get_default_hostname();
1203 if (!dhn)
1204 return log_oom();
1205
1206 if (isempty(c->data[PROP_ICON_NAME]))
1207 in = context_fallback_icon_name(c);
1208
8c8b1800 1209 chassis = context_get_chassis(c);
7ecead8f
LP
1210
1211 assert_se(uname(&u) >= 0);
1212
0924ea2b 1213 if (isempty(c->data[PROP_HARDWARE_VENDOR]))
4fc7e4f3 1214 (void) get_hardware_vendor(&vendor);
0924ea2b 1215 if (isempty(c->data[PROP_HARDWARE_MODEL]))
4fc7e4f3 1216 (void) get_hardware_model(&model);
7ecead8f 1217
96976629
YW
1218 if (privileged) {
1219 /* The product UUID and hardware serial is only available to privileged clients */
1220 (void) id128_get_product(&product_uuid);
1221 (void) get_hardware_serial(&serial);
1222 }
c52950c2 1223 (void) get_firmware_version(&firmware_version);
7ecead8f
LP
1224
1225 r = json_build(&v, JSON_BUILD_OBJECT(
1226 JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
1227 JSON_BUILD_PAIR("StaticHostname", JSON_BUILD_STRING(c->data[PROP_STATIC_HOSTNAME])),
1228 JSON_BUILD_PAIR("PrettyHostname", JSON_BUILD_STRING(c->data[PROP_PRETTY_HOSTNAME])),
1229 JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)),
1230 JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))),
1231 JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])),
8c8b1800 1232 JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis)),
7ecead8f
LP
1233 JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])),
1234 JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])),
1235 JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)),
1236 JSON_BUILD_PAIR("KernelRelease", JSON_BUILD_STRING(u.release)),
1237 JSON_BUILD_PAIR("KernelVersion", JSON_BUILD_STRING(u.version)),
1238 JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])),
1239 JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])),
1240 JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])),
0924ea2b
LP
1241 JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor ?: c->data[PROP_HARDWARE_VENDOR])),
1242 JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_HARDWARE_MODEL])),
96976629 1243 JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
c52950c2 1244 JSON_BUILD_PAIR("FirmwareVersion", JSON_BUILD_STRING(firmware_version)),
7ecead8f
LP
1245 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
1246 JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
1247
1248 if (r < 0)
1249 return log_error_errno(r, "Failed to build JSON data: %m");
1250
1251 r = json_variant_format(v, 0, &text);
1252 if (r < 0)
1253 return log_error_errno(r, "Failed to format JSON data: %m");
1254
1255 r = sd_bus_message_new_method_return(m, &reply);
1256 if (r < 0)
1257 return r;
1258
1259 r = sd_bus_message_append(reply, "s", text);
1260 if (r < 0)
1261 return r;
1262
1263 return sd_bus_send(NULL, reply, NULL);
1264}
1265
66a4c743
LP
1266static const sd_bus_vtable hostname_vtable[] = {
1267 SD_BUS_VTABLE_START(0),
aa994368 1268 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
1269 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1270 SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
8770c813 1271 SD_BUS_PROPERTY("DefaultHostname", "s", property_get_default_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
60e4fb42 1272 SD_BUS_PROPERTY("HostnameSource", "s", property_get_hostname_source, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
66a4c743
LP
1273 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1274 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
1275 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1276 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
1277 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
1278 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
1279 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
1280 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
1281 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
1282 SD_BUS_PROPERTY("HomeURL", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
b9d80698
FB
1283 SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
1284 SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
c52950c2 1285 SD_BUS_PROPERTY("FirmwareVersion", "s", property_get_firmware_version, 0, SD_BUS_VTABLE_PROPERTY_CONST),
106d79be 1286
92c9f47d
SS
1287 SD_BUS_METHOD_WITH_ARGS("SetHostname",
1288 SD_BUS_ARGS("s", hostname, "b", interactive),
1289 SD_BUS_NO_RESULT,
1290 method_set_hostname,
1291 SD_BUS_VTABLE_UNPRIVILEGED),
1292 SD_BUS_METHOD_WITH_ARGS("SetStaticHostname",
1293 SD_BUS_ARGS("s", hostname, "b", interactive),
1294 SD_BUS_NO_RESULT,
1295 method_set_static_hostname,
1296 SD_BUS_VTABLE_UNPRIVILEGED),
1297 SD_BUS_METHOD_WITH_ARGS("SetPrettyHostname",
1298 SD_BUS_ARGS("s", hostname, "b", interactive),
1299 SD_BUS_NO_RESULT,
1300 method_set_pretty_hostname,
1301 SD_BUS_VTABLE_UNPRIVILEGED),
1302 SD_BUS_METHOD_WITH_ARGS("SetIconName",
1303 SD_BUS_ARGS("s", icon, "b", interactive),
1304 SD_BUS_NO_RESULT,
1305 method_set_icon_name,
1306 SD_BUS_VTABLE_UNPRIVILEGED),
1307 SD_BUS_METHOD_WITH_ARGS("SetChassis",
1308 SD_BUS_ARGS("s", chassis, "b", interactive),
1309 SD_BUS_NO_RESULT,
1310 method_set_chassis,
1311 SD_BUS_VTABLE_UNPRIVILEGED),
1312 SD_BUS_METHOD_WITH_ARGS("SetDeployment",
1313 SD_BUS_ARGS("s", deployment, "b", interactive),
1314 SD_BUS_NO_RESULT,
1315 method_set_deployment,
1316 SD_BUS_VTABLE_UNPRIVILEGED),
1317 SD_BUS_METHOD_WITH_ARGS("SetLocation",
1318 SD_BUS_ARGS("s", location, "b", interactive),
1319 SD_BUS_NO_RESULT,
1320 method_set_location,
1321 SD_BUS_VTABLE_UNPRIVILEGED),
1322 SD_BUS_METHOD_WITH_ARGS("GetProductUUID",
1323 SD_BUS_ARGS("b", interactive),
1324 SD_BUS_RESULT("ay", uuid),
1325 method_get_product_uuid,
1326 SD_BUS_VTABLE_UNPRIVILEGED),
1327 SD_BUS_METHOD_WITH_ARGS("GetHardwareSerial",
1328 SD_BUS_NO_ARGS,
1329 SD_BUS_RESULT("s", serial),
1330 method_get_hardware_serial,
1331 SD_BUS_VTABLE_UNPRIVILEGED),
7ecead8f
LP
1332 SD_BUS_METHOD_WITH_ARGS("Describe",
1333 SD_BUS_NO_ARGS,
1334 SD_BUS_RESULT("s", json),
1335 method_describe,
1336 SD_BUS_VTABLE_UNPRIVILEGED),
106d79be 1337
66a4c743
LP
1338 SD_BUS_VTABLE_END,
1339};
1340
670139db
ZJS
1341static const BusObjectImplementation manager_object = {
1342 "/org/freedesktop/hostname1",
1343 "org.freedesktop.hostname1",
1344 .vtables = BUS_VTABLES(hostname_vtable),
1345};
1346
2ac4d1d4 1347static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
4afd3348 1348 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
1349 int r;
1350
66a4c743
LP
1351 assert(c);
1352 assert(event);
2ac4d1d4 1353 assert(ret);
d0baa06f 1354
76b54375 1355 r = sd_bus_default_system(&bus);
23bbb0de
MS
1356 if (r < 0)
1357 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 1358
670139db 1359 r = bus_add_implementation(bus, &manager_object, c);
23bbb0de 1360 if (r < 0)
670139db 1361 return r;
d0baa06f 1362
ac9f55ed
LP
1363 r = bus_log_control_api_register(bus);
1364 if (r < 0)
1365 return r;
1366
0c0b9306 1367 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 1368 if (r < 0)
0c0b9306 1369 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1370
66a4c743 1371 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
1372 if (r < 0)
1373 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 1374
2ac4d1d4 1375 *ret = TAKE_PTR(bus);
66a4c743 1376 return 0;
d0baa06f
LP
1377}
1378
85ae63ee 1379static int run(int argc, char *argv[]) {
60e4fb42
ZJS
1380 _cleanup_(context_destroy) Context context = {
1381 .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
1382 };
4afd3348
LP
1383 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1384 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 1385 int r;
d0baa06f 1386
d2acb93d 1387 log_setup();
7640a5de 1388
fc021a5b
ZJS
1389 r = service_parse_argv("systemd-hostnamed.service",
1390 "Manage the system hostname and related metadata.",
670139db
ZJS
1391 BUS_IMPLEMENTATIONS(&manager_object,
1392 &log_control_object),
fc021a5b
ZJS
1393 argc, argv);
1394 if (r <= 0)
1395 return r;
1396
4c12626c 1397 umask(0022);
a9ba0e32
CG
1398
1399 r = mac_selinux_init();
1400 if (r < 0)
1401 return r;
4c12626c 1402
b22c8bfc
YW
1403 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1404
afc6adb5 1405 r = sd_event_default(&event);
85ae63ee
YW
1406 if (r < 0)
1407 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 1408
b22c8bfc
YW
1409 (void) sd_event_set_watchdog(event, true);
1410
1411 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
1412 if (r < 0)
1413 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
1414
1415 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
1416 if (r < 0)
1417 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1418
66a4c743 1419 r = connect_bus(&context, event, &bus);
d0baa06f 1420 if (r < 0)
85ae63ee 1421 return r;
7640a5de 1422
37224a5f 1423 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
1424 if (r < 0)
1425 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 1426
85ae63ee 1427 return 0;
7640a5de 1428}
85ae63ee
YW
1429
1430DEFINE_MAIN_FUNCTION(run);