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