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