]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
Merge pull request #12971 from yuwata/network-reassign-static-routes
[thirdparty/systemd.git] / src / hostname / hostnamed.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
7640a5de 2
7640a5de
LP
3#include <errno.h>
4#include <string.h>
f426cc5d 5#include <sys/utsname.h>
ca78ad1d
ZJS
6#include <sys/stat.h>
7#include <sys/types.h>
cf0fbc49 8#include <unistd.h>
7640a5de 9
b5efdb8a 10#include "alloc-util.h"
21e627da 11#include "bus-common-errors.h"
6bedfcbb 12#include "bus-util.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"
b22c8bfc 29#include "signal-util.h"
6bedfcbb 30#include "strv.h"
ee104e11 31#include "user-util.h"
6bedfcbb
LP
32#include "util.h"
33#include "virt.h"
91f9dcaf 34
799298d6
JG
35#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
36
7640a5de
LP
37enum {
38 PROP_HOSTNAME,
39 PROP_STATIC_HOSTNAME,
40 PROP_PRETTY_HOSTNAME,
41 PROP_ICON_NAME,
7871c8e9 42 PROP_CHASSIS,
799298d6 43 PROP_DEPLOYMENT,
ce0f1493 44 PROP_LOCATION,
f426cc5d
DH
45 PROP_KERNEL_NAME,
46 PROP_KERNEL_RELEASE,
9be3455f 47 PROP_KERNEL_VERSION,
44c32988
DH
48 PROP_OS_PRETTY_NAME,
49 PROP_OS_CPE_NAME,
64928aa5 50 PROP_HOME_URL,
7640a5de
LP
51 _PROP_MAX
52};
53
66a4c743
LP
54typedef struct Context {
55 char *data[_PROP_MAX];
56 Hashmap *polkit_registry;
21e627da
YW
57 sd_id128_t uuid;
58 bool has_uuid;
66a4c743 59} Context;
ad740100 60
66a4c743 61static void context_reset(Context *c) {
7640a5de
LP
62 int p;
63
66a4c743
LP
64 assert(c);
65
1f6b4113 66 for (p = 0; p < _PROP_MAX; p++)
a1e58e8e 67 c->data[p] = mfree(c->data[p]);
7640a5de
LP
68}
69
85ae63ee 70static void context_clear(Context *c) {
66a4c743
LP
71 assert(c);
72
73 context_reset(c);
36e34057 74 bus_verify_polkit_async_registry_free(c->polkit_registry);
66a4c743
LP
75}
76
77static int context_read_data(Context *c) {
7640a5de 78 int r;
f426cc5d 79 struct utsname u;
7640a5de 80
66a4c743
LP
81 assert(c);
82
83 context_reset(c);
7640a5de 84
f426cc5d
DH
85 assert_se(uname(&u) >= 0);
86 c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
87 c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
9be3455f
DH
88 c->data[PROP_KERNEL_VERSION] = strdup(u.version);
89 if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] ||
90 !c->data[PROP_KERNEL_VERSION])
f426cc5d
DH
91 return -ENOMEM;
92
66a4c743
LP
93 c->data[PROP_HOSTNAME] = gethostname_malloc();
94 if (!c->data[PROP_HOSTNAME])
7640a5de
LP
95 return -ENOMEM;
96
f35cb39e 97 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
7640a5de
LP
98 if (r < 0 && r != -ENOENT)
99 return r;
100
aa8fbc74 101 r = parse_env_file(NULL, "/etc/machine-info",
66a4c743
LP
102 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
103 "ICON_NAME", &c->data[PROP_ICON_NAME],
104 "CHASSIS", &c->data[PROP_CHASSIS],
799298d6 105 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
13df9c39 106 "LOCATION", &c->data[PROP_LOCATION]);
7640a5de
LP
107 if (r < 0 && r != -ENOENT)
108 return r;
109
d58ad743
LP
110 r = parse_os_release(NULL,
111 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
112 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
113 "HOME_URL", &c->data[PROP_HOME_URL],
114 NULL);
44c32988
DH
115 if (r < 0 && r != -ENOENT)
116 return r;
117
21e627da
YW
118 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid);
119 if (r < 0)
6839aa56
LP
120 log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
121 "Failed to read product UUID, ignoring: %m");
122 else if (sd_id128_is_null(c->uuid) || sd_id128_is_allf(c->uuid))
123 log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(c->uuid));
124 else
125 c->has_uuid = true;
21e627da 126
7640a5de
LP
127 return 0;
128}
129
7871c8e9 130static bool valid_chassis(const char *chassis) {
7871c8e9
LP
131 assert(chassis);
132
133 return nulstr_contains(
134 "vm\0"
135 "container\0"
136 "desktop\0"
137 "laptop\0"
34b52450 138 "convertible\0"
7871c8e9
LP
139 "server\0"
140 "tablet\0"
c49e59c1 141 "handset\0"
25fa306e
LP
142 "watch\0"
143 "embedded\0",
7871c8e9
LP
144 chassis);
145}
146
799298d6
JG
147static bool valid_deployment(const char *deployment) {
148 assert(deployment);
149
c2142cf1 150 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
799298d6
JG
151}
152
7871c8e9 153static const char* fallback_chassis(void) {
7640a5de
LP
154 char *type;
155 unsigned t;
219bfe38 156 int v, r;
7871c8e9 157
75f86906 158 v = detect_virtualization();
75f86906 159 if (VIRTUALIZATION_IS_VM(v))
7871c8e9 160 return "vm";
75f86906 161 if (VIRTUALIZATION_IS_CONTAINER(v))
7871c8e9
LP
162 return "container";
163
219bfe38 164 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
7871c8e9 165 if (r < 0)
219bfe38 166 goto try_acpi;
7871c8e9
LP
167
168 r = safe_atou(type, &t);
169 free(type);
170 if (r < 0)
219bfe38 171 goto try_acpi;
7871c8e9 172
219bfe38
LP
173 /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
174 additional guesswork on top of that.
175
176 See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here:
177
178 https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
7871c8e9
LP
179 */
180
219bfe38 181 switch (t) {
7871c8e9 182
219bfe38
LP
183 case 0x3: /* Desktop */
184 case 0x4: /* Low Profile Desktop */
185 case 0x6: /* Mini Tower */
186 case 0x7: /* Tower */
7871c8e9
LP
187 return "desktop";
188
219bfe38
LP
189 case 0x8: /* Portable */
190 case 0x9: /* Laptop */
191 case 0xA: /* Notebook */
192 case 0xE: /* Sub Notebook */
7871c8e9 193 return "laptop";
7640a5de 194
219bfe38
LP
195 case 0xB: /* Hand Held */
196 return "handset";
197
198 case 0x11: /* Main Server Chassis */
199 case 0x1C: /* Blade */
200 case 0x1D: /* Blade Enclosure */
7871c8e9 201 return "server";
7640a5de 202
219bfe38 203 case 0x1E: /* Tablet */
7871c8e9 204 return "tablet";
b70af833
DH
205
206 case 0x1F: /* Convertible */
b4227dbb 207 case 0x20: /* Detachable */
b70af833 208 return "convertible";
7871c8e9
LP
209 }
210
219bfe38
LP
211try_acpi:
212 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
7640a5de
LP
213 if (r < 0)
214 return NULL;
215
216 r = safe_atou(type, &t);
217 free(type);
7640a5de
LP
218 if (r < 0)
219 return NULL;
220
219bfe38
LP
221 /* We only list the really obvious cases here as the ACPI data is not really super reliable.
222 *
223 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
224 *
225 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
41550d40 226 */
7640a5de 227
219bfe38 228 switch(t) {
7640a5de 229
219bfe38
LP
230 case 1: /* Desktop */
231 case 3: /* Workstation */
232 case 6: /* Appliance PC */
7871c8e9 233 return "desktop";
7640a5de 234
219bfe38 235 case 2: /* Mobile */
7871c8e9
LP
236 return "laptop";
237
219bfe38
LP
238 case 4: /* Enterprise Server */
239 case 5: /* SOHO Server */
240 case 7: /* Performance Server */
7871c8e9 241 return "server";
f3f4f008 242
219bfe38 243 case 8: /* Tablet */
f3f4f008 244 return "tablet";
7640a5de
LP
245 }
246
7640a5de
LP
247 return NULL;
248}
249
66a4c743 250static char* context_fallback_icon_name(Context *c) {
7871c8e9
LP
251 const char *chassis;
252
66a4c743
LP
253 assert(c);
254
255 if (!isempty(c->data[PROP_CHASSIS]))
256 return strappend("computer-", c->data[PROP_CHASSIS]);
7871c8e9
LP
257
258 chassis = fallback_chassis();
259 if (chassis)
260 return strappend("computer-", chassis);
261
262 return strdup("computer");
263}
264
c779a442 265static bool hostname_is_useful(const char *hn) {
fecc80c1 266 return !isempty(hn) && !is_localhost(hn);
c779a442
SW
267}
268
269static int context_update_kernel_hostname(Context *c) {
270 const char *static_hn;
7640a5de
LP
271 const char *hn;
272
66a4c743
LP
273 assert(c);
274
c779a442
SW
275 static_hn = c->data[PROP_STATIC_HOSTNAME];
276
277 /* /etc/hostname with something other than "localhost"
278 * has the highest preference ... */
279 if (hostname_is_useful(static_hn))
280 hn = static_hn;
281
f7340ab2 282 /* ... the transient host name, (ie: DHCP) comes next ... */
c779a442 283 else if (!isempty(c->data[PROP_HOSTNAME]))
66a4c743 284 hn = c->data[PROP_HOSTNAME];
7640a5de 285
c779a442
SW
286 /* ... fallback to static "localhost.*" ignored above ... */
287 else if (!isempty(static_hn))
288 hn = static_hn;
289
290 /* ... and the ultimate fallback */
291 else
8146c32b 292 hn = FALLBACK_HOSTNAME;
c779a442 293
605f81a8 294 if (sethostname_idempotent(hn) < 0)
7640a5de
LP
295 return -errno;
296
7782e0a0
LP
297 (void) nscd_flush_cache(STRV_MAKE("hosts"));
298
7640a5de
LP
299 return 0;
300}
301
66a4c743 302static int context_write_data_static_hostname(Context *c) {
7640a5de 303
66a4c743
LP
304 assert(c);
305
306 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
7640a5de
LP
307
308 if (unlink("/etc/hostname") < 0)
309 return errno == ENOENT ? 0 : -errno;
310
311 return 0;
312 }
66a4c743 313 return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
7640a5de
LP
314}
315
f200e8bb 316static int context_write_data_machine_info(Context *c) {
4f34ed54 317
7640a5de
LP
318 static const char * const name[_PROP_MAX] = {
319 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
7871c8e9 320 [PROP_ICON_NAME] = "ICON_NAME",
799298d6
JG
321 [PROP_CHASSIS] = "CHASSIS",
322 [PROP_DEPLOYMENT] = "DEPLOYMENT",
ce0f1493 323 [PROP_LOCATION] = "LOCATION",
7640a5de
LP
324 };
325
0ccad099 326 _cleanup_strv_free_ char **l = NULL;
7640a5de
LP
327 int r, p;
328
66a4c743
LP
329 assert(c);
330
aa8fbc74 331 r = load_env_file(NULL, "/etc/machine-info", &l);
7640a5de
LP
332 if (r < 0 && r != -ENOENT)
333 return r;
334
ce0f1493 335 for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
d77ab3f7
LP
336 _cleanup_free_ char *t = NULL;
337 char **u;
7640a5de
LP
338
339 assert(name[p]);
340
66a4c743 341 if (isempty(c->data[p])) {
f8440af5 342 strv_env_unset(l, name[p]);
7640a5de
LP
343 continue;
344 }
345
605405c6 346 t = strjoin(name[p], "=", c->data[p]);
d77ab3f7 347 if (!t)
7640a5de 348 return -ENOMEM;
7640a5de
LP
349
350 u = strv_env_set(l, t);
7640a5de
LP
351 if (!u)
352 return -ENOMEM;
0ccad099 353
130d3d22 354 strv_free_and_replace(l, u);
7640a5de
LP
355 }
356
357 if (strv_isempty(l)) {
7640a5de
LP
358 if (unlink("/etc/machine-info") < 0)
359 return errno == ENOENT ? 0 : -errno;
360
361 return 0;
362 }
363
0ccad099 364 return write_env_file_label("/etc/machine-info", l);
7640a5de
LP
365}
366
66a4c743
LP
367static int property_get_icon_name(
368 sd_bus *bus,
369 const char *path,
370 const char *interface,
371 const char *property,
372 sd_bus_message *reply,
ebcf1f97
LP
373 void *userdata,
374 sd_bus_error *error) {
7640a5de 375
66a4c743
LP
376 _cleanup_free_ char *n = NULL;
377 Context *c = userdata;
378 const char *name;
7640a5de 379
66a4c743
LP
380 if (isempty(c->data[PROP_ICON_NAME]))
381 name = n = context_fallback_icon_name(c);
7640a5de 382 else
66a4c743
LP
383 name = c->data[PROP_ICON_NAME];
384
385 if (!name)
386 return -ENOMEM;
7640a5de 387
ebcf1f97 388 return sd_bus_message_append(reply, "s", name);
7640a5de
LP
389}
390
66a4c743
LP
391static int property_get_chassis(
392 sd_bus *bus,
393 const char *path,
394 const char *interface,
395 const char *property,
396 sd_bus_message *reply,
ebcf1f97
LP
397 void *userdata,
398 sd_bus_error *error) {
7871c8e9 399
66a4c743
LP
400 Context *c = userdata;
401 const char *name;
7871c8e9 402
66a4c743 403 if (isempty(c->data[PROP_CHASSIS]))
7871c8e9
LP
404 name = fallback_chassis();
405 else
66a4c743 406 name = c->data[PROP_CHASSIS];
7871c8e9 407
ebcf1f97 408 return sd_bus_message_append(reply, "s", name);
7871c8e9
LP
409}
410
19070062 411static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
412 Context *c = userdata;
413 const char *name;
102d8f81 414 int interactive;
66a4c743 415 int r;
d200735e 416
19070062
LP
417 assert(m);
418 assert(c);
419
66a4c743
LP
420 r = sd_bus_message_read(m, "sb", &name, &interactive);
421 if (r < 0)
ebcf1f97 422 return r;
d200735e 423
66a4c743
LP
424 if (isempty(name))
425 name = c->data[PROP_STATIC_HOSTNAME];
7640a5de 426
66a4c743 427 if (isempty(name))
8146c32b 428 name = FALLBACK_HOSTNAME;
7640a5de 429
8fb49443 430 if (!hostname_is_valid(name, false))
ebcf1f97 431 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
7640a5de 432
66a4c743 433 if (streq_ptr(name, c->data[PROP_HOSTNAME]))
df2d202e 434 return sd_bus_reply_method_return(m, NULL);
7640a5de 435
c529695e
LP
436 r = bus_verify_polkit_async(
437 m,
438 CAP_SYS_ADMIN,
439 "org.freedesktop.hostname1.set-hostname",
403ed0e5 440 NULL,
c529695e
LP
441 interactive,
442 UID_INVALID,
443 &c->polkit_registry,
444 error);
66a4c743 445 if (r < 0)
ebcf1f97 446 return r;
66a4c743
LP
447 if (r == 0)
448 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 449
c650f207
YW
450 r = free_and_strdup(&c->data[PROP_HOSTNAME], name);
451 if (r < 0)
452 return r;
7640a5de 453
c779a442 454 r = context_update_kernel_hostname(c);
66a4c743 455 if (r < 0) {
da927ba9 456 log_error_errno(r, "Failed to set host name: %m");
1b4cd646 457 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
66a4c743 458 }
7640a5de 459
66a4c743 460 log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
7640a5de 461
19070062 462 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
7640a5de 463
df2d202e 464 return sd_bus_reply_method_return(m, NULL);
66a4c743 465}
7640a5de 466
19070062 467static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
468 Context *c = userdata;
469 const char *name;
102d8f81 470 int interactive;
66a4c743 471 int r;
7640a5de 472
19070062
LP
473 assert(m);
474 assert(c);
475
66a4c743
LP
476 r = sd_bus_message_read(m, "sb", &name, &interactive);
477 if (r < 0)
ebcf1f97 478 return r;
7640a5de 479
3c6f7c34 480 name = empty_to_null(name);
7640a5de 481
66a4c743 482 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 483 return sd_bus_reply_method_return(m, NULL);
7640a5de 484
c650f207
YW
485 if (!isempty(name) && !hostname_is_valid(name, false))
486 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
487
c529695e
LP
488 r = bus_verify_polkit_async(
489 m,
490 CAP_SYS_ADMIN,
491 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 492 NULL,
c529695e
LP
493 interactive,
494 UID_INVALID,
495 &c->polkit_registry,
496 error);
66a4c743 497 if (r < 0)
ebcf1f97 498 return r;
66a4c743
LP
499 if (r == 0)
500 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 501
c650f207
YW
502 r = free_and_strdup(&c->data[PROP_STATIC_HOSTNAME], name);
503 if (r < 0)
504 return r;
7640a5de 505
c779a442
SW
506 r = context_update_kernel_hostname(c);
507 if (r < 0) {
da927ba9 508 log_error_errno(r, "Failed to set host name: %m");
1b4cd646 509 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
c779a442
SW
510 }
511
66a4c743
LP
512 r = context_write_data_static_hostname(c);
513 if (r < 0) {
da927ba9 514 log_error_errno(r, "Failed to write static host name: %m");
1b4cd646 515 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 516 }
7640a5de 517
66a4c743 518 log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
7640a5de 519
19070062 520 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
7640a5de 521
df2d202e 522 return sd_bus_reply_method_return(m, NULL);
66a4c743 523}
7640a5de 524
19070062 525static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 526 int interactive;
66a4c743
LP
527 const char *name;
528 int r;
529
530 assert(c);
66a4c743
LP
531 assert(m);
532
533 r = sd_bus_message_read(m, "sb", &name, &interactive);
534 if (r < 0)
ebcf1f97 535 return r;
66a4c743 536
3c6f7c34 537 name = empty_to_null(name);
66a4c743
LP
538
539 if (streq_ptr(name, c->data[prop]))
df2d202e 540 return sd_bus_reply_method_return(m, NULL);
7640a5de 541
c650f207
YW
542 if (!isempty(name)) {
543 /* The icon name might ultimately be used as file
544 * name, so better be safe than sorry */
545
546 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
547 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
548 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
549 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
550 if (prop == PROP_CHASSIS && !valid_chassis(name))
551 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
552 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
553 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
554 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
555 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
556 }
557
66a4c743
LP
558 /* Since the pretty hostname should always be changed at the
559 * same time as the static one, use the same policy action for
560 * both... */
561
c529695e
LP
562 r = bus_verify_polkit_async(
563 m,
564 CAP_SYS_ADMIN,
565 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 566 NULL,
c529695e
LP
567 interactive,
568 UID_INVALID,
569 &c->polkit_registry,
570 error);
66a4c743 571 if (r < 0)
ebcf1f97 572 return r;
66a4c743
LP
573 if (r == 0)
574 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
575
c650f207
YW
576 r = free_and_strdup(&c->data[prop], name);
577 if (r < 0)
578 return r;
7640a5de 579
f200e8bb 580 r = context_write_data_machine_info(c);
66a4c743 581 if (r < 0) {
da927ba9 582 log_error_errno(r, "Failed to write machine info: %m");
1b4cd646 583 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 584 }
7640a5de 585
66a4c743
LP
586 log_info("Changed %s to '%s'",
587 prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
799298d6 588 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 589 prop == PROP_LOCATION ? "location" :
66a4c743 590 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 591
19070062
LP
592 (void) sd_bus_emit_properties_changed(
593 sd_bus_message_get_bus(m),
594 "/org/freedesktop/hostname1",
595 "org.freedesktop.hostname1",
596 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
597 prop == PROP_DEPLOYMENT ? "Deployment" :
598 prop == PROP_LOCATION ? "Location" :
599 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 600
df2d202e 601 return sd_bus_reply_method_return(m, NULL);
66a4c743 602}
7640a5de 603
19070062
LP
604static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
605 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
606}
607
19070062
LP
608static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
609 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
610}
611
19070062
LP
612static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
613 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
614}
615
19070062
LP
616static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
617 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
618}
619
19070062
LP
620static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
621 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
622}
623
21e627da
YW
624static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
625 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
626 Context *c = userdata;
627 int interactive, r;
628
629 assert(m);
630 assert(c);
631
632 if (!c->has_uuid)
633 return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from /sys/class/dmi/id/product_uuid");
634
635 r = sd_bus_message_read(m, "b", &interactive);
636 if (r < 0)
637 return r;
638
639 r = bus_verify_polkit_async(
640 m,
641 CAP_SYS_ADMIN,
642 "org.freedesktop.hostname1.get-product-uuid",
643 NULL,
644 interactive,
645 UID_INVALID,
646 &c->polkit_registry,
647 error);
648 if (r < 0)
649 return r;
650 if (r == 0)
651 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
652
653 r = sd_bus_message_new_method_return(m, &reply);
654 if (r < 0)
655 return r;
656
657 r = sd_bus_message_append_array(reply, 'y', &c->uuid, sizeof(c->uuid));
658 if (r < 0)
659 return r;
660
661 return sd_bus_send(NULL, reply, NULL);
662}
663
66a4c743
LP
664static const sd_bus_vtable hostname_vtable[] = {
665 SD_BUS_VTABLE_START(0),
caffbef6 666 SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
66a4c743
LP
667 SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
668 SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
669 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
670 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
ce0f1493
LP
671 SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
672 SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
f426cc5d
DH
673 SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
674 SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
9be3455f 675 SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
44c32988
DH
676 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
677 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
64928aa5 678 SD_BUS_PROPERTY("HomeURL", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
adacb957
LP
679 SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
680 SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
681 SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
682 SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
683 SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
799298d6 684 SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
ce0f1493 685 SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
21e627da 686 SD_BUS_METHOD("GetProductUUID", "b", "ay", method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED),
66a4c743
LP
687 SD_BUS_VTABLE_END,
688};
689
690static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 691 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
692 int r;
693
66a4c743
LP
694 assert(c);
695 assert(event);
d0baa06f
LP
696 assert(_bus);
697
76b54375 698 r = sd_bus_default_system(&bus);
23bbb0de
MS
699 if (r < 0)
700 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 701
19befb2d 702 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
23bbb0de
MS
703 if (r < 0)
704 return log_error_errno(r, "Failed to register object: %m");
d0baa06f 705
0c0b9306 706 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 707 if (r < 0)
0c0b9306 708 return log_error_errno(r, "Failed to request name: %m");
add10b5a 709
66a4c743 710 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
711 if (r < 0)
712 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 713
1cc6c93a 714 *_bus = TAKE_PTR(bus);
d0baa06f 715
66a4c743 716 return 0;
d0baa06f
LP
717}
718
85ae63ee
YW
719static int run(int argc, char *argv[]) {
720 _cleanup_(context_clear) Context context = {};
4afd3348
LP
721 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
722 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 723 int r;
d0baa06f 724
6bf3c61c 725 log_setup_service();
7640a5de 726
4c12626c 727 umask(0022);
c3dacc8b 728 mac_selinux_init();
4c12626c 729
7640a5de
LP
730 if (argc != 1) {
731 log_error("This program takes no arguments.");
85ae63ee 732 return -EINVAL;
7640a5de
LP
733 }
734
b22c8bfc
YW
735 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
736
afc6adb5 737 r = sd_event_default(&event);
85ae63ee
YW
738 if (r < 0)
739 return log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de 740
b22c8bfc
YW
741 (void) sd_event_set_watchdog(event, true);
742
743 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
85ae63ee
YW
744 if (r < 0)
745 return log_error_errno(r, "Failed to install SIGINT handler: %m");
b22c8bfc
YW
746
747 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
85ae63ee
YW
748 if (r < 0)
749 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
cde93897 750
66a4c743 751 r = connect_bus(&context, event, &bus);
d0baa06f 752 if (r < 0)
85ae63ee 753 return r;
7640a5de 754
66a4c743 755 r = context_read_data(&context);
85ae63ee
YW
756 if (r < 0)
757 return log_error_errno(r, "Failed to read hostname and machine information: %m");
ad740100 758
37224a5f 759 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
85ae63ee
YW
760 if (r < 0)
761 return log_error_errno(r, "Failed to run event loop: %m");
7640a5de 762
85ae63ee 763 return 0;
7640a5de 764}
85ae63ee
YW
765
766DEFINE_MAIN_FUNCTION(run);