chiark / gitweb /
bus: always explicitly close bus from main programs
[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 "label.h"
34 #include "bus-util.h"
35 #include "event-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, sd_bus *bus) {
72         assert(c);
73
74         context_reset(c);
75         bus_verify_polkit_async_registry_free(bus, 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                         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         int r;
152         char *type;
153         unsigned t;
154         int v;
155
156         v = detect_virtualization(NULL);
157
158         if (v == VIRTUALIZATION_VM)
159                 return "vm";
160         if (v == VIRTUALIZATION_CONTAINER)
161                 return "container";
162
163         r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
164         if (r < 0)
165                 goto try_dmi;
166
167         r = safe_atou(type, &t);
168         free(type);
169         if (r < 0)
170                 goto try_dmi;
171
172         /* We only list the really obvious cases here as the ACPI data
173          * is not really super reliable.
174          *
175          * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
176          *
177          * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
178          */
179
180         switch(t) {
181
182         case 1:
183         case 3:
184         case 6:
185                 return "desktop";
186
187         case 2:
188                 return "laptop";
189
190         case 4:
191         case 5:
192         case 7:
193                 return "server";
194
195         case 8:
196                 return "tablet";
197         }
198
199 try_dmi:
200         r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
201         if (r < 0)
202                 return NULL;
203
204         r = safe_atou(type, &t);
205         free(type);
206         if (r < 0)
207                 return NULL;
208
209         /* We only list the really obvious cases here. The DMI data is
210            unreliable enough, so let's not do any additional guesswork
211            on top of that.
212
213            See the SMBIOS Specification 2.7.1 section 7.4.1 for
214            details about the values listed here:
215
216            http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
217          */
218
219         switch (t) {
220
221         case 0x3:
222         case 0x4:
223         case 0x6:
224         case 0x7:
225                 return "desktop";
226
227         case 0x8:
228         case 0x9:
229         case 0xA:
230         case 0xE:
231                 return "laptop";
232
233         case 0xB:
234                 return "handset";
235
236         case 0x11:
237         case 0x1C:
238                 return "server";
239         }
240
241         return NULL;
242 }
243
244 static char* context_fallback_icon_name(Context *c) {
245         const char *chassis;
246
247         assert(c);
248
249         if (!isempty(c->data[PROP_CHASSIS]))
250                 return strappend("computer-", c->data[PROP_CHASSIS]);
251
252         chassis = fallback_chassis();
253         if (chassis)
254                 return strappend("computer-", chassis);
255
256         return strdup("computer");
257 }
258
259
260 static bool hostname_is_useful(const char *hn) {
261         return !isempty(hn) && !is_localhost(hn);
262 }
263
264 static int context_update_kernel_hostname(Context *c) {
265         const char *static_hn;
266         const char *hn;
267
268         assert(c);
269
270         static_hn = c->data[PROP_STATIC_HOSTNAME];
271
272         /* /etc/hostname with something other than "localhost"
273          * has the highest preference ... */
274         if (hostname_is_useful(static_hn))
275                 hn = static_hn;
276
277         /* ... the transient host name, (ie: DHCP) comes next ...*/
278         else if (!isempty(c->data[PROP_HOSTNAME]))
279                 hn = c->data[PROP_HOSTNAME];
280
281         /* ... fallback to static "localhost.*" ignored above ... */
282         else if (!isempty(static_hn))
283                 hn = static_hn;
284
285         /* ... and the ultimate fallback */
286         else
287                 hn = "localhost";
288
289         if (sethostname(hn, strlen(hn)) < 0)
290                 return -errno;
291
292         return 0;
293 }
294
295 static int context_write_data_static_hostname(Context *c) {
296
297         assert(c);
298
299         if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
300
301                 if (unlink("/etc/hostname") < 0)
302                         return errno == ENOENT ? 0 : -errno;
303
304                 return 0;
305         }
306         return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
307 }
308
309 static int context_write_data_machine_info(Context *c) {
310
311         static const char * const name[_PROP_MAX] = {
312                 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
313                 [PROP_ICON_NAME] = "ICON_NAME",
314                 [PROP_CHASSIS] = "CHASSIS",
315                 [PROP_DEPLOYMENT] = "DEPLOYMENT",
316                 [PROP_LOCATION] = "LOCATION",
317         };
318
319         _cleanup_strv_free_ char **l = NULL;
320         int r, p;
321
322         assert(c);
323
324         r = load_env_file(NULL, "/etc/machine-info", NULL, &l);
325         if (r < 0 && r != -ENOENT)
326                 return r;
327
328         for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
329                 _cleanup_free_ char *t = NULL;
330                 char **u;
331
332                 assert(name[p]);
333
334                 if (isempty(c->data[p]))  {
335                         strv_env_unset(l, name[p]);
336                         continue;
337                 }
338
339                 t = strjoin(name[p], "=", c->data[p], NULL);
340                 if (!t)
341                         return -ENOMEM;
342
343                 u = strv_env_set(l, t);
344                 if (!u)
345                         return -ENOMEM;
346
347                 strv_free(l);
348                 l = u;
349         }
350
351         if (strv_isempty(l)) {
352                 if (unlink("/etc/machine-info") < 0)
353                         return errno == ENOENT ? 0 : -errno;
354
355                 return 0;
356         }
357
358         return write_env_file_label("/etc/machine-info", l);
359 }
360
361 static int property_get_icon_name(
362                 sd_bus *bus,
363                 const char *path,
364                 const char *interface,
365                 const char *property,
366                 sd_bus_message *reply,
367                 void *userdata,
368                 sd_bus_error *error) {
369
370         _cleanup_free_ char *n = NULL;
371         Context *c = userdata;
372         const char *name;
373
374         if (isempty(c->data[PROP_ICON_NAME]))
375                 name = n = context_fallback_icon_name(c);
376         else
377                 name = c->data[PROP_ICON_NAME];
378
379         if (!name)
380                 return -ENOMEM;
381
382         return sd_bus_message_append(reply, "s", name);
383 }
384
385 static int property_get_chassis(
386                 sd_bus *bus,
387                 const char *path,
388                 const char *interface,
389                 const char *property,
390                 sd_bus_message *reply,
391                 void *userdata,
392                 sd_bus_error *error) {
393
394         Context *c = userdata;
395         const char *name;
396
397         if (isempty(c->data[PROP_CHASSIS]))
398                 name = fallback_chassis();
399         else
400                 name = c->data[PROP_CHASSIS];
401
402         return sd_bus_message_append(reply, "s", name);
403 }
404
405 static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
406         Context *c = userdata;
407         const char *name;
408         int interactive;
409         char *h;
410         int r;
411
412         r = sd_bus_message_read(m, "sb", &name, &interactive);
413         if (r < 0)
414                 return r;
415
416         if (isempty(name))
417                 name = c->data[PROP_STATIC_HOSTNAME];
418
419         if (isempty(name))
420                 name = "localhost";
421
422         if (!hostname_is_valid(name))
423                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
424
425         if (streq_ptr(name, c->data[PROP_HOSTNAME]))
426                 return sd_bus_reply_method_return(m, NULL);
427
428         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-hostname", interactive, error, method_set_hostname, c);
429         if (r < 0)
430                 return r;
431         if (r == 0)
432                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
433
434         h = strdup(name);
435         if (!h)
436                 return -ENOMEM;
437
438         free(c->data[PROP_HOSTNAME]);
439         c->data[PROP_HOSTNAME] = h;
440
441         r = context_update_kernel_hostname(c);
442         if (r < 0) {
443                 log_error("Failed to set host name: %s", strerror(-r));
444                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
445         }
446
447         log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
448
449         sd_bus_emit_properties_changed(bus, "/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 *bus, 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         r = sd_bus_message_read(m, "sb", &name, &interactive);
461         if (r < 0)
462                 return r;
463
464         if (isempty(name))
465                 name = NULL;
466
467         if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
468                 return sd_bus_reply_method_return(m, NULL);
469
470         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, "org.freedesktop.hostname1.set-static-hostname", interactive, error, method_set_static_hostname, c);
471         if (r < 0)
472                 return r;
473         if (r == 0)
474                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
475
476         if (isempty(name)) {
477                 free(c->data[PROP_STATIC_HOSTNAME]);
478                 c->data[PROP_STATIC_HOSTNAME] = NULL;
479         } else {
480                 char *h;
481
482                 if (!hostname_is_valid(name))
483                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
484
485                 h = strdup(name);
486                 if (!h)
487                         return -ENOMEM;
488
489                 free(c->data[PROP_STATIC_HOSTNAME]);
490                 c->data[PROP_STATIC_HOSTNAME] = h;
491         }
492
493         r = context_update_kernel_hostname(c);
494         if (r < 0) {
495                 log_error("Failed to set host name: %s", strerror(-r));
496                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
497         }
498
499         r = context_write_data_static_hostname(c);
500         if (r < 0) {
501                 log_error("Failed to write static host name: %s", strerror(-r));
502                 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r));
503         }
504
505         log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
506
507         sd_bus_emit_properties_changed(bus, "/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 *bus, 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(bus);
519         assert(m);
520
521         r = sd_bus_message_read(m, "sb", &name, &interactive);
522         if (r < 0)
523                 return r;
524
525         if (isempty(name))
526                 name = NULL;
527
528         if (streq_ptr(name, c->data[prop]))
529                 return sd_bus_reply_method_return(m, NULL);
530
531         /* Since the pretty hostname should always be changed at the
532          * same time as the static one, use the same policy action for
533          * both... */
534
535         r = bus_verify_polkit_async(bus, &c->polkit_registry, m, prop == PROP_PRETTY_HOSTNAME ?
536                           "org.freedesktop.hostname1.set-static-hostname" :
537                           "org.freedesktop.hostname1.set-machine-info", interactive, error, cb, c);
538         if (r < 0)
539                 return r;
540         if (r == 0)
541                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
542
543         if (isempty(name)) {
544                 free(c->data[prop]);
545                 c->data[prop] = NULL;
546         } else {
547                 char *h;
548
549                 /* The icon name might ultimately be used as file
550                  * name, so better be safe than sorry */
551
552                 if (prop == PROP_ICON_NAME && !filename_is_safe(name))
553                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
554                 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
555                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
556                 if (prop == PROP_CHASSIS && !valid_chassis(name))
557                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
558                 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
559                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
560                 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
561                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
562
563                 h = strdup(name);
564                 if (!h)
565                         return -ENOMEM;
566
567                 free(c->data[prop]);
568                 c->data[prop] = h;
569         }
570
571         r = context_write_data_machine_info(c);
572         if (r < 0) {
573                 log_error("Failed to write machine info: %s", strerror(-r));
574                 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r));
575         }
576
577         log_info("Changed %s to '%s'",
578                  prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
579                  prop == PROP_DEPLOYMENT ? "deployment" :
580                  prop == PROP_LOCATION ? "location" :
581                  prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
582
583         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
584                                        prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
585                                        prop == PROP_DEPLOYMENT ? "Deployment" :
586                                        prop == PROP_LOCATION ? "Location" :
587                                        prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
588
589         return sd_bus_reply_method_return(m, NULL);
590 }
591
592 static int method_set_pretty_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
593         return set_machine_info(userdata, bus, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
594 }
595
596 static int method_set_icon_name(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
597         return set_machine_info(userdata, bus, m, PROP_ICON_NAME, method_set_icon_name, error);
598 }
599
600 static int method_set_chassis(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
601         return set_machine_info(userdata, bus, m, PROP_CHASSIS, method_set_chassis, error);
602 }
603
604 static int method_set_deployment(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
605         return set_machine_info(userdata, bus, m, PROP_DEPLOYMENT, method_set_deployment, error);
606 }
607
608 static int method_set_location(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
609         return set_machine_info(userdata, bus, m, PROP_LOCATION, method_set_location, error);
610 }
611
612 static const sd_bus_vtable hostname_vtable[] = {
613         SD_BUS_VTABLE_START(0),
614         SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, 0),
615         SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
616         SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
617         SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
618         SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
619         SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
620         SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
621         SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
622         SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
623         SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
624         SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
625         SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
626         SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
627         SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
628         SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
629         SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
630         SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
631         SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
632         SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
633         SD_BUS_VTABLE_END,
634 };
635
636 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
637         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
638         int r;
639
640         assert(c);
641         assert(event);
642         assert(_bus);
643
644         r = sd_bus_default_system(&bus);
645         if (r < 0) {
646                 log_error("Failed to get system bus connection: %s", strerror(-r));
647                 return r;
648         }
649
650         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
651         if (r < 0) {
652                 log_error("Failed to register object: %s", strerror(-r));
653                 return r;
654         }
655
656         r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0);
657         if (r < 0) {
658                 log_error("Failed to register name: %s", strerror(-r));
659                 return r;
660         }
661
662         r = sd_bus_attach_event(bus, event, 0);
663         if (r < 0) {
664                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
665                 return r;
666         }
667
668         *_bus = bus;
669         bus = NULL;
670
671         return 0;
672 }
673
674 int main(int argc, char *argv[]) {
675         Context context = {};
676         _cleanup_event_unref_ sd_event *event = NULL;
677         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
678         int r;
679
680         log_set_target(LOG_TARGET_AUTO);
681         log_parse_environment();
682         log_open();
683
684         umask(0022);
685         label_init("/etc");
686
687         if (argc != 1) {
688                 log_error("This program takes no arguments.");
689                 r = -EINVAL;
690                 goto finish;
691         }
692
693         if (argc != 1) {
694                 log_error("This program takes no arguments.");
695                 r = -EINVAL;
696                 goto finish;
697         }
698
699         r = sd_event_default(&event);
700         if (r < 0) {
701                 log_error("Failed to allocate event loop: %s", strerror(-r));
702                 goto finish;
703         }
704
705         sd_event_set_watchdog(event, true);
706
707         r = connect_bus(&context, event, &bus);
708         if (r < 0)
709                 goto finish;
710
711         r = context_read_data(&context);
712         if (r < 0) {
713                 log_error("Failed to read hostname and machine information: %s", strerror(-r));
714                 goto finish;
715         }
716
717         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
718         if (r < 0) {
719                 log_error("Failed to run event loop: %s", strerror(-r));
720                 goto finish;
721         }
722
723 finish:
724         context_free(&context, bus);
725
726         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
727 }