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