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