]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
portable: reopen socket with O_RDONLY
[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;
ff28d259 1085 int r;
96976629
YW
1086
1087 assert(m);
1088 assert(c);
1089
96976629
YW
1090 r = bus_verify_polkit_async(
1091 m,
1092 CAP_SYS_ADMIN,
1093 "org.freedesktop.hostname1.get-hardware-serial",
1094 NULL,
ff28d259 1095 false,
96976629
YW
1096 UID_INVALID,
1097 &c->polkit_registry,
1098 error);
1099 if (r < 0)
1100 return r;
1101 if (r == 0)
1102 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1103
1104 r = get_hardware_serial(&serial);
1105 if (r < 0)
1106 return r;
1107
1108 r = sd_bus_message_new_method_return(m, &reply);
1109 if (r < 0)
1110 return r;
1111
1112 r = sd_bus_message_append(reply, "s", serial);
1113 if (r < 0)
1114 return r;
1115
1116 return sd_bus_send(NULL, reply, NULL);
1117}
1118
7ecead8f 1119static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
96976629
YW
1120 _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
1121 *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL;
7ecead8f
LP
1122 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1123 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1124 sd_id128_t product_uuid = SD_ID128_NULL;
7ecead8f
LP
1125 Context *c = userdata;
1126 bool privileged;
1127 struct utsname u;
1128 int r;
1129
1130 assert(m);
1131 assert(c);
1132
1133 r = bus_verify_polkit_async(
1134 m,
1135 CAP_SYS_ADMIN,
96976629 1136 "org.freedesktop.hostname1.get-description",
7ecead8f
LP
1137 NULL,
1138 false,
1139 UID_INVALID,
1140 &c->polkit_registry,
1141 NULL);
1142 if (r == 0)
1143 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1144
1145 /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
1146 * the product ID which we'll check explicitly. */
1147 privileged = r > 0;
1148
1149 context_read_etc_hostname(c);
1150 context_read_machine_info(c);
1151 context_read_os_release(c);
1152 context_determine_hostname_source(c);
1153
1154 r = gethostname_strict(&hn);
1155 if (r < 0) {
1156 if (r != -ENXIO)
1157 return log_error_errno(r, "Failed to read local host name: %m");
1158
1159 hn = get_default_hostname();
1160 if (!hn)
1161 return log_oom();
1162 }
1163
1164 dhn = get_default_hostname();
1165 if (!dhn)
1166 return log_oom();
1167
1168 if (isempty(c->data[PROP_ICON_NAME]))
1169 in = context_fallback_icon_name(c);
1170
8c8b1800 1171 chassis = context_get_chassis(c);
7ecead8f
LP
1172
1173 assert_se(uname(&u) >= 0);
1174
4fc7e4f3
YW
1175 if (isempty(c->data[PROP_VENDOR]))
1176 (void) get_hardware_vendor(&vendor);
1177 if (isempty(c->data[PROP_MODEL]))
1178 (void) get_hardware_model(&model);
7ecead8f 1179
96976629
YW
1180 if (privileged) {
1181 /* The product UUID and hardware serial is only available to privileged clients */
1182 (void) id128_get_product(&product_uuid);
1183 (void) get_hardware_serial(&serial);
1184 }
7ecead8f
LP
1185
1186 r = json_build(&v, JSON_BUILD_OBJECT(
1187 JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
1188 JSON_BUILD_PAIR("StaticHostname", JSON_BUILD_STRING(c->data[PROP_STATIC_HOSTNAME])),
1189 JSON_BUILD_PAIR("PrettyHostname", JSON_BUILD_STRING(c->data[PROP_PRETTY_HOSTNAME])),
1190 JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)),
1191 JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))),
1192 JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])),
8c8b1800 1193 JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis)),
7ecead8f
LP
1194 JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])),
1195 JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])),
1196 JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)),
1197 JSON_BUILD_PAIR("KernelRelease", JSON_BUILD_STRING(u.release)),
1198 JSON_BUILD_PAIR("KernelVersion", JSON_BUILD_STRING(u.version)),
1199 JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])),
1200 JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])),
1201 JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])),
4fc7e4f3
YW
1202 JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor ?: c->data[PROP_VENDOR])),
1203 JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_MODEL])),
96976629 1204 JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
7ecead8f
LP
1205 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
1206 JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
1207
1208 if (r < 0)
1209 return log_error_errno(r, "Failed to build JSON data: %m");
1210
1211 r = json_variant_format(v, 0, &text);
1212 if (r < 0)
1213 return log_error_errno(r, "Failed to format JSON data: %m");
1214
1215 r = sd_bus_message_new_method_return(m, &reply);
1216 if (r < 0)
1217 return r;
1218
1219 r = sd_bus_message_append(reply, "s", text);
1220 if (r < 0)
1221 return r;
1222
1223 return sd_bus_send(NULL, reply, NULL);
1224}
1225
66a4c743
LP
1226static const sd_bus_vtable hostname_vtable[] = {
1227 SD_BUS_VTABLE_START(0),
aa994368 1228 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
1229 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1230 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 1231 SD_BUS_PROPERTY("DefaultHostname", "s", property_get_default_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
60e4fb42 1232 SD_BUS_PROPERTY("HostnameSource", "s", property_get_hostname_source, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
66a4c743
LP
1233 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1234 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
1235 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1236 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
1237 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
1238 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
1239 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
1240 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
1241 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
1242 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
1243 SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
1244 SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
106d79be
ZJS
1245
1246 SD_BUS_METHOD_WITH_NAMES("SetHostname",
1247 "sb",
1248 SD_BUS_PARAM(hostname)
1249 SD_BUS_PARAM(interactive),
1250 NULL,,
1251 method_set_hostname,
1252 SD_BUS_VTABLE_UNPRIVILEGED),
1253 SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
1254 "sb",
1255 SD_BUS_PARAM(hostname)
1256 SD_BUS_PARAM(interactive),
1257 NULL,,
1258 method_set_static_hostname,
1259 SD_BUS_VTABLE_UNPRIVILEGED),
1260 SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
1261 "sb",
1262 SD_BUS_PARAM(hostname)
1263 SD_BUS_PARAM(interactive),
1264 NULL,,
1265 method_set_pretty_hostname,
1266 SD_BUS_VTABLE_UNPRIVILEGED),
1267 SD_BUS_METHOD_WITH_NAMES("SetIconName",
1268 "sb",
1269 SD_BUS_PARAM(icon)
1270 SD_BUS_PARAM(interactive),
1271 NULL,,
1272 method_set_icon_name,
1273 SD_BUS_VTABLE_UNPRIVILEGED),
1274 SD_BUS_METHOD_WITH_NAMES("SetChassis",
1275 "sb",
1276 SD_BUS_PARAM(chassis)
1277 SD_BUS_PARAM(interactive),
1278 NULL,,
1279 method_set_chassis,
1280 SD_BUS_VTABLE_UNPRIVILEGED),
1281 SD_BUS_METHOD_WITH_NAMES("SetDeployment",
1282 "sb",
1283 SD_BUS_PARAM(deployment)
1284 SD_BUS_PARAM(interactive),
1285 NULL,,
1286 method_set_deployment,
1287 SD_BUS_VTABLE_UNPRIVILEGED),
1288 SD_BUS_METHOD_WITH_NAMES("SetLocation",
1289 "sb",
1290 SD_BUS_PARAM(location)
1291 SD_BUS_PARAM(interactive),
1292 NULL,,
1293 method_set_location,
1294 SD_BUS_VTABLE_UNPRIVILEGED),
1295 SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
1296 "b",
1297 SD_BUS_PARAM(interactive),
1298 "ay",
1299 SD_BUS_PARAM(uuid),
1300 method_get_product_uuid,
1301 SD_BUS_VTABLE_UNPRIVILEGED),
96976629 1302 SD_BUS_METHOD_WITH_NAMES("GetHardwareSerial",
ff28d259 1303 NULL,,
96976629
YW
1304 "s",
1305 SD_BUS_PARAM(serial),
1306 method_get_hardware_serial,
1307 SD_BUS_VTABLE_UNPRIVILEGED),
7ecead8f
LP
1308 SD_BUS_METHOD_WITH_ARGS("Describe",
1309 SD_BUS_NO_ARGS,
1310 SD_BUS_RESULT("s", json),
1311 method_describe,
1312 SD_BUS_VTABLE_UNPRIVILEGED),
106d79be 1313
66a4c743
LP
1314 SD_BUS_VTABLE_END,
1315};
1316
670139db
ZJS
1317static const BusObjectImplementation manager_object = {
1318 "/org/freedesktop/hostname1",
1319 "org.freedesktop.hostname1",
1320 .vtables = BUS_VTABLES(hostname_vtable),
1321};
1322
2ac4d1d4 1323static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
4afd3348 1324 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
1325 int r;
1326
66a4c743
LP
1327 assert(c);
1328 assert(event);
2ac4d1d4 1329 assert(ret);
d0baa06f 1330
76b54375 1331 r = sd_bus_default_system(&bus);
23bbb0de
MS
1332 if (r < 0)
1333 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 1334
670139db 1335 r = bus_add_implementation(bus, &manager_object, c);
23bbb0de 1336 if (r < 0)
670139db 1337 return r;
d0baa06f 1338
ac9f55ed
LP
1339 r = bus_log_control_api_register(bus);
1340 if (r < 0)
1341 return r;
1342
0c0b9306 1343 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 1344 if (r < 0)
0c0b9306 1345 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1346
66a4c743 1347 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
1348 if (r < 0)
1349 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 1350
2ac4d1d4 1351 *ret = TAKE_PTR(bus);
66a4c743 1352 return 0;
d0baa06f
LP
1353}
1354
85ae63ee 1355static int run(int argc, char *argv[]) {
60e4fb42
ZJS
1356 _cleanup_(context_destroy) Context context = {
1357 .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
1358 };
4afd3348
LP
1359 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1360 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 1361 int r;
d0baa06f 1362
d2acb93d 1363 log_setup();
7640a5de 1364
fc021a5b
ZJS
1365 r = service_parse_argv("systemd-hostnamed.service",
1366 "Manage the system hostname and related metadata.",
670139db
ZJS
1367 BUS_IMPLEMENTATIONS(&manager_object,
1368 &log_control_object),
fc021a5b
ZJS
1369 argc, argv);
1370 if (r <= 0)
1371 return r;
1372
4c12626c 1373 umask(0022);
a9ba0e32
CG
1374
1375 r = mac_selinux_init();
1376 if (r < 0)
1377 return r;
4c12626c 1378
b22c8bfc
YW
1379 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1380
afc6adb5 1381 r = sd_event_default(&event);
85ae63ee
YW
1382 if (r < 0)
1383 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 1384
b22c8bfc
YW
1385 (void) sd_event_set_watchdog(event, true);
1386
1387 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
1388 if (r < 0)
1389 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
1390
1391 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
1392 if (r < 0)
1393 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1394
66a4c743 1395 r = connect_bus(&context, event, &bus);
d0baa06f 1396 if (r < 0)
85ae63ee 1397 return r;
7640a5de 1398
37224a5f 1399 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
1400 if (r < 0)
1401 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 1402
85ae63ee 1403 return 0;
7640a5de 1404}
85ae63ee
YW
1405
1406DEFINE_MAIN_FUNCTION(run);