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