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