]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
basic: nulstr-util: add nulstr_get() returning the matching string
[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 206 if (r < 0) {
105a4245 207 log_debug_errno(r, "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 213 if (r < 0) {
105a4245 214 log_debug_errno(r, "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 262 if (r < 0) {
105a4245 263 log_debug_errno(r, "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 269 if (r < 0) {
105a4245 270 log_debug_errno(r, "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 577static void context_determine_hostname_source(Context *c) {
0995accd 578 _cleanup_free_ char *hostname = NULL;
f2a434a5
LP
579 int r;
580
581 assert(c);
582
583 if (c->hostname_source >= 0)
584 return;
585
0995accd 586 (void) gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hostname);
f2a434a5
LP
587
588 if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME]))
589 c->hostname_source = HOSTNAME_STATIC;
590 else {
0995accd
YW
591 _cleanup_free_ char *fallback = NULL;
592
f2a434a5
LP
593 /* If the hostname was not set by us, try to figure out where it came from. If we set it to
594 * the default hostname, the file will tell us. We compare the string because it is possible
595 * that the hostname was set by an older version that had a different fallback, in the
596 * initramfs or before we reexecuted. */
597
598 r = read_one_line_file("/run/systemd/default-hostname", &fallback);
599 if (r < 0 && r != -ENOENT)
600 log_warning_errno(r, "Failed to read /run/systemd/default-hostname, ignoring: %m");
601
602 if (streq_ptr(fallback, hostname))
603 c->hostname_source = HOSTNAME_DEFAULT;
604 else
605 c->hostname_source = HOSTNAME_TRANSIENT;
606 }
607}
608
60e4fb42
ZJS
609static int property_get_hostname_source(
610 sd_bus *bus,
611 const char *path,
612 const char *interface,
613 const char *property,
614 sd_bus_message *reply,
615 void *userdata,
616 sd_bus_error *error) {
617
618 Context *c = userdata;
60e4fb42
ZJS
619 assert(c);
620
621 context_read_etc_hostname(c);
f2a434a5 622 context_determine_hostname_source(c);
60e4fb42
ZJS
623
624 return sd_bus_message_append(reply, "s", hostname_source_to_string(c->hostname_source));
625}
626
d7f4ad20
LP
627static int property_get_machine_info_field(
628 sd_bus *bus,
629 const char *path,
630 const char *interface,
631 const char *property,
632 sd_bus_message *reply,
633 void *userdata,
634 sd_bus_error *error) {
635
636 sd_bus_slot *slot;
637 Context *c;
638
639 /* Acquire the context object without this property's userdata offset added. Explanation: we want
640 * access to two pointers here: a) the main context object we cache all properties in, and b) the
641 * pointer to the property field inside the context object that we are supposed to update and
642 * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us
643 * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we
644 * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from
645 * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot
646 * object is (which encapsulates the vtable registration), and then query the 'userdata' field
647 * directly off it. */
648 assert_se(slot = sd_bus_get_current_slot(bus));
649 assert_se(c = sd_bus_slot_get_userdata(slot));
650
651 context_read_machine_info(c);
652
653 return sd_bus_message_append(reply, "s", *(char**) userdata);
654}
655
656static int property_get_os_release_field(
657 sd_bus *bus,
658 const char *path,
659 const char *interface,
660 const char *property,
661 sd_bus_message *reply,
662 void *userdata,
663 sd_bus_error *error) {
664
665 sd_bus_slot *slot;
666 Context *c;
667
668 /* As above, acquire the current context without this property's userdata offset added. */
669 assert_se(slot = sd_bus_get_current_slot(bus));
670 assert_se(c = sd_bus_slot_get_userdata(slot));
671
672 context_read_os_release(c);
673
674 return sd_bus_message_append(reply, "s", *(char**) userdata);
675}
676
66a4c743
LP
677static int property_get_icon_name(
678 sd_bus *bus,
679 const char *path,
680 const char *interface,
681 const char *property,
682 sd_bus_message *reply,
ebcf1f97
LP
683 void *userdata,
684 sd_bus_error *error) {
7640a5de 685
66a4c743
LP
686 _cleanup_free_ char *n = NULL;
687 Context *c = userdata;
688 const char *name;
7640a5de 689
d7f4ad20
LP
690 context_read_machine_info(c);
691
66a4c743
LP
692 if (isempty(c->data[PROP_ICON_NAME]))
693 name = n = context_fallback_icon_name(c);
7640a5de 694 else
66a4c743
LP
695 name = c->data[PROP_ICON_NAME];
696
697 if (!name)
698 return -ENOMEM;
7640a5de 699
ebcf1f97 700 return sd_bus_message_append(reply, "s", name);
7640a5de
LP
701}
702
66a4c743
LP
703static int property_get_chassis(
704 sd_bus *bus,
705 const char *path,
706 const char *interface,
707 const char *property,
708 sd_bus_message *reply,
ebcf1f97
LP
709 void *userdata,
710 sd_bus_error *error) {
7871c8e9 711
66a4c743
LP
712 Context *c = userdata;
713 const char *name;
7871c8e9 714
d7f4ad20
LP
715 context_read_machine_info(c);
716
66a4c743 717 if (isempty(c->data[PROP_CHASSIS]))
7871c8e9
LP
718 name = fallback_chassis();
719 else
66a4c743 720 name = c->data[PROP_CHASSIS];
7871c8e9 721
ebcf1f97 722 return sd_bus_message_append(reply, "s", name);
7871c8e9
LP
723}
724
72f48cd3
LP
725static int property_get_uname_field(
726 sd_bus *bus,
727 const char *path,
728 const char *interface,
729 const char *property,
730 sd_bus_message *reply,
731 void *userdata,
732 sd_bus_error *error) {
733
734 struct utsname u;
735
736 assert_se(uname(&u) >= 0);
737
738 return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
739}
740
19070062 741static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
742 Context *c = userdata;
743 const char *name;
aa994368 744 int interactive, r;
d200735e 745
19070062
LP
746 assert(m);
747 assert(c);
748
66a4c743
LP
749 r = sd_bus_message_read(m, "sb", &name, &interactive);
750 if (r < 0)
ebcf1f97 751 return r;
d200735e 752
60e4fb42 753 name = empty_to_null(name);
7640a5de 754
60e4fb42
ZJS
755 /* We always go through with the procedure below without comparing to the current hostname, because
756 * we might want to adjust hostname source information even if the actual hostname is unchanged. */
7640a5de 757
f190ac48 758 if (name && !hostname_is_valid(name, 0))
ebcf1f97 759 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
7640a5de 760
60e4fb42 761 context_read_etc_hostname(c);
7640a5de 762
c529695e
LP
763 r = bus_verify_polkit_async(
764 m,
765 CAP_SYS_ADMIN,
766 "org.freedesktop.hostname1.set-hostname",
403ed0e5 767 NULL,
c529695e
LP
768 interactive,
769 UID_INVALID,
770 &c->polkit_registry,
771 error);
66a4c743 772 if (r < 0)
ebcf1f97 773 return r;
66a4c743
LP
774 if (r == 0)
775 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 776
aa994368 777 r = context_update_kernel_hostname(c, name);
60e4fb42 778 if (r < 0)
1b4cd646 779 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
60e4fb42 780 else if (r > 0)
efda832d
ZJS
781 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
782 "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
783 "Hostname", "HostnameSource", NULL);
7640a5de 784
df2d202e 785 return sd_bus_reply_method_return(m, NULL);
66a4c743 786}
7640a5de 787
19070062 788static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
789 Context *c = userdata;
790 const char *name;
102d8f81 791 int interactive;
66a4c743 792 int r;
7640a5de 793
19070062
LP
794 assert(m);
795 assert(c);
796
66a4c743
LP
797 r = sd_bus_message_read(m, "sb", &name, &interactive);
798 if (r < 0)
ebcf1f97 799 return r;
7640a5de 800
3c6f7c34 801 name = empty_to_null(name);
7640a5de 802
d7f4ad20
LP
803 context_read_etc_hostname(c);
804
66a4c743 805 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 806 return sd_bus_reply_method_return(m, NULL);
7640a5de 807
60e4fb42 808 if (name && !hostname_is_valid(name, 0))
c650f207
YW
809 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
810
c529695e
LP
811 r = bus_verify_polkit_async(
812 m,
813 CAP_SYS_ADMIN,
814 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 815 NULL,
c529695e
LP
816 interactive,
817 UID_INVALID,
818 &c->polkit_registry,
819 error);
66a4c743 820 if (r < 0)
ebcf1f97 821 return r;
66a4c743
LP
822 if (r == 0)
823 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 824
e8acf091 825 r = free_and_strdup_warn(&c->data[PROP_STATIC_HOSTNAME], name);
c650f207
YW
826 if (r < 0)
827 return r;
7640a5de 828
66a4c743
LP
829 r = context_write_data_static_hostname(c);
830 if (r < 0) {
38b38500 831 log_error_errno(r, "Failed to write static hostname: %m");
957991b7
YW
832 if (ERRNO_IS_PRIVILEGE(r))
833 return sd_bus_error_set(error, BUS_ERROR_FILE_IS_PROTECTED, "Not allowed to update /etc/hostname.");
834 if (r == -EROFS)
835 return sd_bus_error_set(error, BUS_ERROR_READ_ONLY_FILESYSTEM, "/etc/hostname is in a read-only filesystem.");
1b4cd646 836 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 837 }
7640a5de 838
60e4fb42
ZJS
839 r = context_update_kernel_hostname(c, NULL);
840 if (r < 0) {
841 log_error_errno(r, "Failed to set hostname: %m");
842 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
843 }
7640a5de 844
efda832d 845 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
60e4fb42
ZJS
846 "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
847 "StaticHostname", "Hostname", "HostnameSource", NULL);
7640a5de 848
df2d202e 849 return sd_bus_reply_method_return(m, NULL);
66a4c743 850}
7640a5de 851
19070062 852static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 853 int interactive;
66a4c743
LP
854 const char *name;
855 int r;
856
857 assert(c);
66a4c743
LP
858 assert(m);
859
860 r = sd_bus_message_read(m, "sb", &name, &interactive);
861 if (r < 0)
ebcf1f97 862 return r;
66a4c743 863
3c6f7c34 864 name = empty_to_null(name);
66a4c743 865
d7f4ad20
LP
866 context_read_machine_info(c);
867
66a4c743 868 if (streq_ptr(name, c->data[prop]))
df2d202e 869 return sd_bus_reply_method_return(m, NULL);
7640a5de 870
c650f207
YW
871 if (!isempty(name)) {
872 /* The icon name might ultimately be used as file
873 * name, so better be safe than sorry */
874
875 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
876 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
877 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
38b38500 878 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
c650f207
YW
879 if (prop == PROP_CHASSIS && !valid_chassis(name))
880 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
881 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
882 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
883 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
884 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
885 }
886
66a4c743
LP
887 /* Since the pretty hostname should always be changed at the
888 * same time as the static one, use the same policy action for
889 * both... */
890
c529695e
LP
891 r = bus_verify_polkit_async(
892 m,
893 CAP_SYS_ADMIN,
894 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 895 NULL,
c529695e
LP
896 interactive,
897 UID_INVALID,
898 &c->polkit_registry,
899 error);
66a4c743 900 if (r < 0)
ebcf1f97 901 return r;
66a4c743
LP
902 if (r == 0)
903 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
904
e8acf091 905 r = free_and_strdup_warn(&c->data[prop], name);
c650f207
YW
906 if (r < 0)
907 return r;
7640a5de 908
f200e8bb 909 r = context_write_data_machine_info(c);
66a4c743 910 if (r < 0) {
da927ba9 911 log_error_errno(r, "Failed to write machine info: %m");
957991b7
YW
912 if (ERRNO_IS_PRIVILEGE(r))
913 return sd_bus_error_set(error, BUS_ERROR_FILE_IS_PROTECTED, "Not allowed to update /etc/machine-info.");
914 if (r == -EROFS)
915 return sd_bus_error_set(error, BUS_ERROR_READ_ONLY_FILESYSTEM, "/etc/machine-info is in a read-only filesystem.");
1b4cd646 916 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 917 }
7640a5de 918
66a4c743 919 log_info("Changed %s to '%s'",
38b38500 920 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
799298d6 921 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 922 prop == PROP_LOCATION ? "location" :
66a4c743 923 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 924
19070062
LP
925 (void) sd_bus_emit_properties_changed(
926 sd_bus_message_get_bus(m),
927 "/org/freedesktop/hostname1",
928 "org.freedesktop.hostname1",
929 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
930 prop == PROP_DEPLOYMENT ? "Deployment" :
931 prop == PROP_LOCATION ? "Location" :
932 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 933
df2d202e 934 return sd_bus_reply_method_return(m, NULL);
66a4c743 935}
7640a5de 936
19070062
LP
937static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
938 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
939}
940
19070062
LP
941static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
942 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
943}
944
19070062
LP
945static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
946 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
947}
948
19070062
LP
949static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
950 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
951}
952
19070062
LP
953static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
954 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
955}
956
21e627da
YW
957static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
958 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
959 Context *c = userdata;
960 int interactive, r;
5704cd73 961 sd_id128_t uuid;
21e627da
YW
962
963 assert(m);
964 assert(c);
965
21e627da
YW
966 r = sd_bus_message_read(m, "b", &interactive);
967 if (r < 0)
968 return r;
969
970 r = bus_verify_polkit_async(
971 m,
972 CAP_SYS_ADMIN,
973 "org.freedesktop.hostname1.get-product-uuid",
974 NULL,
975 interactive,
976 UID_INVALID,
977 &c->polkit_registry,
978 error);
979 if (r < 0)
980 return r;
981 if (r == 0)
982 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
983
66ee2298
LP
984 r = id128_get_product(&uuid);
985 if (r < 0) {
986 if (r == -EADDRNOTAVAIL)
987 log_debug_errno(r, "DMI product UUID is all 0x00 or all 0xFF, ignoring.");
988 else
989 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
990 "Failed to read product UUID, ignoring: %m");
991
992 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
993 "Failed to read product UUID from firmware.");
994 }
995
21e627da
YW
996 r = sd_bus_message_new_method_return(m, &reply);
997 if (r < 0)
998 return r;
999
c52e295d 1000 r = sd_bus_message_append_array(reply, 'y', uuid.bytes, sizeof(uuid.bytes));
21e627da
YW
1001 if (r < 0)
1002 return r;
1003
1004 return sd_bus_send(NULL, reply, NULL);
1005}
1006
7ecead8f
LP
1007static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
1008 _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL, *vendor = NULL, *model = NULL;
1009 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1010 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1011 sd_id128_t product_uuid = SD_ID128_NULL;
1012 const char *chassis = NULL;
1013 Context *c = userdata;
1014 bool privileged;
1015 struct utsname u;
1016 int r;
1017
1018 assert(m);
1019 assert(c);
1020
1021 r = bus_verify_polkit_async(
1022 m,
1023 CAP_SYS_ADMIN,
1024 "org.freedesktop.hostname1.get-product-uuid",
1025 NULL,
1026 false,
1027 UID_INVALID,
1028 &c->polkit_registry,
1029 NULL);
1030 if (r == 0)
1031 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1032
1033 /* We ignore all authentication errors here, since most data is unprivileged, the one exception being
1034 * the product ID which we'll check explicitly. */
1035 privileged = r > 0;
1036
1037 context_read_etc_hostname(c);
1038 context_read_machine_info(c);
1039 context_read_os_release(c);
1040 context_determine_hostname_source(c);
1041
1042 r = gethostname_strict(&hn);
1043 if (r < 0) {
1044 if (r != -ENXIO)
1045 return log_error_errno(r, "Failed to read local host name: %m");
1046
1047 hn = get_default_hostname();
1048 if (!hn)
1049 return log_oom();
1050 }
1051
1052 dhn = get_default_hostname();
1053 if (!dhn)
1054 return log_oom();
1055
1056 if (isempty(c->data[PROP_ICON_NAME]))
1057 in = context_fallback_icon_name(c);
1058
1059 if (isempty(c->data[PROP_CHASSIS]))
1060 chassis = fallback_chassis();
1061
1062 assert_se(uname(&u) >= 0);
1063
1064 (void) get_hardware_vendor(&vendor);
1065 (void) get_hardware_model(&model);
1066
1067 if (privileged) /* The product UUID is only available to privileged clients */
1068 id128_get_product(&product_uuid);
1069
1070 r = json_build(&v, JSON_BUILD_OBJECT(
1071 JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
1072 JSON_BUILD_PAIR("StaticHostname", JSON_BUILD_STRING(c->data[PROP_STATIC_HOSTNAME])),
1073 JSON_BUILD_PAIR("PrettyHostname", JSON_BUILD_STRING(c->data[PROP_PRETTY_HOSTNAME])),
1074 JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)),
1075 JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))),
1076 JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])),
1077 JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis ?: c->data[PROP_CHASSIS])),
1078 JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])),
1079 JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])),
1080 JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)),
1081 JSON_BUILD_PAIR("KernelRelease", JSON_BUILD_STRING(u.release)),
1082 JSON_BUILD_PAIR("KernelVersion", JSON_BUILD_STRING(u.version)),
1083 JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])),
1084 JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])),
1085 JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])),
1086 JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor)),
1087 JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model)),
1088 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
1089 JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
1090
1091 if (r < 0)
1092 return log_error_errno(r, "Failed to build JSON data: %m");
1093
1094 r = json_variant_format(v, 0, &text);
1095 if (r < 0)
1096 return log_error_errno(r, "Failed to format JSON data: %m");
1097
1098 r = sd_bus_message_new_method_return(m, &reply);
1099 if (r < 0)
1100 return r;
1101
1102 r = sd_bus_message_append(reply, "s", text);
1103 if (r < 0)
1104 return r;
1105
1106 return sd_bus_send(NULL, reply, NULL);
1107}
1108
66a4c743
LP
1109static const sd_bus_vtable hostname_vtable[] = {
1110 SD_BUS_VTABLE_START(0),
aa994368 1111 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
1112 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1113 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 1114 SD_BUS_PROPERTY("DefaultHostname", "s", property_get_default_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
60e4fb42 1115 SD_BUS_PROPERTY("HostnameSource", "s", property_get_hostname_source, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
66a4c743
LP
1116 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1117 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
1118 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1119 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
1120 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
1121 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
1122 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
1123 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
1124 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
1125 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
1126 SD_BUS_PROPERTY("HardwareVendor", "s", property_get_hardware_vendor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
1127 SD_BUS_PROPERTY("HardwareModel", "s", property_get_hardware_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
106d79be
ZJS
1128
1129 SD_BUS_METHOD_WITH_NAMES("SetHostname",
1130 "sb",
1131 SD_BUS_PARAM(hostname)
1132 SD_BUS_PARAM(interactive),
1133 NULL,,
1134 method_set_hostname,
1135 SD_BUS_VTABLE_UNPRIVILEGED),
1136 SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
1137 "sb",
1138 SD_BUS_PARAM(hostname)
1139 SD_BUS_PARAM(interactive),
1140 NULL,,
1141 method_set_static_hostname,
1142 SD_BUS_VTABLE_UNPRIVILEGED),
1143 SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
1144 "sb",
1145 SD_BUS_PARAM(hostname)
1146 SD_BUS_PARAM(interactive),
1147 NULL,,
1148 method_set_pretty_hostname,
1149 SD_BUS_VTABLE_UNPRIVILEGED),
1150 SD_BUS_METHOD_WITH_NAMES("SetIconName",
1151 "sb",
1152 SD_BUS_PARAM(icon)
1153 SD_BUS_PARAM(interactive),
1154 NULL,,
1155 method_set_icon_name,
1156 SD_BUS_VTABLE_UNPRIVILEGED),
1157 SD_BUS_METHOD_WITH_NAMES("SetChassis",
1158 "sb",
1159 SD_BUS_PARAM(chassis)
1160 SD_BUS_PARAM(interactive),
1161 NULL,,
1162 method_set_chassis,
1163 SD_BUS_VTABLE_UNPRIVILEGED),
1164 SD_BUS_METHOD_WITH_NAMES("SetDeployment",
1165 "sb",
1166 SD_BUS_PARAM(deployment)
1167 SD_BUS_PARAM(interactive),
1168 NULL,,
1169 method_set_deployment,
1170 SD_BUS_VTABLE_UNPRIVILEGED),
1171 SD_BUS_METHOD_WITH_NAMES("SetLocation",
1172 "sb",
1173 SD_BUS_PARAM(location)
1174 SD_BUS_PARAM(interactive),
1175 NULL,,
1176 method_set_location,
1177 SD_BUS_VTABLE_UNPRIVILEGED),
1178 SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
1179 "b",
1180 SD_BUS_PARAM(interactive),
1181 "ay",
1182 SD_BUS_PARAM(uuid),
1183 method_get_product_uuid,
1184 SD_BUS_VTABLE_UNPRIVILEGED),
7ecead8f
LP
1185 SD_BUS_METHOD_WITH_ARGS("Describe",
1186 SD_BUS_NO_ARGS,
1187 SD_BUS_RESULT("s", json),
1188 method_describe,
1189 SD_BUS_VTABLE_UNPRIVILEGED),
106d79be 1190
66a4c743
LP
1191 SD_BUS_VTABLE_END,
1192};
1193
670139db
ZJS
1194static const BusObjectImplementation manager_object = {
1195 "/org/freedesktop/hostname1",
1196 "org.freedesktop.hostname1",
1197 .vtables = BUS_VTABLES(hostname_vtable),
1198};
1199
2ac4d1d4 1200static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
4afd3348 1201 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
1202 int r;
1203
66a4c743
LP
1204 assert(c);
1205 assert(event);
2ac4d1d4 1206 assert(ret);
d0baa06f 1207
76b54375 1208 r = sd_bus_default_system(&bus);
23bbb0de
MS
1209 if (r < 0)
1210 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 1211
670139db 1212 r = bus_add_implementation(bus, &manager_object, c);
23bbb0de 1213 if (r < 0)
670139db 1214 return r;
d0baa06f 1215
ac9f55ed
LP
1216 r = bus_log_control_api_register(bus);
1217 if (r < 0)
1218 return r;
1219
0c0b9306 1220 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 1221 if (r < 0)
0c0b9306 1222 return log_error_errno(r, "Failed to request name: %m");
add10b5a 1223
66a4c743 1224 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
1225 if (r < 0)
1226 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 1227
2ac4d1d4 1228 *ret = TAKE_PTR(bus);
66a4c743 1229 return 0;
d0baa06f
LP
1230}
1231
85ae63ee 1232static int run(int argc, char *argv[]) {
60e4fb42
ZJS
1233 _cleanup_(context_destroy) Context context = {
1234 .hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
1235 };
4afd3348
LP
1236 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
1237 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 1238 int r;
d0baa06f 1239
d2acb93d 1240 log_setup();
7640a5de 1241
fc021a5b
ZJS
1242 r = service_parse_argv("systemd-hostnamed.service",
1243 "Manage the system hostname and related metadata.",
670139db
ZJS
1244 BUS_IMPLEMENTATIONS(&manager_object,
1245 &log_control_object),
fc021a5b
ZJS
1246 argc, argv);
1247 if (r <= 0)
1248 return r;
1249
4c12626c 1250 umask(0022);
a9ba0e32
CG
1251
1252 r = mac_selinux_init();
1253 if (r < 0)
1254 return r;
4c12626c 1255
b22c8bfc
YW
1256 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
1257
afc6adb5 1258 r = sd_event_default(&event);
85ae63ee
YW
1259 if (r < 0)
1260 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 1261
b22c8bfc
YW
1262 (void) sd_event_set_watchdog(event, true);
1263
1264 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
1265 if (r < 0)
1266 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
1267
1268 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
1269 if (r < 0)
1270 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1271
66a4c743 1272 r = connect_bus(&context, event, &bus);
d0baa06f 1273 if (r < 0)
85ae63ee 1274 return r;
7640a5de 1275
37224a5f 1276 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
1277 if (r < 0)
1278 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 1279
85ae63ee 1280 return 0;
7640a5de 1281}
85ae63ee
YW
1282
1283DEFINE_MAIN_FUNCTION(run);