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