]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
hostnamed: minor style cleanups
[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"
85ae63ee 23#include "main-func.h"
36dd5ffd 24#include "missing_capability.h"
7782e0a0 25#include "nscd-flush.h"
d8b4d14d 26#include "nulstr-util.h"
d58ad743 27#include "os-util.h"
6bedfcbb 28#include "parse-util.h"
bb15fafe 29#include "path-util.h"
6bedfcbb 30#include "selinux-util.h"
fc021a5b 31#include "service-util.h"
b22c8bfc 32#include "signal-util.h"
d7f4ad20 33#include "stat-util.h"
6bedfcbb 34#include "strv.h"
ee104e11 35#include "user-util.h"
6bedfcbb
LP
36#include "util.h"
37#include "virt.h"
91f9dcaf 38
799298d6
JG
39#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
40
7640a5de 41enum {
d7f4ad20 42 /* Read from /etc/hostname */
7640a5de 43 PROP_STATIC_HOSTNAME,
d7f4ad20
LP
44
45 /* Read from /etc/machine-info */
7640a5de
LP
46 PROP_PRETTY_HOSTNAME,
47 PROP_ICON_NAME,
7871c8e9 48 PROP_CHASSIS,
799298d6 49 PROP_DEPLOYMENT,
ce0f1493 50 PROP_LOCATION,
d7f4ad20
LP
51
52 /* Read from /etc/os-release (or /usr/lib/os-release) */
44c32988
DH
53 PROP_OS_PRETTY_NAME,
54 PROP_OS_CPE_NAME,
d7f4ad20
LP
55 PROP_OS_HOME_URL,
56 _PROP_MAX,
57 _PROP_INVALID = -1,
7640a5de
LP
58};
59
66a4c743
LP
60typedef struct Context {
61 char *data[_PROP_MAX];
d7f4ad20
LP
62
63 struct stat etc_hostname_stat;
64 struct stat etc_os_release_stat;
65 struct stat etc_machine_info_stat;
66
66a4c743
LP
67 Hashmap *polkit_registry;
68} Context;
ad740100 69
d7f4ad20 70static void context_reset(Context *c, uint64_t mask) {
66a4c743
LP
71 assert(c);
72
536970d4 73 for (int p = 0; p < _PROP_MAX; p++) {
d7f4ad20
LP
74 if (!FLAGS_SET(mask, UINT64_C(1) << p))
75 continue;
76
a1e58e8e 77 c->data[p] = mfree(c->data[p]);
d7f4ad20 78 }
7640a5de
LP
79}
80
cfb9433d 81static void context_destroy(Context *c) {
66a4c743
LP
82 assert(c);
83
d7f4ad20 84 context_reset(c, UINT64_MAX);
36e34057 85 bus_verify_polkit_async_registry_free(c->polkit_registry);
66a4c743
LP
86}
87
d7f4ad20
LP
88static void context_read_etc_hostname(Context *c) {
89 struct stat current_stat = {};
7640a5de
LP
90 int r;
91
66a4c743
LP
92 assert(c);
93
d7f4ad20
LP
94 if (stat("/etc/hostname", &current_stat) >= 0 &&
95 stat_inode_unmodified(&c->etc_hostname_stat, &current_stat))
96 return;
97
98 context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME);
7640a5de 99
f35cb39e 100 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
7640a5de 101 if (r < 0 && r != -ENOENT)
d7f4ad20
LP
102 log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m");
103
104 c->etc_hostname_stat = current_stat;
105}
106
107static void context_read_machine_info(Context *c) {
108 struct stat current_stat = {};
109 int r;
110
111 assert(c);
112
113 if (stat("/etc/machine-info", &current_stat) >= 0 &&
114 stat_inode_unmodified(&c->etc_machine_info_stat, &current_stat))
115 return;
116
117 context_reset(c,
118 (UINT64_C(1) << PROP_PRETTY_HOSTNAME) |
119 (UINT64_C(1) << PROP_ICON_NAME) |
120 (UINT64_C(1) << PROP_CHASSIS) |
121 (UINT64_C(1) << PROP_DEPLOYMENT) |
122 (UINT64_C(1) << PROP_LOCATION));
7640a5de 123
aa8fbc74 124 r = parse_env_file(NULL, "/etc/machine-info",
66a4c743
LP
125 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
126 "ICON_NAME", &c->data[PROP_ICON_NAME],
127 "CHASSIS", &c->data[PROP_CHASSIS],
799298d6 128 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
13df9c39 129 "LOCATION", &c->data[PROP_LOCATION]);
7640a5de 130 if (r < 0 && r != -ENOENT)
d7f4ad20
LP
131 log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
132
133 c->etc_machine_info_stat = current_stat;
134}
135
136static void context_read_os_release(Context *c) {
137 struct stat current_stat = {};
138 int r;
139
140 assert(c);
141
142 if ((stat("/etc/os-release", &current_stat) >= 0 ||
143 stat("/usr/lib/os-release", &current_stat) >= 0) &&
144 stat_inode_unmodified(&c->etc_os_release_stat, &current_stat))
145 return;
146
147 context_reset(c,
148 (UINT64_C(1) << PROP_OS_PRETTY_NAME) |
149 (UINT64_C(1) << PROP_OS_CPE_NAME) |
150 (UINT64_C(1) << PROP_OS_HOME_URL));
7640a5de 151
d58ad743
LP
152 r = parse_os_release(NULL,
153 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
154 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
d7f4ad20 155 "HOME_URL", &c->data[PROP_OS_HOME_URL],
d58ad743 156 NULL);
44c32988 157 if (r < 0 && r != -ENOENT)
d7f4ad20 158 log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
44c32988 159
d7f4ad20 160 c->etc_os_release_stat = current_stat;
7640a5de
LP
161}
162
7871c8e9 163static bool valid_chassis(const char *chassis) {
7871c8e9
LP
164 assert(chassis);
165
166 return nulstr_contains(
167 "vm\0"
168 "container\0"
169 "desktop\0"
170 "laptop\0"
34b52450 171 "convertible\0"
7871c8e9
LP
172 "server\0"
173 "tablet\0"
c49e59c1 174 "handset\0"
25fa306e
LP
175 "watch\0"
176 "embedded\0",
7871c8e9
LP
177 chassis);
178}
179
799298d6
JG
180static bool valid_deployment(const char *deployment) {
181 assert(deployment);
182
c2142cf1 183 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
799298d6
JG
184}
185
7871c8e9 186static const char* fallback_chassis(void) {
7640a5de
LP
187 char *type;
188 unsigned t;
219bfe38 189 int v, r;
7871c8e9 190
75f86906 191 v = detect_virtualization();
2ac4d1d4
LP
192 if (v < 0)
193 log_debug_errno(v, "Failed to detect virtualization, ignoring: %m");
194 else if (VIRTUALIZATION_IS_VM(v))
7871c8e9 195 return "vm";
2ac4d1d4 196 else if (VIRTUALIZATION_IS_CONTAINER(v))
7871c8e9
LP
197 return "container";
198
219bfe38 199 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
2ac4d1d4
LP
200 if (r < 0) {
201 log_debug_errno(v, "Failed to read DMI chassis type, ignoring: %m");
219bfe38 202 goto try_acpi;
2ac4d1d4 203 }
7871c8e9
LP
204
205 r = safe_atou(type, &t);
206 free(type);
2ac4d1d4
LP
207 if (r < 0) {
208 log_debug_errno(v, "Failed to parse DMI chassis type, ignoring: %m");
219bfe38 209 goto try_acpi;
2ac4d1d4 210 }
7871c8e9 211
219bfe38
LP
212 /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
213 additional guesswork on top of that.
214
215 See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here:
216
217 https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
7871c8e9
LP
218 */
219
219bfe38 220 switch (t) {
7871c8e9 221
219bfe38
LP
222 case 0x3: /* Desktop */
223 case 0x4: /* Low Profile Desktop */
224 case 0x6: /* Mini Tower */
225 case 0x7: /* Tower */
7c5c59d4 226 case 0xD: /* All in one (i.e. PC built into monitor) */
7871c8e9
LP
227 return "desktop";
228
219bfe38
LP
229 case 0x8: /* Portable */
230 case 0x9: /* Laptop */
231 case 0xA: /* Notebook */
232 case 0xE: /* Sub Notebook */
7871c8e9 233 return "laptop";
7640a5de 234
219bfe38
LP
235 case 0xB: /* Hand Held */
236 return "handset";
237
238 case 0x11: /* Main Server Chassis */
239 case 0x1C: /* Blade */
240 case 0x1D: /* Blade Enclosure */
7871c8e9 241 return "server";
7640a5de 242
219bfe38 243 case 0x1E: /* Tablet */
7871c8e9 244 return "tablet";
b70af833
DH
245
246 case 0x1F: /* Convertible */
b4227dbb 247 case 0x20: /* Detachable */
b70af833 248 return "convertible";
2ac4d1d4
LP
249
250 default:
251 log_debug("Unhandled DMI chassis type 0x%02x, ignoring.", t);
7871c8e9
LP
252 }
253
219bfe38
LP
254try_acpi:
255 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
2ac4d1d4
LP
256 if (r < 0) {
257 log_debug_errno(v, "Failed read ACPI PM profile, ignoring: %m");
7640a5de 258 return NULL;
2ac4d1d4 259 }
7640a5de
LP
260
261 r = safe_atou(type, &t);
262 free(type);
2ac4d1d4
LP
263 if (r < 0) {
264 log_debug_errno(v, "Failed parse ACPI PM profile, ignoring: %m");
7640a5de 265 return NULL;
2ac4d1d4 266 }
7640a5de 267
219bfe38
LP
268 /* We only list the really obvious cases here as the ACPI data is not really super reliable.
269 *
270 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
271 *
272 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
41550d40 273 */
7640a5de 274
219bfe38 275 switch(t) {
7640a5de 276
219bfe38
LP
277 case 1: /* Desktop */
278 case 3: /* Workstation */
279 case 6: /* Appliance PC */
7871c8e9 280 return "desktop";
7640a5de 281
219bfe38 282 case 2: /* Mobile */
7871c8e9
LP
283 return "laptop";
284
219bfe38
LP
285 case 4: /* Enterprise Server */
286 case 5: /* SOHO Server */
287 case 7: /* Performance Server */
7871c8e9 288 return "server";
f3f4f008 289
219bfe38 290 case 8: /* Tablet */
f3f4f008 291 return "tablet";
2ac4d1d4
LP
292
293 default:
294 log_debug("Unhandled ACPI PM profile 0x%02x, ignoring.", t);
7640a5de
LP
295 }
296
7640a5de
LP
297 return NULL;
298}
299
66a4c743 300static char* context_fallback_icon_name(Context *c) {
7871c8e9
LP
301 const char *chassis;
302
66a4c743
LP
303 assert(c);
304
305 if (!isempty(c->data[PROP_CHASSIS]))
b910cc72 306 return strjoin("computer-", c->data[PROP_CHASSIS]);
7871c8e9
LP
307
308 chassis = fallback_chassis();
309 if (chassis)
b910cc72 310 return strjoin("computer-", chassis);
7871c8e9
LP
311
312 return strdup("computer");
313}
314
c779a442 315static bool hostname_is_useful(const char *hn) {
fecc80c1 316 return !isempty(hn) && !is_localhost(hn);
c779a442
SW
317}
318
aa994368
LP
319static int context_update_kernel_hostname(
320 Context *c,
321 const char *transient_hn) {
322
323 const char *static_hn, *hn;
324 struct utsname u;
7d9ec609 325 int r;
7640a5de 326
66a4c743
LP
327 assert(c);
328
aa994368
LP
329 if (!transient_hn) {
330 /* If no transient hostname is passed in, then let's check what is currently set. */
331 assert_se(uname(&u) >= 0);
332 transient_hn =
333 isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
334 }
335
c779a442
SW
336 static_hn = c->data[PROP_STATIC_HOSTNAME];
337
338 /* /etc/hostname with something other than "localhost"
339 * has the highest preference ... */
340 if (hostname_is_useful(static_hn))
341 hn = static_hn;
342
38b38500 343 /* ... the transient hostname, (ie: DHCP) comes next ... */
aa994368
LP
344 else if (!isempty(transient_hn))
345 hn = transient_hn;
7640a5de 346
c779a442
SW
347 /* ... fallback to static "localhost.*" ignored above ... */
348 else if (!isempty(static_hn))
349 hn = static_hn;
350
351 /* ... and the ultimate fallback */
352 else
8146c32b 353 hn = FALLBACK_HOSTNAME;
c779a442 354
7d9ec609
ZJS
355 r = sethostname_idempotent(hn);
356 if (r < 0)
357 return r;
7640a5de 358
7782e0a0 359 (void) nscd_flush_cache(STRV_MAKE("hosts"));
efda832d 360 return r; /* 0 if no change, 1 if something was done */
7640a5de
LP
361}
362
66a4c743 363static int context_write_data_static_hostname(Context *c) {
66a4c743
LP
364 assert(c);
365
366 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
7640a5de
LP
367 if (unlink("/etc/hostname") < 0)
368 return errno == ENOENT ? 0 : -errno;
7640a5de
LP
369 return 0;
370 }
536970d4 371
66a4c743 372 return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
7640a5de
LP
373}
374
f200e8bb 375static int context_write_data_machine_info(Context *c) {
4f34ed54 376
7640a5de
LP
377 static const char * const name[_PROP_MAX] = {
378 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
7871c8e9 379 [PROP_ICON_NAME] = "ICON_NAME",
799298d6
JG
380 [PROP_CHASSIS] = "CHASSIS",
381 [PROP_DEPLOYMENT] = "DEPLOYMENT",
ce0f1493 382 [PROP_LOCATION] = "LOCATION",
7640a5de
LP
383 };
384
0ccad099 385 _cleanup_strv_free_ char **l = NULL;
536970d4 386 int r;
7640a5de 387
66a4c743
LP
388 assert(c);
389
aa8fbc74 390 r = load_env_file(NULL, "/etc/machine-info", &l);
7640a5de
LP
391 if (r < 0 && r != -ENOENT)
392 return r;
393
536970d4 394 for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
d77ab3f7
LP
395 _cleanup_free_ char *t = NULL;
396 char **u;
7640a5de
LP
397
398 assert(name[p]);
399
66a4c743 400 if (isempty(c->data[p])) {
f8440af5 401 strv_env_unset(l, name[p]);
7640a5de
LP
402 continue;
403 }
404
605405c6 405 t = strjoin(name[p], "=", c->data[p]);
d77ab3f7 406 if (!t)
7640a5de 407 return -ENOMEM;
7640a5de
LP
408
409 u = strv_env_set(l, t);
7640a5de
LP
410 if (!u)
411 return -ENOMEM;
0ccad099 412
130d3d22 413 strv_free_and_replace(l, u);
7640a5de
LP
414 }
415
416 if (strv_isempty(l)) {
7640a5de
LP
417 if (unlink("/etc/machine-info") < 0)
418 return errno == ENOENT ? 0 : -errno;
419
420 return 0;
421 }
422
0ccad099 423 return write_env_file_label("/etc/machine-info", l);
7640a5de
LP
424}
425
aa994368
LP
426static int property_get_hostname(
427 sd_bus *bus,
428 const char *path,
429 const char *interface,
430 const char *property,
431 sd_bus_message *reply,
432 void *userdata,
433 sd_bus_error *error) {
434
435 _cleanup_free_ char *current = NULL;
436 int r;
437
438 r = gethostname_strict(&current);
439 if (r == -ENXIO)
440 return sd_bus_message_append(reply, "s", FALLBACK_HOSTNAME);
441 if (r < 0)
442 return r;
443
444 return sd_bus_message_append(reply, "s", current);
445}
446
d7f4ad20
LP
447static int property_get_static_hostname(
448 sd_bus *bus,
449 const char *path,
450 const char *interface,
451 const char *property,
452 sd_bus_message *reply,
453 void *userdata,
454 sd_bus_error *error) {
455
456 Context *c = userdata;
457 assert(c);
458
459 context_read_etc_hostname(c);
460
461 return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]);
462}
463
ce6b138c
ZJS
464static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_fallback_hostname, "s", FALLBACK_HOSTNAME);
465
d7f4ad20
LP
466static int property_get_machine_info_field(
467 sd_bus *bus,
468 const char *path,
469 const char *interface,
470 const char *property,
471 sd_bus_message *reply,
472 void *userdata,
473 sd_bus_error *error) {
474
475 sd_bus_slot *slot;
476 Context *c;
477
478 /* Acquire the context object without this property's userdata offset added. Explanation: we want
479 * access to two pointers here: a) the main context object we cache all properties in, and b) the
480 * pointer to the property field inside the context object that we are supposed to update and
481 * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us
482 * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we
483 * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from
484 * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot
485 * object is (which encapsulates the vtable registration), and then query the 'userdata' field
486 * directly off it. */
487 assert_se(slot = sd_bus_get_current_slot(bus));
488 assert_se(c = sd_bus_slot_get_userdata(slot));
489
490 context_read_machine_info(c);
491
492 return sd_bus_message_append(reply, "s", *(char**) userdata);
493}
494
495static int property_get_os_release_field(
496 sd_bus *bus,
497 const char *path,
498 const char *interface,
499 const char *property,
500 sd_bus_message *reply,
501 void *userdata,
502 sd_bus_error *error) {
503
504 sd_bus_slot *slot;
505 Context *c;
506
507 /* As above, acquire the current context without this property's userdata offset added. */
508 assert_se(slot = sd_bus_get_current_slot(bus));
509 assert_se(c = sd_bus_slot_get_userdata(slot));
510
511 context_read_os_release(c);
512
513 return sd_bus_message_append(reply, "s", *(char**) userdata);
514}
515
66a4c743
LP
516static int property_get_icon_name(
517 sd_bus *bus,
518 const char *path,
519 const char *interface,
520 const char *property,
521 sd_bus_message *reply,
ebcf1f97
LP
522 void *userdata,
523 sd_bus_error *error) {
7640a5de 524
66a4c743
LP
525 _cleanup_free_ char *n = NULL;
526 Context *c = userdata;
527 const char *name;
7640a5de 528
d7f4ad20
LP
529 context_read_machine_info(c);
530
66a4c743
LP
531 if (isempty(c->data[PROP_ICON_NAME]))
532 name = n = context_fallback_icon_name(c);
7640a5de 533 else
66a4c743
LP
534 name = c->data[PROP_ICON_NAME];
535
536 if (!name)
537 return -ENOMEM;
7640a5de 538
ebcf1f97 539 return sd_bus_message_append(reply, "s", name);
7640a5de
LP
540}
541
66a4c743
LP
542static int property_get_chassis(
543 sd_bus *bus,
544 const char *path,
545 const char *interface,
546 const char *property,
547 sd_bus_message *reply,
ebcf1f97
LP
548 void *userdata,
549 sd_bus_error *error) {
7871c8e9 550
66a4c743
LP
551 Context *c = userdata;
552 const char *name;
7871c8e9 553
d7f4ad20
LP
554 context_read_machine_info(c);
555
66a4c743 556 if (isempty(c->data[PROP_CHASSIS]))
7871c8e9
LP
557 name = fallback_chassis();
558 else
66a4c743 559 name = c->data[PROP_CHASSIS];
7871c8e9 560
ebcf1f97 561 return sd_bus_message_append(reply, "s", name);
7871c8e9
LP
562}
563
72f48cd3
LP
564static int property_get_uname_field(
565 sd_bus *bus,
566 const char *path,
567 const char *interface,
568 const char *property,
569 sd_bus_message *reply,
570 void *userdata,
571 sd_bus_error *error) {
572
573 struct utsname u;
574
575 assert_se(uname(&u) >= 0);
576
577 return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
578}
579
19070062 580static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
581 Context *c = userdata;
582 const char *name;
aa994368
LP
583 int interactive, r;
584 struct utsname u;
d200735e 585
19070062
LP
586 assert(m);
587 assert(c);
588
66a4c743
LP
589 r = sd_bus_message_read(m, "sb", &name, &interactive);
590 if (r < 0)
ebcf1f97 591 return r;
d200735e 592
d7f4ad20
LP
593 context_read_etc_hostname(c);
594
66a4c743
LP
595 if (isempty(name))
596 name = c->data[PROP_STATIC_HOSTNAME];
7640a5de 597
66a4c743 598 if (isempty(name))
8146c32b 599 name = FALLBACK_HOSTNAME;
7640a5de 600
52ef5dd7 601 if (!hostname_is_valid(name, 0))
ebcf1f97 602 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
7640a5de 603
aa994368
LP
604 assert_se(uname(&u) >= 0);
605 if (streq_ptr(name, u.nodename))
df2d202e 606 return sd_bus_reply_method_return(m, NULL);
7640a5de 607
c529695e
LP
608 r = bus_verify_polkit_async(
609 m,
610 CAP_SYS_ADMIN,
611 "org.freedesktop.hostname1.set-hostname",
403ed0e5 612 NULL,
c529695e
LP
613 interactive,
614 UID_INVALID,
615 &c->polkit_registry,
616 error);
66a4c743 617 if (r < 0)
ebcf1f97 618 return r;
66a4c743
LP
619 if (r == 0)
620 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 621
aa994368 622 r = context_update_kernel_hostname(c, name);
66a4c743 623 if (r < 0) {
38b38500 624 log_error_errno(r, "Failed to set hostname: %m");
1b4cd646 625 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
efda832d
ZJS
626 } else if (r == 0)
627 log_debug("Hostname was already set to <%s>.", name);
628 else {
629 log_info("Hostname set to <%s>", name);
630
631 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
632 "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
633 "Hostname", "HostnameSource", NULL);
66a4c743 634 }
7640a5de 635
df2d202e 636 return sd_bus_reply_method_return(m, NULL);
66a4c743 637}
7640a5de 638
19070062 639static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
640 Context *c = userdata;
641 const char *name;
102d8f81 642 int interactive;
66a4c743 643 int r;
7640a5de 644
19070062
LP
645 assert(m);
646 assert(c);
647
66a4c743
LP
648 r = sd_bus_message_read(m, "sb", &name, &interactive);
649 if (r < 0)
ebcf1f97 650 return r;
7640a5de 651
3c6f7c34 652 name = empty_to_null(name);
7640a5de 653
d7f4ad20
LP
654 context_read_etc_hostname(c);
655
66a4c743 656 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 657 return sd_bus_reply_method_return(m, NULL);
7640a5de 658
52ef5dd7 659 if (!isempty(name) && !hostname_is_valid(name, 0))
c650f207
YW
660 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
661
c529695e
LP
662 r = bus_verify_polkit_async(
663 m,
664 CAP_SYS_ADMIN,
665 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 666 NULL,
c529695e
LP
667 interactive,
668 UID_INVALID,
669 &c->polkit_registry,
670 error);
66a4c743 671 if (r < 0)
ebcf1f97 672 return r;
66a4c743
LP
673 if (r == 0)
674 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 675
c650f207
YW
676 r = free_and_strdup(&c->data[PROP_STATIC_HOSTNAME], name);
677 if (r < 0)
678 return r;
7640a5de 679
aa994368 680 r = context_update_kernel_hostname(c, NULL);
c779a442 681 if (r < 0) {
38b38500 682 log_error_errno(r, "Failed to set hostname: %m");
1b4cd646 683 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
c779a442
SW
684 }
685
66a4c743
LP
686 r = context_write_data_static_hostname(c);
687 if (r < 0) {
38b38500 688 log_error_errno(r, "Failed to write static hostname: %m");
1b4cd646 689 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 690 }
7640a5de 691
38b38500 692 log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
7640a5de 693
efda832d
ZJS
694 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
695 "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
7640a5de 696
df2d202e 697 return sd_bus_reply_method_return(m, NULL);
66a4c743 698}
7640a5de 699
19070062 700static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 701 int interactive;
66a4c743
LP
702 const char *name;
703 int r;
704
705 assert(c);
66a4c743
LP
706 assert(m);
707
708 r = sd_bus_message_read(m, "sb", &name, &interactive);
709 if (r < 0)
ebcf1f97 710 return r;
66a4c743 711
3c6f7c34 712 name = empty_to_null(name);
66a4c743 713
d7f4ad20
LP
714 context_read_machine_info(c);
715
66a4c743 716 if (streq_ptr(name, c->data[prop]))
df2d202e 717 return sd_bus_reply_method_return(m, NULL);
7640a5de 718
c650f207
YW
719 if (!isempty(name)) {
720 /* The icon name might ultimately be used as file
721 * name, so better be safe than sorry */
722
723 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
724 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
725 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
38b38500 726 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
c650f207
YW
727 if (prop == PROP_CHASSIS && !valid_chassis(name))
728 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
729 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
730 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
731 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
732 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
733 }
734
66a4c743
LP
735 /* Since the pretty hostname should always be changed at the
736 * same time as the static one, use the same policy action for
737 * both... */
738
c529695e
LP
739 r = bus_verify_polkit_async(
740 m,
741 CAP_SYS_ADMIN,
742 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 743 NULL,
c529695e
LP
744 interactive,
745 UID_INVALID,
746 &c->polkit_registry,
747 error);
66a4c743 748 if (r < 0)
ebcf1f97 749 return r;
66a4c743
LP
750 if (r == 0)
751 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
752
c650f207
YW
753 r = free_and_strdup(&c->data[prop], name);
754 if (r < 0)
755 return r;
7640a5de 756
f200e8bb 757 r = context_write_data_machine_info(c);
66a4c743 758 if (r < 0) {
da927ba9 759 log_error_errno(r, "Failed to write machine info: %m");
1b4cd646 760 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 761 }
7640a5de 762
66a4c743 763 log_info("Changed %s to '%s'",
38b38500 764 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
799298d6 765 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 766 prop == PROP_LOCATION ? "location" :
66a4c743 767 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 768
19070062
LP
769 (void) sd_bus_emit_properties_changed(
770 sd_bus_message_get_bus(m),
771 "/org/freedesktop/hostname1",
772 "org.freedesktop.hostname1",
773 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
774 prop == PROP_DEPLOYMENT ? "Deployment" :
775 prop == PROP_LOCATION ? "Location" :
776 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 777
df2d202e 778 return sd_bus_reply_method_return(m, NULL);
66a4c743 779}
7640a5de 780
19070062
LP
781static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
782 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
783}
784
19070062
LP
785static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
786 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
787}
788
19070062
LP
789static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
790 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
791}
792
19070062
LP
793static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
794 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
795}
796
19070062
LP
797static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
798 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
799}
800
21e627da
YW
801static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
802 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
803 Context *c = userdata;
5704cd73 804 bool has_uuid = false;
21e627da 805 int interactive, r;
5704cd73 806 sd_id128_t uuid;
21e627da
YW
807
808 assert(m);
809 assert(c);
810
5704cd73
LP
811 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
812 if (r == -ENOENT)
813 r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
814 if (r < 0)
815 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
816 "Failed to read product UUID, ignoring: %m");
817 else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
818 log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid));
819 else
820 has_uuid = true;
821
822 if (!has_uuid)
823 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
824 "Failed to read product UUID from firmware.");
21e627da
YW
825
826 r = sd_bus_message_read(m, "b", &interactive);
827 if (r < 0)
828 return r;
829
830 r = bus_verify_polkit_async(
831 m,
832 CAP_SYS_ADMIN,
833 "org.freedesktop.hostname1.get-product-uuid",
834 NULL,
835 interactive,
836 UID_INVALID,
837 &c->polkit_registry,
838 error);
839 if (r < 0)
840 return r;
841 if (r == 0)
842 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
843
844 r = sd_bus_message_new_method_return(m, &reply);
845 if (r < 0)
846 return r;
847
5704cd73 848 r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid));
21e627da
YW
849 if (r < 0)
850 return r;
851
852 return sd_bus_send(NULL, reply, NULL);
853}
854
66a4c743
LP
855static const sd_bus_vtable hostname_vtable[] = {
856 SD_BUS_VTABLE_START(0),
aa994368 857 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
858 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
859 SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
ce6b138c 860 SD_BUS_PROPERTY("FallbackHostname", "s", property_get_fallback_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
66a4c743
LP
861 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
862 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
863 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
864 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
865 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
866 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
867 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
868 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
869 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
870 SD_BUS_PROPERTY("HomeURL", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
106d79be
ZJS
871
872 SD_BUS_METHOD_WITH_NAMES("SetHostname",
873 "sb",
874 SD_BUS_PARAM(hostname)
875 SD_BUS_PARAM(interactive),
876 NULL,,
877 method_set_hostname,
878 SD_BUS_VTABLE_UNPRIVILEGED),
879 SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
880 "sb",
881 SD_BUS_PARAM(hostname)
882 SD_BUS_PARAM(interactive),
883 NULL,,
884 method_set_static_hostname,
885 SD_BUS_VTABLE_UNPRIVILEGED),
886 SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
887 "sb",
888 SD_BUS_PARAM(hostname)
889 SD_BUS_PARAM(interactive),
890 NULL,,
891 method_set_pretty_hostname,
892 SD_BUS_VTABLE_UNPRIVILEGED),
893 SD_BUS_METHOD_WITH_NAMES("SetIconName",
894 "sb",
895 SD_BUS_PARAM(icon)
896 SD_BUS_PARAM(interactive),
897 NULL,,
898 method_set_icon_name,
899 SD_BUS_VTABLE_UNPRIVILEGED),
900 SD_BUS_METHOD_WITH_NAMES("SetChassis",
901 "sb",
902 SD_BUS_PARAM(chassis)
903 SD_BUS_PARAM(interactive),
904 NULL,,
905 method_set_chassis,
906 SD_BUS_VTABLE_UNPRIVILEGED),
907 SD_BUS_METHOD_WITH_NAMES("SetDeployment",
908 "sb",
909 SD_BUS_PARAM(deployment)
910 SD_BUS_PARAM(interactive),
911 NULL,,
912 method_set_deployment,
913 SD_BUS_VTABLE_UNPRIVILEGED),
914 SD_BUS_METHOD_WITH_NAMES("SetLocation",
915 "sb",
916 SD_BUS_PARAM(location)
917 SD_BUS_PARAM(interactive),
918 NULL,,
919 method_set_location,
920 SD_BUS_VTABLE_UNPRIVILEGED),
921 SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
922 "b",
923 SD_BUS_PARAM(interactive),
924 "ay",
925 SD_BUS_PARAM(uuid),
926 method_get_product_uuid,
927 SD_BUS_VTABLE_UNPRIVILEGED),
928
66a4c743
LP
929 SD_BUS_VTABLE_END,
930};
931
670139db
ZJS
932static const BusObjectImplementation manager_object = {
933 "/org/freedesktop/hostname1",
934 "org.freedesktop.hostname1",
935 .vtables = BUS_VTABLES(hostname_vtable),
936};
937
2ac4d1d4 938static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
4afd3348 939 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
940 int r;
941
66a4c743
LP
942 assert(c);
943 assert(event);
2ac4d1d4 944 assert(ret);
d0baa06f 945
76b54375 946 r = sd_bus_default_system(&bus);
23bbb0de
MS
947 if (r < 0)
948 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 949
670139db 950 r = bus_add_implementation(bus, &manager_object, c);
23bbb0de 951 if (r < 0)
670139db 952 return r;
d0baa06f 953
ac9f55ed
LP
954 r = bus_log_control_api_register(bus);
955 if (r < 0)
956 return r;
957
0c0b9306 958 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 959 if (r < 0)
0c0b9306 960 return log_error_errno(r, "Failed to request name: %m");
add10b5a 961
66a4c743 962 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
963 if (r < 0)
964 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 965
2ac4d1d4 966 *ret = TAKE_PTR(bus);
66a4c743 967 return 0;
d0baa06f
LP
968}
969
85ae63ee 970static int run(int argc, char *argv[]) {
cfb9433d 971 _cleanup_(context_destroy) Context context = {};
4afd3348
LP
972 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
973 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 974 int r;
d0baa06f 975
6bf3c61c 976 log_setup_service();
7640a5de 977
fc021a5b
ZJS
978 r = service_parse_argv("systemd-hostnamed.service",
979 "Manage the system hostname and related metadata.",
670139db
ZJS
980 BUS_IMPLEMENTATIONS(&manager_object,
981 &log_control_object),
fc021a5b
ZJS
982 argc, argv);
983 if (r <= 0)
984 return r;
985
4c12626c 986 umask(0022);
a9ba0e32
CG
987
988 r = mac_selinux_init();
989 if (r < 0)
990 return r;
4c12626c 991
b22c8bfc
YW
992 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
993
afc6adb5 994 r = sd_event_default(&event);
85ae63ee
YW
995 if (r < 0)
996 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 997
b22c8bfc
YW
998 (void) sd_event_set_watchdog(event, true);
999
1000 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
1001 if (r < 0)
1002 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
1003
1004 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
1005 if (r < 0)
1006 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1007
66a4c743 1008 r = connect_bus(&context, event, &bus);
d0baa06f 1009 if (r < 0)
85ae63ee 1010 return r;
7640a5de 1011
37224a5f 1012 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
1013 if (r < 0)
1014 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 1015
85ae63ee 1016 return 0;
7640a5de 1017}
85ae63ee
YW
1018
1019DEFINE_MAIN_FUNCTION(run);