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