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