]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamed.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / hostname / hostnamed.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <string.h>
5 #include <sys/utsname.h>
6 #include <unistd.h>
7
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
10 #include "bus-util.h"
11 #include "def.h"
12 #include "env-file-label.h"
13 #include "env-file.h"
14 #include "env-util.h"
15 #include "fileio-label.h"
16 #include "fileio.h"
17 #include "hostname-util.h"
18 #include "id128-util.h"
19 #include "main-func.h"
20 #include "missing_capability.h"
21 #include "nscd-flush.h"
22 #include "os-util.h"
23 #include "parse-util.h"
24 #include "path-util.h"
25 #include "selinux-util.h"
26 #include "signal-util.h"
27 #include "strv.h"
28 #include "user-util.h"
29 #include "util.h"
30 #include "virt.h"
31
32 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
33
34 enum {
35 PROP_HOSTNAME,
36 PROP_STATIC_HOSTNAME,
37 PROP_PRETTY_HOSTNAME,
38 PROP_ICON_NAME,
39 PROP_CHASSIS,
40 PROP_DEPLOYMENT,
41 PROP_LOCATION,
42 PROP_KERNEL_NAME,
43 PROP_KERNEL_RELEASE,
44 PROP_KERNEL_VERSION,
45 PROP_OS_PRETTY_NAME,
46 PROP_OS_CPE_NAME,
47 PROP_HOME_URL,
48 _PROP_MAX
49 };
50
51 typedef struct Context {
52 char *data[_PROP_MAX];
53 Hashmap *polkit_registry;
54 sd_id128_t uuid;
55 bool has_uuid;
56 } Context;
57
58 static void context_reset(Context *c) {
59 int p;
60
61 assert(c);
62
63 for (p = 0; p < _PROP_MAX; p++)
64 c->data[p] = mfree(c->data[p]);
65 }
66
67 static void context_clear(Context *c) {
68 assert(c);
69
70 context_reset(c);
71 bus_verify_polkit_async_registry_free(c->polkit_registry);
72 }
73
74 static int context_read_data(Context *c) {
75 int r;
76 struct utsname u;
77
78 assert(c);
79
80 context_reset(c);
81
82 assert_se(uname(&u) >= 0);
83 c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
84 c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
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])
88 return -ENOMEM;
89
90 c->data[PROP_HOSTNAME] = gethostname_malloc();
91 if (!c->data[PROP_HOSTNAME])
92 return -ENOMEM;
93
94 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
95 if (r < 0 && r != -ENOENT)
96 return r;
97
98 r = parse_env_file(NULL, "/etc/machine-info",
99 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
100 "ICON_NAME", &c->data[PROP_ICON_NAME],
101 "CHASSIS", &c->data[PROP_CHASSIS],
102 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
103 "LOCATION", &c->data[PROP_LOCATION]);
104 if (r < 0 && r != -ENOENT)
105 return r;
106
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);
112 if (r < 0 && r != -ENOENT)
113 return r;
114
115 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid);
116 if (r < 0)
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;
123
124 return 0;
125 }
126
127 static bool valid_chassis(const char *chassis) {
128 assert(chassis);
129
130 return nulstr_contains(
131 "vm\0"
132 "container\0"
133 "desktop\0"
134 "laptop\0"
135 "convertible\0"
136 "server\0"
137 "tablet\0"
138 "handset\0"
139 "watch\0"
140 "embedded\0",
141 chassis);
142 }
143
144 static bool valid_deployment(const char *deployment) {
145 assert(deployment);
146
147 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
148 }
149
150 static const char* fallback_chassis(void) {
151 char *type;
152 unsigned t;
153 int v, r;
154
155 v = detect_virtualization();
156 if (VIRTUALIZATION_IS_VM(v))
157 return "vm";
158 if (VIRTUALIZATION_IS_CONTAINER(v))
159 return "container";
160
161 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
162 if (r < 0)
163 goto try_acpi;
164
165 r = safe_atou(type, &t);
166 free(type);
167 if (r < 0)
168 goto try_acpi;
169
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
176 */
177
178 switch (t) {
179
180 case 0x3: /* Desktop */
181 case 0x4: /* Low Profile Desktop */
182 case 0x6: /* Mini Tower */
183 case 0x7: /* Tower */
184 return "desktop";
185
186 case 0x8: /* Portable */
187 case 0x9: /* Laptop */
188 case 0xA: /* Notebook */
189 case 0xE: /* Sub Notebook */
190 return "laptop";
191
192 case 0xB: /* Hand Held */
193 return "handset";
194
195 case 0x11: /* Main Server Chassis */
196 case 0x1C: /* Blade */
197 case 0x1D: /* Blade Enclosure */
198 return "server";
199
200 case 0x1E: /* Tablet */
201 return "tablet";
202
203 case 0x1F: /* Convertible */
204 case 0x20: /* Detachable */
205 return "convertible";
206 }
207
208 try_acpi:
209 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
210 if (r < 0)
211 return NULL;
212
213 r = safe_atou(type, &t);
214 free(type);
215 if (r < 0)
216 return NULL;
217
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
223 */
224
225 switch(t) {
226
227 case 1: /* Desktop */
228 case 3: /* Workstation */
229 case 6: /* Appliance PC */
230 return "desktop";
231
232 case 2: /* Mobile */
233 return "laptop";
234
235 case 4: /* Enterprise Server */
236 case 5: /* SOHO Server */
237 case 7: /* Performance Server */
238 return "server";
239
240 case 8: /* Tablet */
241 return "tablet";
242 }
243
244 return NULL;
245 }
246
247 static char* context_fallback_icon_name(Context *c) {
248 const char *chassis;
249
250 assert(c);
251
252 if (!isempty(c->data[PROP_CHASSIS]))
253 return strappend("computer-", c->data[PROP_CHASSIS]);
254
255 chassis = fallback_chassis();
256 if (chassis)
257 return strappend("computer-", chassis);
258
259 return strdup("computer");
260 }
261
262 static bool hostname_is_useful(const char *hn) {
263 return !isempty(hn) && !is_localhost(hn);
264 }
265
266 static int context_update_kernel_hostname(Context *c) {
267 const char *static_hn;
268 const char *hn;
269
270 assert(c);
271
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
279 /* ... the transient host name, (ie: DHCP) comes next ... */
280 else if (!isempty(c->data[PROP_HOSTNAME]))
281 hn = c->data[PROP_HOSTNAME];
282
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
289 hn = FALLBACK_HOSTNAME;
290
291 if (sethostname_idempotent(hn) < 0)
292 return -errno;
293
294 (void) nscd_flush_cache(STRV_MAKE("hosts"));
295
296 return 0;
297 }
298
299 static int context_write_data_static_hostname(Context *c) {
300
301 assert(c);
302
303 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
304
305 if (unlink("/etc/hostname") < 0)
306 return errno == ENOENT ? 0 : -errno;
307
308 return 0;
309 }
310 return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
311 }
312
313 static int context_write_data_machine_info(Context *c) {
314
315 static const char * const name[_PROP_MAX] = {
316 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
317 [PROP_ICON_NAME] = "ICON_NAME",
318 [PROP_CHASSIS] = "CHASSIS",
319 [PROP_DEPLOYMENT] = "DEPLOYMENT",
320 [PROP_LOCATION] = "LOCATION",
321 };
322
323 _cleanup_strv_free_ char **l = NULL;
324 int r, p;
325
326 assert(c);
327
328 r = load_env_file(NULL, "/etc/machine-info", &l);
329 if (r < 0 && r != -ENOENT)
330 return r;
331
332 for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
333 _cleanup_free_ char *t = NULL;
334 char **u;
335
336 assert(name[p]);
337
338 if (isempty(c->data[p])) {
339 strv_env_unset(l, name[p]);
340 continue;
341 }
342
343 t = strjoin(name[p], "=", c->data[p]);
344 if (!t)
345 return -ENOMEM;
346
347 u = strv_env_set(l, t);
348 if (!u)
349 return -ENOMEM;
350
351 strv_free_and_replace(l, u);
352 }
353
354 if (strv_isempty(l)) {
355 if (unlink("/etc/machine-info") < 0)
356 return errno == ENOENT ? 0 : -errno;
357
358 return 0;
359 }
360
361 return write_env_file_label("/etc/machine-info", l);
362 }
363
364 static 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,
370 void *userdata,
371 sd_bus_error *error) {
372
373 _cleanup_free_ char *n = NULL;
374 Context *c = userdata;
375 const char *name;
376
377 if (isempty(c->data[PROP_ICON_NAME]))
378 name = n = context_fallback_icon_name(c);
379 else
380 name = c->data[PROP_ICON_NAME];
381
382 if (!name)
383 return -ENOMEM;
384
385 return sd_bus_message_append(reply, "s", name);
386 }
387
388 static 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,
394 void *userdata,
395 sd_bus_error *error) {
396
397 Context *c = userdata;
398 const char *name;
399
400 if (isempty(c->data[PROP_CHASSIS]))
401 name = fallback_chassis();
402 else
403 name = c->data[PROP_CHASSIS];
404
405 return sd_bus_message_append(reply, "s", name);
406 }
407
408 static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
409 Context *c = userdata;
410 const char *name;
411 int interactive;
412 int r;
413
414 assert(m);
415 assert(c);
416
417 r = sd_bus_message_read(m, "sb", &name, &interactive);
418 if (r < 0)
419 return r;
420
421 if (isempty(name))
422 name = c->data[PROP_STATIC_HOSTNAME];
423
424 if (isempty(name))
425 name = FALLBACK_HOSTNAME;
426
427 if (!hostname_is_valid(name, false))
428 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
429
430 if (streq_ptr(name, c->data[PROP_HOSTNAME]))
431 return sd_bus_reply_method_return(m, NULL);
432
433 r = bus_verify_polkit_async(
434 m,
435 CAP_SYS_ADMIN,
436 "org.freedesktop.hostname1.set-hostname",
437 NULL,
438 interactive,
439 UID_INVALID,
440 &c->polkit_registry,
441 error);
442 if (r < 0)
443 return r;
444 if (r == 0)
445 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
446
447 r = free_and_strdup(&c->data[PROP_HOSTNAME], name);
448 if (r < 0)
449 return r;
450
451 r = context_update_kernel_hostname(c);
452 if (r < 0) {
453 log_error_errno(r, "Failed to set host name: %m");
454 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
455 }
456
457 log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
458
459 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
460
461 return sd_bus_reply_method_return(m, NULL);
462 }
463
464 static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
465 Context *c = userdata;
466 const char *name;
467 int interactive;
468 int r;
469
470 assert(m);
471 assert(c);
472
473 r = sd_bus_message_read(m, "sb", &name, &interactive);
474 if (r < 0)
475 return r;
476
477 name = empty_to_null(name);
478
479 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
480 return sd_bus_reply_method_return(m, NULL);
481
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
485 r = bus_verify_polkit_async(
486 m,
487 CAP_SYS_ADMIN,
488 "org.freedesktop.hostname1.set-static-hostname",
489 NULL,
490 interactive,
491 UID_INVALID,
492 &c->polkit_registry,
493 error);
494 if (r < 0)
495 return r;
496 if (r == 0)
497 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
498
499 r = free_and_strdup(&c->data[PROP_STATIC_HOSTNAME], name);
500 if (r < 0)
501 return r;
502
503 r = context_update_kernel_hostname(c);
504 if (r < 0) {
505 log_error_errno(r, "Failed to set host name: %m");
506 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
507 }
508
509 r = context_write_data_static_hostname(c);
510 if (r < 0) {
511 log_error_errno(r, "Failed to write static host name: %m");
512 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
513 }
514
515 log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
516
517 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
518
519 return sd_bus_reply_method_return(m, NULL);
520 }
521
522 static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
523 int interactive;
524 const char *name;
525 int r;
526
527 assert(c);
528 assert(m);
529
530 r = sd_bus_message_read(m, "sb", &name, &interactive);
531 if (r < 0)
532 return r;
533
534 name = empty_to_null(name);
535
536 if (streq_ptr(name, c->data[prop]))
537 return sd_bus_reply_method_return(m, NULL);
538
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
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
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",
563 NULL,
564 interactive,
565 UID_INVALID,
566 &c->polkit_registry,
567 error);
568 if (r < 0)
569 return r;
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
573 r = free_and_strdup(&c->data[prop], name);
574 if (r < 0)
575 return r;
576
577 r = context_write_data_machine_info(c);
578 if (r < 0) {
579 log_error_errno(r, "Failed to write machine info: %m");
580 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
581 }
582
583 log_info("Changed %s to '%s'",
584 prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
585 prop == PROP_DEPLOYMENT ? "deployment" :
586 prop == PROP_LOCATION ? "location" :
587 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
588
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);
597
598 return sd_bus_reply_method_return(m, NULL);
599 }
600
601 static 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);
603 }
604
605 static 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);
607 }
608
609 static 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);
611 }
612
613 static 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);
615 }
616
617 static 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);
619 }
620
621 static 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
661 static const sd_bus_vtable hostname_vtable[] = {
662 SD_BUS_VTABLE_START(0),
663 SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
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),
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),
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),
672 SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
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),
675 SD_BUS_PROPERTY("HomeURL", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
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),
681 SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
682 SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
683 SD_BUS_METHOD("GetProductUUID", "b", "ay", method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED),
684 SD_BUS_VTABLE_END,
685 };
686
687 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
688 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
689 int r;
690
691 assert(c);
692 assert(event);
693 assert(_bus);
694
695 r = sd_bus_default_system(&bus);
696 if (r < 0)
697 return log_error_errno(r, "Failed to get system bus connection: %m");
698
699 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
700 if (r < 0)
701 return log_error_errno(r, "Failed to register object: %m");
702
703 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
704 if (r < 0)
705 return log_error_errno(r, "Failed to request name: %m");
706
707 r = sd_bus_attach_event(bus, event, 0);
708 if (r < 0)
709 return log_error_errno(r, "Failed to attach bus to event loop: %m");
710
711 *_bus = TAKE_PTR(bus);
712
713 return 0;
714 }
715
716 static int run(int argc, char *argv[]) {
717 _cleanup_(context_clear) Context context = {};
718 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
719 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
720 int r;
721
722 log_setup_service();
723
724 umask(0022);
725 mac_selinux_init();
726
727 if (argc != 1) {
728 log_error("This program takes no arguments.");
729 return -EINVAL;
730 }
731
732 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
733
734 r = sd_event_default(&event);
735 if (r < 0)
736 return log_error_errno(r, "Failed to allocate event loop: %m");
737
738 (void) sd_event_set_watchdog(event, true);
739
740 r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
741 if (r < 0)
742 return log_error_errno(r, "Failed to install SIGINT handler: %m");
743
744 r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
745 if (r < 0)
746 return log_error_errno(r, "Failed to install SIGTERM handler: %m");
747
748 r = connect_bus(&context, event, &bus);
749 if (r < 0)
750 return r;
751
752 r = context_read_data(&context);
753 if (r < 0)
754 return log_error_errno(r, "Failed to read hostname and machine information: %m");
755
756 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
757 if (r < 0)
758 return log_error_errno(r, "Failed to run event loop: %m");
759
760 return 0;
761 }
762
763 DEFINE_MAIN_FUNCTION(run);