]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
rules.d: Add rule for the /sys/class/dmi/id device
[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"
ac9f55ed 11#include "bus-log-control-api.h"
269e4d2d 12#include "bus-polkit.h"
ad740100 13#include "def.h"
686d13b9
LP
14#include "env-file-label.h"
15#include "env-file.h"
4d1a6904 16#include "env-util.h"
6bedfcbb 17#include "fileio-label.h"
ee228be1 18#include "fileio.h"
958b66ea 19#include "hostname-util.h"
21e627da 20#include "id128-util.h"
85ae63ee 21#include "main-func.h"
36dd5ffd 22#include "missing_capability.h"
7782e0a0 23#include "nscd-flush.h"
d8b4d14d 24#include "nulstr-util.h"
d58ad743 25#include "os-util.h"
6bedfcbb 26#include "parse-util.h"
bb15fafe 27#include "path-util.h"
6bedfcbb 28#include "selinux-util.h"
fc021a5b 29#include "service-util.h"
b22c8bfc 30#include "signal-util.h"
d7f4ad20 31#include "stat-util.h"
6bedfcbb 32#include "strv.h"
ee104e11 33#include "user-util.h"
6bedfcbb
LP
34#include "util.h"
35#include "virt.h"
91f9dcaf 36
799298d6
JG
37#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
38
7640a5de 39enum {
d7f4ad20 40 /* Read from /etc/hostname */
7640a5de 41 PROP_STATIC_HOSTNAME,
d7f4ad20
LP
42
43 /* Read from /etc/machine-info */
7640a5de
LP
44 PROP_PRETTY_HOSTNAME,
45 PROP_ICON_NAME,
7871c8e9 46 PROP_CHASSIS,
799298d6 47 PROP_DEPLOYMENT,
ce0f1493 48 PROP_LOCATION,
d7f4ad20
LP
49
50 /* Read from /etc/os-release (or /usr/lib/os-release) */
44c32988
DH
51 PROP_OS_PRETTY_NAME,
52 PROP_OS_CPE_NAME,
d7f4ad20
LP
53 PROP_OS_HOME_URL,
54 _PROP_MAX,
55 _PROP_INVALID = -1,
7640a5de
LP
56};
57
66a4c743
LP
58typedef struct Context {
59 char *data[_PROP_MAX];
d7f4ad20
LP
60
61 struct stat etc_hostname_stat;
62 struct stat etc_os_release_stat;
63 struct stat etc_machine_info_stat;
64
66a4c743
LP
65 Hashmap *polkit_registry;
66} Context;
ad740100 67
d7f4ad20 68static void context_reset(Context *c, uint64_t mask) {
7640a5de
LP
69 int p;
70
66a4c743
LP
71 assert(c);
72
d7f4ad20
LP
73 for (p = 0; p < _PROP_MAX; p++) {
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;
7640a5de 325
66a4c743
LP
326 assert(c);
327
aa994368
LP
328 if (!transient_hn) {
329 /* If no transient hostname is passed in, then let's check what is currently set. */
330 assert_se(uname(&u) >= 0);
331 transient_hn =
332 isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
333 }
334
c779a442
SW
335 static_hn = c->data[PROP_STATIC_HOSTNAME];
336
337 /* /etc/hostname with something other than "localhost"
338 * has the highest preference ... */
339 if (hostname_is_useful(static_hn))
340 hn = static_hn;
341
38b38500 342 /* ... the transient hostname, (ie: DHCP) comes next ... */
aa994368
LP
343 else if (!isempty(transient_hn))
344 hn = transient_hn;
7640a5de 345
c779a442
SW
346 /* ... fallback to static "localhost.*" ignored above ... */
347 else if (!isempty(static_hn))
348 hn = static_hn;
349
350 /* ... and the ultimate fallback */
351 else
8146c32b 352 hn = FALLBACK_HOSTNAME;
c779a442 353
605f81a8 354 if (sethostname_idempotent(hn) < 0)
7640a5de
LP
355 return -errno;
356
7782e0a0
LP
357 (void) nscd_flush_cache(STRV_MAKE("hosts"));
358
7640a5de
LP
359 return 0;
360}
361
66a4c743 362static int context_write_data_static_hostname(Context *c) {
66a4c743
LP
363 assert(c);
364
365 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
7640a5de
LP
366
367 if (unlink("/etc/hostname") < 0)
368 return errno == ENOENT ? 0 : -errno;
369
370 return 0;
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;
7640a5de
LP
386 int r, p;
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
ce0f1493 394 for (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
464static int property_get_machine_info_field(
465 sd_bus *bus,
466 const char *path,
467 const char *interface,
468 const char *property,
469 sd_bus_message *reply,
470 void *userdata,
471 sd_bus_error *error) {
472
473 sd_bus_slot *slot;
474 Context *c;
475
476 /* Acquire the context object without this property's userdata offset added. Explanation: we want
477 * access to two pointers here: a) the main context object we cache all properties in, and b) the
478 * pointer to the property field inside the context object that we are supposed to update and
479 * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us
480 * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we
481 * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from
482 * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot
483 * object is (which encapsulates the vtable registration), and then query the 'userdata' field
484 * directly off it. */
485 assert_se(slot = sd_bus_get_current_slot(bus));
486 assert_se(c = sd_bus_slot_get_userdata(slot));
487
488 context_read_machine_info(c);
489
490 return sd_bus_message_append(reply, "s", *(char**) userdata);
491}
492
493static int property_get_os_release_field(
494 sd_bus *bus,
495 const char *path,
496 const char *interface,
497 const char *property,
498 sd_bus_message *reply,
499 void *userdata,
500 sd_bus_error *error) {
501
502 sd_bus_slot *slot;
503 Context *c;
504
505 /* As above, acquire the current context without this property's userdata offset added. */
506 assert_se(slot = sd_bus_get_current_slot(bus));
507 assert_se(c = sd_bus_slot_get_userdata(slot));
508
509 context_read_os_release(c);
510
511 return sd_bus_message_append(reply, "s", *(char**) userdata);
512}
513
66a4c743
LP
514static int property_get_icon_name(
515 sd_bus *bus,
516 const char *path,
517 const char *interface,
518 const char *property,
519 sd_bus_message *reply,
ebcf1f97
LP
520 void *userdata,
521 sd_bus_error *error) {
7640a5de 522
66a4c743
LP
523 _cleanup_free_ char *n = NULL;
524 Context *c = userdata;
525 const char *name;
7640a5de 526
d7f4ad20
LP
527 context_read_machine_info(c);
528
66a4c743
LP
529 if (isempty(c->data[PROP_ICON_NAME]))
530 name = n = context_fallback_icon_name(c);
7640a5de 531 else
66a4c743
LP
532 name = c->data[PROP_ICON_NAME];
533
534 if (!name)
535 return -ENOMEM;
7640a5de 536
ebcf1f97 537 return sd_bus_message_append(reply, "s", name);
7640a5de
LP
538}
539
66a4c743
LP
540static int property_get_chassis(
541 sd_bus *bus,
542 const char *path,
543 const char *interface,
544 const char *property,
545 sd_bus_message *reply,
ebcf1f97
LP
546 void *userdata,
547 sd_bus_error *error) {
7871c8e9 548
66a4c743
LP
549 Context *c = userdata;
550 const char *name;
7871c8e9 551
d7f4ad20
LP
552 context_read_machine_info(c);
553
66a4c743 554 if (isempty(c->data[PROP_CHASSIS]))
7871c8e9
LP
555 name = fallback_chassis();
556 else
66a4c743 557 name = c->data[PROP_CHASSIS];
7871c8e9 558
ebcf1f97 559 return sd_bus_message_append(reply, "s", name);
7871c8e9
LP
560}
561
72f48cd3
LP
562static int property_get_uname_field(
563 sd_bus *bus,
564 const char *path,
565 const char *interface,
566 const char *property,
567 sd_bus_message *reply,
568 void *userdata,
569 sd_bus_error *error) {
570
571 struct utsname u;
572
573 assert_se(uname(&u) >= 0);
574
575 return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
576}
577
19070062 578static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
579 Context *c = userdata;
580 const char *name;
aa994368
LP
581 int interactive, r;
582 struct utsname u;
d200735e 583
19070062
LP
584 assert(m);
585 assert(c);
586
66a4c743
LP
587 r = sd_bus_message_read(m, "sb", &name, &interactive);
588 if (r < 0)
ebcf1f97 589 return r;
d200735e 590
d7f4ad20
LP
591 context_read_etc_hostname(c);
592
66a4c743
LP
593 if (isempty(name))
594 name = c->data[PROP_STATIC_HOSTNAME];
7640a5de 595
66a4c743 596 if (isempty(name))
8146c32b 597 name = FALLBACK_HOSTNAME;
7640a5de 598
8fb49443 599 if (!hostname_is_valid(name, false))
ebcf1f97 600 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
7640a5de 601
aa994368
LP
602 assert_se(uname(&u) >= 0);
603 if (streq_ptr(name, u.nodename))
df2d202e 604 return sd_bus_reply_method_return(m, NULL);
7640a5de 605
c529695e
LP
606 r = bus_verify_polkit_async(
607 m,
608 CAP_SYS_ADMIN,
609 "org.freedesktop.hostname1.set-hostname",
403ed0e5 610 NULL,
c529695e
LP
611 interactive,
612 UID_INVALID,
613 &c->polkit_registry,
614 error);
66a4c743 615 if (r < 0)
ebcf1f97 616 return r;
66a4c743
LP
617 if (r == 0)
618 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 619
aa994368 620 r = context_update_kernel_hostname(c, name);
66a4c743 621 if (r < 0) {
38b38500 622 log_error_errno(r, "Failed to set hostname: %m");
1b4cd646 623 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
66a4c743 624 }
7640a5de 625
aa994368 626 log_info("Changed hostname to '%s'", name);
7640a5de 627
19070062 628 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
7640a5de 629
df2d202e 630 return sd_bus_reply_method_return(m, NULL);
66a4c743 631}
7640a5de 632
19070062 633static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
634 Context *c = userdata;
635 const char *name;
102d8f81 636 int interactive;
66a4c743 637 int r;
7640a5de 638
19070062
LP
639 assert(m);
640 assert(c);
641
66a4c743
LP
642 r = sd_bus_message_read(m, "sb", &name, &interactive);
643 if (r < 0)
ebcf1f97 644 return r;
7640a5de 645
3c6f7c34 646 name = empty_to_null(name);
7640a5de 647
d7f4ad20
LP
648 context_read_etc_hostname(c);
649
66a4c743 650 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 651 return sd_bus_reply_method_return(m, NULL);
7640a5de 652
c650f207
YW
653 if (!isempty(name) && !hostname_is_valid(name, false))
654 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
655
c529695e
LP
656 r = bus_verify_polkit_async(
657 m,
658 CAP_SYS_ADMIN,
659 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 660 NULL,
c529695e
LP
661 interactive,
662 UID_INVALID,
663 &c->polkit_registry,
664 error);
66a4c743 665 if (r < 0)
ebcf1f97 666 return r;
66a4c743
LP
667 if (r == 0)
668 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 669
c650f207
YW
670 r = free_and_strdup(&c->data[PROP_STATIC_HOSTNAME], name);
671 if (r < 0)
672 return r;
7640a5de 673
aa994368 674 r = context_update_kernel_hostname(c, NULL);
c779a442 675 if (r < 0) {
38b38500 676 log_error_errno(r, "Failed to set hostname: %m");
1b4cd646 677 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
c779a442
SW
678 }
679
66a4c743
LP
680 r = context_write_data_static_hostname(c);
681 if (r < 0) {
38b38500 682 log_error_errno(r, "Failed to write static hostname: %m");
1b4cd646 683 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 684 }
7640a5de 685
38b38500 686 log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
7640a5de 687
19070062 688 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
7640a5de 689
df2d202e 690 return sd_bus_reply_method_return(m, NULL);
66a4c743 691}
7640a5de 692
19070062 693static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 694 int interactive;
66a4c743
LP
695 const char *name;
696 int r;
697
698 assert(c);
66a4c743
LP
699 assert(m);
700
701 r = sd_bus_message_read(m, "sb", &name, &interactive);
702 if (r < 0)
ebcf1f97 703 return r;
66a4c743 704
3c6f7c34 705 name = empty_to_null(name);
66a4c743 706
d7f4ad20
LP
707 context_read_machine_info(c);
708
66a4c743 709 if (streq_ptr(name, c->data[prop]))
df2d202e 710 return sd_bus_reply_method_return(m, NULL);
7640a5de 711
c650f207
YW
712 if (!isempty(name)) {
713 /* The icon name might ultimately be used as file
714 * name, so better be safe than sorry */
715
716 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
717 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
718 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
38b38500 719 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
c650f207
YW
720 if (prop == PROP_CHASSIS && !valid_chassis(name))
721 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
722 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
723 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
724 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
725 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
726 }
727
66a4c743
LP
728 /* Since the pretty hostname should always be changed at the
729 * same time as the static one, use the same policy action for
730 * both... */
731
c529695e
LP
732 r = bus_verify_polkit_async(
733 m,
734 CAP_SYS_ADMIN,
735 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 736 NULL,
c529695e
LP
737 interactive,
738 UID_INVALID,
739 &c->polkit_registry,
740 error);
66a4c743 741 if (r < 0)
ebcf1f97 742 return r;
66a4c743
LP
743 if (r == 0)
744 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
745
c650f207
YW
746 r = free_and_strdup(&c->data[prop], name);
747 if (r < 0)
748 return r;
7640a5de 749
f200e8bb 750 r = context_write_data_machine_info(c);
66a4c743 751 if (r < 0) {
da927ba9 752 log_error_errno(r, "Failed to write machine info: %m");
1b4cd646 753 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 754 }
7640a5de 755
66a4c743 756 log_info("Changed %s to '%s'",
38b38500 757 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
799298d6 758 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 759 prop == PROP_LOCATION ? "location" :
66a4c743 760 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 761
19070062
LP
762 (void) sd_bus_emit_properties_changed(
763 sd_bus_message_get_bus(m),
764 "/org/freedesktop/hostname1",
765 "org.freedesktop.hostname1",
766 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
767 prop == PROP_DEPLOYMENT ? "Deployment" :
768 prop == PROP_LOCATION ? "Location" :
769 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 770
df2d202e 771 return sd_bus_reply_method_return(m, NULL);
66a4c743 772}
7640a5de 773
19070062
LP
774static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
775 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
776}
777
19070062
LP
778static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
779 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
780}
781
19070062
LP
782static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
783 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
784}
785
19070062
LP
786static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
787 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
788}
789
19070062
LP
790static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
791 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
792}
793
21e627da
YW
794static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
795 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
796 Context *c = userdata;
5704cd73 797 bool has_uuid = false;
21e627da 798 int interactive, r;
5704cd73 799 sd_id128_t uuid;
21e627da
YW
800
801 assert(m);
802 assert(c);
803
5704cd73
LP
804 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
805 if (r == -ENOENT)
806 r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
807 if (r < 0)
808 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
809 "Failed to read product UUID, ignoring: %m");
810 else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
811 log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid));
812 else
813 has_uuid = true;
814
815 if (!has_uuid)
816 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
817 "Failed to read product UUID from firmware.");
21e627da
YW
818
819 r = sd_bus_message_read(m, "b", &interactive);
820 if (r < 0)
821 return r;
822
823 r = bus_verify_polkit_async(
824 m,
825 CAP_SYS_ADMIN,
826 "org.freedesktop.hostname1.get-product-uuid",
827 NULL,
828 interactive,
829 UID_INVALID,
830 &c->polkit_registry,
831 error);
832 if (r < 0)
833 return r;
834 if (r == 0)
835 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
836
837 r = sd_bus_message_new_method_return(m, &reply);
838 if (r < 0)
839 return r;
840
5704cd73 841 r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid));
21e627da
YW
842 if (r < 0)
843 return r;
844
845 return sd_bus_send(NULL, reply, NULL);
846}
847
66a4c743
LP
848static const sd_bus_vtable hostname_vtable[] = {
849 SD_BUS_VTABLE_START(0),
aa994368 850 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
851 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
852 SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
66a4c743
LP
853 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
854 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
855 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
856 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
857 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
858 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
859 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
860 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
861 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
862 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
863
864 SD_BUS_METHOD_WITH_NAMES("SetHostname",
865 "sb",
866 SD_BUS_PARAM(hostname)
867 SD_BUS_PARAM(interactive),
868 NULL,,
869 method_set_hostname,
870 SD_BUS_VTABLE_UNPRIVILEGED),
871 SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
872 "sb",
873 SD_BUS_PARAM(hostname)
874 SD_BUS_PARAM(interactive),
875 NULL,,
876 method_set_static_hostname,
877 SD_BUS_VTABLE_UNPRIVILEGED),
878 SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
879 "sb",
880 SD_BUS_PARAM(hostname)
881 SD_BUS_PARAM(interactive),
882 NULL,,
883 method_set_pretty_hostname,
884 SD_BUS_VTABLE_UNPRIVILEGED),
885 SD_BUS_METHOD_WITH_NAMES("SetIconName",
886 "sb",
887 SD_BUS_PARAM(icon)
888 SD_BUS_PARAM(interactive),
889 NULL,,
890 method_set_icon_name,
891 SD_BUS_VTABLE_UNPRIVILEGED),
892 SD_BUS_METHOD_WITH_NAMES("SetChassis",
893 "sb",
894 SD_BUS_PARAM(chassis)
895 SD_BUS_PARAM(interactive),
896 NULL,,
897 method_set_chassis,
898 SD_BUS_VTABLE_UNPRIVILEGED),
899 SD_BUS_METHOD_WITH_NAMES("SetDeployment",
900 "sb",
901 SD_BUS_PARAM(deployment)
902 SD_BUS_PARAM(interactive),
903 NULL,,
904 method_set_deployment,
905 SD_BUS_VTABLE_UNPRIVILEGED),
906 SD_BUS_METHOD_WITH_NAMES("SetLocation",
907 "sb",
908 SD_BUS_PARAM(location)
909 SD_BUS_PARAM(interactive),
910 NULL,,
911 method_set_location,
912 SD_BUS_VTABLE_UNPRIVILEGED),
913 SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
914 "b",
915 SD_BUS_PARAM(interactive),
916 "ay",
917 SD_BUS_PARAM(uuid),
918 method_get_product_uuid,
919 SD_BUS_VTABLE_UNPRIVILEGED),
920
66a4c743
LP
921 SD_BUS_VTABLE_END,
922};
923
670139db
ZJS
924static const BusObjectImplementation manager_object = {
925 "/org/freedesktop/hostname1",
926 "org.freedesktop.hostname1",
927 .vtables = BUS_VTABLES(hostname_vtable),
928};
929
2ac4d1d4 930static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
4afd3348 931 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
932 int r;
933
66a4c743
LP
934 assert(c);
935 assert(event);
2ac4d1d4 936 assert(ret);
d0baa06f 937
76b54375 938 r = sd_bus_default_system(&bus);
23bbb0de
MS
939 if (r < 0)
940 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 941
670139db 942 r = bus_add_implementation(bus, &manager_object, c);
23bbb0de 943 if (r < 0)
670139db 944 return r;
d0baa06f 945
ac9f55ed
LP
946 r = bus_log_control_api_register(bus);
947 if (r < 0)
948 return r;
949
0c0b9306 950 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 951 if (r < 0)
0c0b9306 952 return log_error_errno(r, "Failed to request name: %m");
add10b5a 953
66a4c743 954 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
955 if (r < 0)
956 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 957
2ac4d1d4 958 *ret = TAKE_PTR(bus);
66a4c743 959 return 0;
d0baa06f
LP
960}
961
85ae63ee 962static int run(int argc, char *argv[]) {
cfb9433d 963 _cleanup_(context_destroy) Context context = {};
4afd3348
LP
964 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
965 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 966 int r;
d0baa06f 967
6bf3c61c 968 log_setup_service();
7640a5de 969
fc021a5b
ZJS
970 r = service_parse_argv("systemd-hostnamed.service",
971 "Manage the system hostname and related metadata.",
670139db
ZJS
972 BUS_IMPLEMENTATIONS(&manager_object,
973 &log_control_object),
fc021a5b
ZJS
974 argc, argv);
975 if (r <= 0)
976 return r;
977
4c12626c 978 umask(0022);
a9ba0e32
CG
979
980 r = mac_selinux_init();
981 if (r < 0)
982 return r;
4c12626c 983
b22c8bfc
YW
984 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
985
afc6adb5 986 r = sd_event_default(&event);
85ae63ee
YW
987 if (r < 0)
988 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 989
b22c8bfc
YW
990 (void) sd_event_set_watchdog(event, true);
991
992 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
993 if (r < 0)
994 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
995
996 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
997 if (r < 0)
998 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 999
66a4c743 1000 r = connect_bus(&context, event, &bus);
d0baa06f 1001 if (r < 0)
85ae63ee 1002 return r;
7640a5de 1003
37224a5f 1004 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
1005 if (r < 0)
1006 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 1007
85ae63ee 1008 return 0;
7640a5de 1009}
85ae63ee
YW
1010
1011DEFINE_MAIN_FUNCTION(run);