]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
hostnamed: fix return value
[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"
958b66ea 20#include "hostname-util.h"
21e627da 21#include "id128-util.h"
85ae63ee 22#include "main-func.h"
36dd5ffd 23#include "missing_capability.h"
7782e0a0 24#include "nscd-flush.h"
d8b4d14d 25#include "nulstr-util.h"
d58ad743 26#include "os-util.h"
6bedfcbb 27#include "parse-util.h"
bb15fafe 28#include "path-util.h"
6bedfcbb 29#include "selinux-util.h"
fc021a5b 30#include "service-util.h"
b22c8bfc 31#include "signal-util.h"
d7f4ad20 32#include "stat-util.h"
6bedfcbb 33#include "strv.h"
ee104e11 34#include "user-util.h"
6bedfcbb
LP
35#include "util.h"
36#include "virt.h"
91f9dcaf 37
799298d6
JG
38#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
39
7640a5de 40enum {
d7f4ad20 41 /* Read from /etc/hostname */
7640a5de 42 PROP_STATIC_HOSTNAME,
d7f4ad20
LP
43
44 /* Read from /etc/machine-info */
7640a5de
LP
45 PROP_PRETTY_HOSTNAME,
46 PROP_ICON_NAME,
7871c8e9 47 PROP_CHASSIS,
799298d6 48 PROP_DEPLOYMENT,
ce0f1493 49 PROP_LOCATION,
d7f4ad20
LP
50
51 /* Read from /etc/os-release (or /usr/lib/os-release) */
44c32988
DH
52 PROP_OS_PRETTY_NAME,
53 PROP_OS_CPE_NAME,
d7f4ad20
LP
54 PROP_OS_HOME_URL,
55 _PROP_MAX,
56 _PROP_INVALID = -1,
7640a5de
LP
57};
58
66a4c743
LP
59typedef struct Context {
60 char *data[_PROP_MAX];
d7f4ad20
LP
61
62 struct stat etc_hostname_stat;
63 struct stat etc_os_release_stat;
64 struct stat etc_machine_info_stat;
65
66a4c743
LP
66 Hashmap *polkit_registry;
67} Context;
ad740100 68
d7f4ad20 69static void context_reset(Context *c, uint64_t mask) {
7640a5de
LP
70 int p;
71
66a4c743
LP
72 assert(c);
73
d7f4ad20
LP
74 for (p = 0; p < _PROP_MAX; p++) {
75 if (!FLAGS_SET(mask, UINT64_C(1) << p))
76 continue;
77
a1e58e8e 78 c->data[p] = mfree(c->data[p]);
d7f4ad20 79 }
7640a5de
LP
80}
81
cfb9433d 82static void context_destroy(Context *c) {
66a4c743
LP
83 assert(c);
84
d7f4ad20 85 context_reset(c, UINT64_MAX);
36e34057 86 bus_verify_polkit_async_registry_free(c->polkit_registry);
66a4c743
LP
87}
88
d7f4ad20
LP
89static void context_read_etc_hostname(Context *c) {
90 struct stat current_stat = {};
7640a5de
LP
91 int r;
92
66a4c743
LP
93 assert(c);
94
d7f4ad20
LP
95 if (stat("/etc/hostname", &current_stat) >= 0 &&
96 stat_inode_unmodified(&c->etc_hostname_stat, &current_stat))
97 return;
98
99 context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME);
7640a5de 100
f35cb39e 101 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
7640a5de 102 if (r < 0 && r != -ENOENT)
d7f4ad20
LP
103 log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m");
104
105 c->etc_hostname_stat = current_stat;
106}
107
108static void context_read_machine_info(Context *c) {
109 struct stat current_stat = {};
110 int r;
111
112 assert(c);
113
114 if (stat("/etc/machine-info", &current_stat) >= 0 &&
115 stat_inode_unmodified(&c->etc_machine_info_stat, &current_stat))
116 return;
117
118 context_reset(c,
119 (UINT64_C(1) << PROP_PRETTY_HOSTNAME) |
120 (UINT64_C(1) << PROP_ICON_NAME) |
121 (UINT64_C(1) << PROP_CHASSIS) |
122 (UINT64_C(1) << PROP_DEPLOYMENT) |
123 (UINT64_C(1) << PROP_LOCATION));
7640a5de 124
aa8fbc74 125 r = parse_env_file(NULL, "/etc/machine-info",
66a4c743
LP
126 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
127 "ICON_NAME", &c->data[PROP_ICON_NAME],
128 "CHASSIS", &c->data[PROP_CHASSIS],
799298d6 129 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
13df9c39 130 "LOCATION", &c->data[PROP_LOCATION]);
7640a5de 131 if (r < 0 && r != -ENOENT)
d7f4ad20
LP
132 log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
133
134 c->etc_machine_info_stat = current_stat;
135}
136
137static void context_read_os_release(Context *c) {
138 struct stat current_stat = {};
139 int r;
140
141 assert(c);
142
143 if ((stat("/etc/os-release", &current_stat) >= 0 ||
144 stat("/usr/lib/os-release", &current_stat) >= 0) &&
145 stat_inode_unmodified(&c->etc_os_release_stat, &current_stat))
146 return;
147
148 context_reset(c,
149 (UINT64_C(1) << PROP_OS_PRETTY_NAME) |
150 (UINT64_C(1) << PROP_OS_CPE_NAME) |
151 (UINT64_C(1) << PROP_OS_HOME_URL));
7640a5de 152
d58ad743
LP
153 r = parse_os_release(NULL,
154 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
155 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
d7f4ad20 156 "HOME_URL", &c->data[PROP_OS_HOME_URL],
d58ad743 157 NULL);
44c32988 158 if (r < 0 && r != -ENOENT)
d7f4ad20 159 log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
44c32988 160
d7f4ad20 161 c->etc_os_release_stat = current_stat;
7640a5de
LP
162}
163
7871c8e9 164static bool valid_chassis(const char *chassis) {
7871c8e9
LP
165 assert(chassis);
166
167 return nulstr_contains(
168 "vm\0"
169 "container\0"
170 "desktop\0"
171 "laptop\0"
34b52450 172 "convertible\0"
7871c8e9
LP
173 "server\0"
174 "tablet\0"
c49e59c1 175 "handset\0"
25fa306e
LP
176 "watch\0"
177 "embedded\0",
7871c8e9
LP
178 chassis);
179}
180
799298d6
JG
181static bool valid_deployment(const char *deployment) {
182 assert(deployment);
183
c2142cf1 184 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
799298d6
JG
185}
186
7871c8e9 187static const char* fallback_chassis(void) {
7640a5de
LP
188 char *type;
189 unsigned t;
219bfe38 190 int v, r;
7871c8e9 191
75f86906 192 v = detect_virtualization();
2ac4d1d4
LP
193 if (v < 0)
194 log_debug_errno(v, "Failed to detect virtualization, ignoring: %m");
195 else if (VIRTUALIZATION_IS_VM(v))
7871c8e9 196 return "vm";
2ac4d1d4 197 else if (VIRTUALIZATION_IS_CONTAINER(v))
7871c8e9
LP
198 return "container";
199
219bfe38 200 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
2ac4d1d4
LP
201 if (r < 0) {
202 log_debug_errno(v, "Failed to read DMI chassis type, ignoring: %m");
219bfe38 203 goto try_acpi;
2ac4d1d4 204 }
7871c8e9
LP
205
206 r = safe_atou(type, &t);
207 free(type);
2ac4d1d4
LP
208 if (r < 0) {
209 log_debug_errno(v, "Failed to parse DMI chassis type, ignoring: %m");
219bfe38 210 goto try_acpi;
2ac4d1d4 211 }
7871c8e9 212
219bfe38
LP
213 /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
214 additional guesswork on top of that.
215
216 See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here:
217
218 https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
7871c8e9
LP
219 */
220
219bfe38 221 switch (t) {
7871c8e9 222
219bfe38
LP
223 case 0x3: /* Desktop */
224 case 0x4: /* Low Profile Desktop */
225 case 0x6: /* Mini Tower */
226 case 0x7: /* Tower */
7c5c59d4 227 case 0xD: /* All in one (i.e. PC built into monitor) */
7871c8e9
LP
228 return "desktop";
229
219bfe38
LP
230 case 0x8: /* Portable */
231 case 0x9: /* Laptop */
232 case 0xA: /* Notebook */
233 case 0xE: /* Sub Notebook */
7871c8e9 234 return "laptop";
7640a5de 235
219bfe38
LP
236 case 0xB: /* Hand Held */
237 return "handset";
238
239 case 0x11: /* Main Server Chassis */
240 case 0x1C: /* Blade */
241 case 0x1D: /* Blade Enclosure */
7871c8e9 242 return "server";
7640a5de 243
219bfe38 244 case 0x1E: /* Tablet */
7871c8e9 245 return "tablet";
b70af833
DH
246
247 case 0x1F: /* Convertible */
b4227dbb 248 case 0x20: /* Detachable */
b70af833 249 return "convertible";
2ac4d1d4
LP
250
251 default:
252 log_debug("Unhandled DMI chassis type 0x%02x, ignoring.", t);
7871c8e9
LP
253 }
254
219bfe38
LP
255try_acpi:
256 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
2ac4d1d4
LP
257 if (r < 0) {
258 log_debug_errno(v, "Failed read ACPI PM profile, ignoring: %m");
7640a5de 259 return NULL;
2ac4d1d4 260 }
7640a5de
LP
261
262 r = safe_atou(type, &t);
263 free(type);
2ac4d1d4
LP
264 if (r < 0) {
265 log_debug_errno(v, "Failed parse ACPI PM profile, ignoring: %m");
7640a5de 266 return NULL;
2ac4d1d4 267 }
7640a5de 268
219bfe38
LP
269 /* We only list the really obvious cases here as the ACPI data is not really super reliable.
270 *
271 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
272 *
273 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
41550d40 274 */
7640a5de 275
219bfe38 276 switch(t) {
7640a5de 277
219bfe38
LP
278 case 1: /* Desktop */
279 case 3: /* Workstation */
280 case 6: /* Appliance PC */
7871c8e9 281 return "desktop";
7640a5de 282
219bfe38 283 case 2: /* Mobile */
7871c8e9
LP
284 return "laptop";
285
219bfe38
LP
286 case 4: /* Enterprise Server */
287 case 5: /* SOHO Server */
288 case 7: /* Performance Server */
7871c8e9 289 return "server";
f3f4f008 290
219bfe38 291 case 8: /* Tablet */
f3f4f008 292 return "tablet";
2ac4d1d4
LP
293
294 default:
295 log_debug("Unhandled ACPI PM profile 0x%02x, ignoring.", t);
7640a5de
LP
296 }
297
7640a5de
LP
298 return NULL;
299}
300
66a4c743 301static char* context_fallback_icon_name(Context *c) {
7871c8e9
LP
302 const char *chassis;
303
66a4c743
LP
304 assert(c);
305
306 if (!isempty(c->data[PROP_CHASSIS]))
b910cc72 307 return strjoin("computer-", c->data[PROP_CHASSIS]);
7871c8e9
LP
308
309 chassis = fallback_chassis();
310 if (chassis)
b910cc72 311 return strjoin("computer-", chassis);
7871c8e9
LP
312
313 return strdup("computer");
314}
315
c779a442 316static bool hostname_is_useful(const char *hn) {
fecc80c1 317 return !isempty(hn) && !is_localhost(hn);
c779a442
SW
318}
319
aa994368
LP
320static int context_update_kernel_hostname(
321 Context *c,
322 const char *transient_hn) {
323
324 const char *static_hn, *hn;
325 struct utsname u;
7d9ec609 326 int r;
7640a5de 327
66a4c743
LP
328 assert(c);
329
aa994368
LP
330 if (!transient_hn) {
331 /* If no transient hostname is passed in, then let's check what is currently set. */
332 assert_se(uname(&u) >= 0);
333 transient_hn =
334 isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
335 }
336
c779a442
SW
337 static_hn = c->data[PROP_STATIC_HOSTNAME];
338
339 /* /etc/hostname with something other than "localhost"
340 * has the highest preference ... */
341 if (hostname_is_useful(static_hn))
342 hn = static_hn;
343
38b38500 344 /* ... the transient hostname, (ie: DHCP) comes next ... */
aa994368
LP
345 else if (!isempty(transient_hn))
346 hn = transient_hn;
7640a5de 347
c779a442
SW
348 /* ... fallback to static "localhost.*" ignored above ... */
349 else if (!isempty(static_hn))
350 hn = static_hn;
351
352 /* ... and the ultimate fallback */
353 else
8146c32b 354 hn = FALLBACK_HOSTNAME;
c779a442 355
7d9ec609
ZJS
356 r = sethostname_idempotent(hn);
357 if (r < 0)
358 return r;
7640a5de 359
7782e0a0
LP
360 (void) nscd_flush_cache(STRV_MAKE("hosts"));
361
7640a5de
LP
362 return 0;
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");
66a4c743 629 }
7640a5de 630
aa994368 631 log_info("Changed hostname to '%s'", name);
7640a5de 632
19070062 633 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
7640a5de 634
df2d202e 635 return sd_bus_reply_method_return(m, NULL);
66a4c743 636}
7640a5de 637
19070062 638static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
639 Context *c = userdata;
640 const char *name;
102d8f81 641 int interactive;
66a4c743 642 int r;
7640a5de 643
19070062
LP
644 assert(m);
645 assert(c);
646
66a4c743
LP
647 r = sd_bus_message_read(m, "sb", &name, &interactive);
648 if (r < 0)
ebcf1f97 649 return r;
7640a5de 650
3c6f7c34 651 name = empty_to_null(name);
7640a5de 652
d7f4ad20
LP
653 context_read_etc_hostname(c);
654
66a4c743 655 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 656 return sd_bus_reply_method_return(m, NULL);
7640a5de 657
52ef5dd7 658 if (!isempty(name) && !hostname_is_valid(name, 0))
c650f207
YW
659 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
660
c529695e
LP
661 r = bus_verify_polkit_async(
662 m,
663 CAP_SYS_ADMIN,
664 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 665 NULL,
c529695e
LP
666 interactive,
667 UID_INVALID,
668 &c->polkit_registry,
669 error);
66a4c743 670 if (r < 0)
ebcf1f97 671 return r;
66a4c743
LP
672 if (r == 0)
673 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 674
c650f207
YW
675 r = free_and_strdup(&c->data[PROP_STATIC_HOSTNAME], name);
676 if (r < 0)
677 return r;
7640a5de 678
aa994368 679 r = context_update_kernel_hostname(c, NULL);
c779a442 680 if (r < 0) {
38b38500 681 log_error_errno(r, "Failed to set hostname: %m");
1b4cd646 682 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
c779a442
SW
683 }
684
66a4c743
LP
685 r = context_write_data_static_hostname(c);
686 if (r < 0) {
38b38500 687 log_error_errno(r, "Failed to write static hostname: %m");
1b4cd646 688 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 689 }
7640a5de 690
38b38500 691 log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
7640a5de 692
19070062 693 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
7640a5de 694
df2d202e 695 return sd_bus_reply_method_return(m, NULL);
66a4c743 696}
7640a5de 697
19070062 698static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 699 int interactive;
66a4c743
LP
700 const char *name;
701 int r;
702
703 assert(c);
66a4c743
LP
704 assert(m);
705
706 r = sd_bus_message_read(m, "sb", &name, &interactive);
707 if (r < 0)
ebcf1f97 708 return r;
66a4c743 709
3c6f7c34 710 name = empty_to_null(name);
66a4c743 711
d7f4ad20
LP
712 context_read_machine_info(c);
713
66a4c743 714 if (streq_ptr(name, c->data[prop]))
df2d202e 715 return sd_bus_reply_method_return(m, NULL);
7640a5de 716
c650f207
YW
717 if (!isempty(name)) {
718 /* The icon name might ultimately be used as file
719 * name, so better be safe than sorry */
720
721 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
722 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
723 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
38b38500 724 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
c650f207
YW
725 if (prop == PROP_CHASSIS && !valid_chassis(name))
726 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
727 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
728 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
729 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
730 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
731 }
732
66a4c743
LP
733 /* Since the pretty hostname should always be changed at the
734 * same time as the static one, use the same policy action for
735 * both... */
736
c529695e
LP
737 r = bus_verify_polkit_async(
738 m,
739 CAP_SYS_ADMIN,
740 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 741 NULL,
c529695e
LP
742 interactive,
743 UID_INVALID,
744 &c->polkit_registry,
745 error);
66a4c743 746 if (r < 0)
ebcf1f97 747 return r;
66a4c743
LP
748 if (r == 0)
749 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
750
c650f207
YW
751 r = free_and_strdup(&c->data[prop], name);
752 if (r < 0)
753 return r;
7640a5de 754
f200e8bb 755 r = context_write_data_machine_info(c);
66a4c743 756 if (r < 0) {
da927ba9 757 log_error_errno(r, "Failed to write machine info: %m");
1b4cd646 758 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 759 }
7640a5de 760
66a4c743 761 log_info("Changed %s to '%s'",
38b38500 762 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
799298d6 763 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 764 prop == PROP_LOCATION ? "location" :
66a4c743 765 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 766
19070062
LP
767 (void) sd_bus_emit_properties_changed(
768 sd_bus_message_get_bus(m),
769 "/org/freedesktop/hostname1",
770 "org.freedesktop.hostname1",
771 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
772 prop == PROP_DEPLOYMENT ? "Deployment" :
773 prop == PROP_LOCATION ? "Location" :
774 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 775
df2d202e 776 return sd_bus_reply_method_return(m, NULL);
66a4c743 777}
7640a5de 778
19070062
LP
779static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
780 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
781}
782
19070062
LP
783static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
784 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
785}
786
19070062
LP
787static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
788 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
789}
790
19070062
LP
791static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
792 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
793}
794
19070062
LP
795static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
796 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
797}
798
21e627da
YW
799static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
800 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
801 Context *c = userdata;
5704cd73 802 bool has_uuid = false;
21e627da 803 int interactive, r;
5704cd73 804 sd_id128_t uuid;
21e627da
YW
805
806 assert(m);
807 assert(c);
808
5704cd73
LP
809 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
810 if (r == -ENOENT)
811 r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
812 if (r < 0)
813 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
814 "Failed to read product UUID, ignoring: %m");
815 else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
816 log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid));
817 else
818 has_uuid = true;
819
820 if (!has_uuid)
821 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
822 "Failed to read product UUID from firmware.");
21e627da
YW
823
824 r = sd_bus_message_read(m, "b", &interactive);
825 if (r < 0)
826 return r;
827
828 r = bus_verify_polkit_async(
829 m,
830 CAP_SYS_ADMIN,
831 "org.freedesktop.hostname1.get-product-uuid",
832 NULL,
833 interactive,
834 UID_INVALID,
835 &c->polkit_registry,
836 error);
837 if (r < 0)
838 return r;
839 if (r == 0)
840 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
841
842 r = sd_bus_message_new_method_return(m, &reply);
843 if (r < 0)
844 return r;
845
5704cd73 846 r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid));
21e627da
YW
847 if (r < 0)
848 return r;
849
850 return sd_bus_send(NULL, reply, NULL);
851}
852
66a4c743
LP
853static const sd_bus_vtable hostname_vtable[] = {
854 SD_BUS_VTABLE_START(0),
aa994368 855 SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
856 SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
857 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 858 SD_BUS_PROPERTY("FallbackHostname", "s", property_get_fallback_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
66a4c743
LP
859 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
860 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d7f4ad20
LP
861 SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
862 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
863 SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
864 SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
865 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
866 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
867 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
868 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
869
870 SD_BUS_METHOD_WITH_NAMES("SetHostname",
871 "sb",
872 SD_BUS_PARAM(hostname)
873 SD_BUS_PARAM(interactive),
874 NULL,,
875 method_set_hostname,
876 SD_BUS_VTABLE_UNPRIVILEGED),
877 SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
878 "sb",
879 SD_BUS_PARAM(hostname)
880 SD_BUS_PARAM(interactive),
881 NULL,,
882 method_set_static_hostname,
883 SD_BUS_VTABLE_UNPRIVILEGED),
884 SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
885 "sb",
886 SD_BUS_PARAM(hostname)
887 SD_BUS_PARAM(interactive),
888 NULL,,
889 method_set_pretty_hostname,
890 SD_BUS_VTABLE_UNPRIVILEGED),
891 SD_BUS_METHOD_WITH_NAMES("SetIconName",
892 "sb",
893 SD_BUS_PARAM(icon)
894 SD_BUS_PARAM(interactive),
895 NULL,,
896 method_set_icon_name,
897 SD_BUS_VTABLE_UNPRIVILEGED),
898 SD_BUS_METHOD_WITH_NAMES("SetChassis",
899 "sb",
900 SD_BUS_PARAM(chassis)
901 SD_BUS_PARAM(interactive),
902 NULL,,
903 method_set_chassis,
904 SD_BUS_VTABLE_UNPRIVILEGED),
905 SD_BUS_METHOD_WITH_NAMES("SetDeployment",
906 "sb",
907 SD_BUS_PARAM(deployment)
908 SD_BUS_PARAM(interactive),
909 NULL,,
910 method_set_deployment,
911 SD_BUS_VTABLE_UNPRIVILEGED),
912 SD_BUS_METHOD_WITH_NAMES("SetLocation",
913 "sb",
914 SD_BUS_PARAM(location)
915 SD_BUS_PARAM(interactive),
916 NULL,,
917 method_set_location,
918 SD_BUS_VTABLE_UNPRIVILEGED),
919 SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
920 "b",
921 SD_BUS_PARAM(interactive),
922 "ay",
923 SD_BUS_PARAM(uuid),
924 method_get_product_uuid,
925 SD_BUS_VTABLE_UNPRIVILEGED),
926
66a4c743
LP
927 SD_BUS_VTABLE_END,
928};
929
670139db
ZJS
930static const BusObjectImplementation manager_object = {
931 "/org/freedesktop/hostname1",
932 "org.freedesktop.hostname1",
933 .vtables = BUS_VTABLES(hostname_vtable),
934};
935
2ac4d1d4 936static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
4afd3348 937 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
938 int r;
939
66a4c743
LP
940 assert(c);
941 assert(event);
2ac4d1d4 942 assert(ret);
d0baa06f 943
76b54375 944 r = sd_bus_default_system(&bus);
23bbb0de
MS
945 if (r < 0)
946 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 947
670139db 948 r = bus_add_implementation(bus, &manager_object, c);
23bbb0de 949 if (r < 0)
670139db 950 return r;
d0baa06f 951
ac9f55ed
LP
952 r = bus_log_control_api_register(bus);
953 if (r < 0)
954 return r;
955
0c0b9306 956 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 957 if (r < 0)
0c0b9306 958 return log_error_errno(r, "Failed to request name: %m");
add10b5a 959
66a4c743 960 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
961 if (r < 0)
962 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 963
2ac4d1d4 964 *ret = TAKE_PTR(bus);
66a4c743 965 return 0;
d0baa06f
LP
966}
967
85ae63ee 968static int run(int argc, char *argv[]) {
cfb9433d 969 _cleanup_(context_destroy) Context context = {};
4afd3348
LP
970 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
971 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 972 int r;
d0baa06f 973
6bf3c61c 974 log_setup_service();
7640a5de 975
fc021a5b
ZJS
976 r = service_parse_argv("systemd-hostnamed.service",
977 "Manage the system hostname and related metadata.",
670139db
ZJS
978 BUS_IMPLEMENTATIONS(&manager_object,
979 &log_control_object),
fc021a5b
ZJS
980 argc, argv);
981 if (r <= 0)
982 return r;
983
4c12626c 984 umask(0022);
a9ba0e32
CG
985
986 r = mac_selinux_init();
987 if (r < 0)
988 return r;
4c12626c 989
b22c8bfc
YW
990 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
991
afc6adb5 992 r = sd_event_default(&event);
85ae63ee
YW
993 if (r < 0)
994 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 995
b22c8bfc
YW
996 (void) sd_event_set_watchdog(event, true);
997
998 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
999 if (r < 0)
1000 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
1001
1002 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
1003 if (r < 0)
1004 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 1005
66a4c743 1006 r = connect_bus(&context, event, &bus);
d0baa06f 1007 if (r < 0)
85ae63ee 1008 return r;
7640a5de 1009
37224a5f 1010 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
1011 if (r < 0)
1012 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 1013
85ae63ee 1014 return 0;
7640a5de 1015}
85ae63ee
YW
1016
1017DEFINE_MAIN_FUNCTION(run);