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