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