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