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