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