chiark / gitweb /
sd-event: name event sources used in libraries
[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 #include <sys/capability.h>
27
28 #include "util.h"
29 #include "strv.h"
30 #include "def.h"
31 #include "virt.h"
32 #include "env-util.h"
33 #include "fileio-label.h"
34 #include "label.h"
35 #include "bus-util.h"
36 #include "event-util.h"
37
38 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
39
40 enum {
41         PROP_HOSTNAME,
42         PROP_STATIC_HOSTNAME,
43         PROP_PRETTY_HOSTNAME,
44         PROP_ICON_NAME,
45         PROP_CHASSIS,
46         PROP_DEPLOYMENT,
47         PROP_LOCATION,
48         PROP_KERNEL_NAME,
49         PROP_KERNEL_RELEASE,
50         PROP_KERNEL_VERSION,
51         PROP_OS_PRETTY_NAME,
52         PROP_OS_CPE_NAME,
53         _PROP_MAX
54 };
55
56 typedef struct Context {
57         char *data[_PROP_MAX];
58         Hashmap *polkit_registry;
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                 free(c->data[p]);
68                 c->data[p] = NULL;
69         }
70 }
71
72 static void context_free(Context *c) {
73         assert(c);
74
75         context_reset(c);
76         bus_verify_polkit_async_registry_free(c->polkit_registry);
77 }
78
79 static int context_read_data(Context *c) {
80         int r;
81         struct utsname u;
82
83         assert(c);
84
85         context_reset(c);
86
87         assert_se(uname(&u) >= 0);
88         c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
89         c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
90         c->data[PROP_KERNEL_VERSION] = strdup(u.version);
91         if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] ||
92             !c->data[PROP_KERNEL_VERSION])
93                 return -ENOMEM;
94
95         c->data[PROP_HOSTNAME] = gethostname_malloc();
96         if (!c->data[PROP_HOSTNAME])
97                 return -ENOMEM;
98
99         r = read_one_line_file("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
100         if (r < 0 && r != -ENOENT)
101                 return r;
102
103         r = parse_env_file("/etc/machine-info", NEWLINE,
104                            "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
105                            "ICON_NAME", &c->data[PROP_ICON_NAME],
106                            "CHASSIS", &c->data[PROP_CHASSIS],
107                            "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
108                            "LOCATION", &c->data[PROP_LOCATION],
109                            NULL);
110         if (r < 0 && r != -ENOENT)
111                 return r;
112
113         r = parse_env_file("/etc/os-release", NEWLINE,
114                            "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
115                            "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
116                            NULL);
117         if (r == -ENOENT) {
118                 r = parse_env_file("/usr/lib/os-release", NEWLINE,
119                                    "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
120                                    "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
121                                    NULL);
122         }
123
124         if (r < 0 && r != -ENOENT)
125                 return r;
126
127         return 0;
128 }
129
130 static bool valid_chassis(const char *chassis) {
131         assert(chassis);
132
133         return nulstr_contains(
134                         "vm\0"
135                         "container\0"
136                         "desktop\0"
137                         "laptop\0"
138                         "server\0"
139                         "tablet\0"
140                         "handset\0"
141                         "watch\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(hn, strlen(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(m, CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-hostname", interactive, &c->polkit_registry, error);
430         if (r < 0)
431                 return r;
432         if (r == 0)
433                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
434
435         h = strdup(name);
436         if (!h)
437                 return -ENOMEM;
438
439         free(c->data[PROP_HOSTNAME]);
440         c->data[PROP_HOSTNAME] = h;
441
442         r = context_update_kernel_hostname(c);
443         if (r < 0) {
444                 log_error("Failed to set host name: %s", strerror(-r));
445                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
446         }
447
448         log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
449
450         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
451
452         return sd_bus_reply_method_return(m, NULL);
453 }
454
455 static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
456         Context *c = userdata;
457         const char *name;
458         int interactive;
459         int r;
460
461         r = sd_bus_message_read(m, "sb", &name, &interactive);
462         if (r < 0)
463                 return r;
464
465         if (isempty(name))
466                 name = NULL;
467
468         if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
469                 return sd_bus_reply_method_return(m, NULL);
470
471         r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-static-hostname", interactive, &c->polkit_registry, error);
472         if (r < 0)
473                 return r;
474         if (r == 0)
475                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
476
477         if (isempty(name)) {
478                 free(c->data[PROP_STATIC_HOSTNAME]);
479                 c->data[PROP_STATIC_HOSTNAME] = NULL;
480         } else {
481                 char *h;
482
483                 if (!hostname_is_valid(name))
484                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
485
486                 h = strdup(name);
487                 if (!h)
488                         return -ENOMEM;
489
490                 free(c->data[PROP_STATIC_HOSTNAME]);
491                 c->data[PROP_STATIC_HOSTNAME] = h;
492         }
493
494         r = context_update_kernel_hostname(c);
495         if (r < 0) {
496                 log_error("Failed to set host name: %s", strerror(-r));
497                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
498         }
499
500         r = context_write_data_static_hostname(c);
501         if (r < 0) {
502                 log_error("Failed to write static host name: %s", strerror(-r));
503                 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r));
504         }
505
506         log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
507
508         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
509
510         return sd_bus_reply_method_return(m, NULL);
511 }
512
513 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) {
514         int interactive;
515         const char *name;
516         int r;
517
518         assert(c);
519         assert(bus);
520         assert(m);
521
522         r = sd_bus_message_read(m, "sb", &name, &interactive);
523         if (r < 0)
524                 return r;
525
526         if (isempty(name))
527                 name = NULL;
528
529         if (streq_ptr(name, c->data[prop]))
530                 return sd_bus_reply_method_return(m, NULL);
531
532         /* Since the pretty hostname should always be changed at the
533          * same time as the static one, use the same policy action for
534          * both... */
535
536         r = bus_verify_polkit_async(m, CAP_SYS_ADMIN,
537                                     prop == PROP_PRETTY_HOSTNAME ?
538                                     "org.freedesktop.hostname1.set-static-hostname" :
539                                     "org.freedesktop.hostname1.set-machine-info", interactive, &c->polkit_registry, error);
540         if (r < 0)
541                 return r;
542         if (r == 0)
543                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
544
545         if (isempty(name)) {
546                 free(c->data[prop]);
547                 c->data[prop] = NULL;
548         } else {
549                 char *h;
550
551                 /* The icon name might ultimately be used as file
552                  * name, so better be safe than sorry */
553
554                 if (prop == PROP_ICON_NAME && !filename_is_safe(name))
555                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
556                 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
557                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
558                 if (prop == PROP_CHASSIS && !valid_chassis(name))
559                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
560                 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
561                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
562                 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
563                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
564
565                 h = strdup(name);
566                 if (!h)
567                         return -ENOMEM;
568
569                 free(c->data[prop]);
570                 c->data[prop] = h;
571         }
572
573         r = context_write_data_machine_info(c);
574         if (r < 0) {
575                 log_error("Failed to write machine info: %s", strerror(-r));
576                 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r));
577         }
578
579         log_info("Changed %s to '%s'",
580                  prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
581                  prop == PROP_DEPLOYMENT ? "deployment" :
582                  prop == PROP_LOCATION ? "location" :
583                  prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
584
585         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
586                                        prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
587                                        prop == PROP_DEPLOYMENT ? "Deployment" :
588                                        prop == PROP_LOCATION ? "Location" :
589                                        prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
590
591         return sd_bus_reply_method_return(m, NULL);
592 }
593
594 static int method_set_pretty_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
595         return set_machine_info(userdata, bus, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
596 }
597
598 static int method_set_icon_name(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
599         return set_machine_info(userdata, bus, m, PROP_ICON_NAME, method_set_icon_name, error);
600 }
601
602 static int method_set_chassis(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
603         return set_machine_info(userdata, bus, m, PROP_CHASSIS, method_set_chassis, error);
604 }
605
606 static int method_set_deployment(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
607         return set_machine_info(userdata, bus, m, PROP_DEPLOYMENT, method_set_deployment, error);
608 }
609
610 static int method_set_location(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
611         return set_machine_info(userdata, bus, m, PROP_LOCATION, method_set_location, error);
612 }
613
614 static const sd_bus_vtable hostname_vtable[] = {
615         SD_BUS_VTABLE_START(0),
616         SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, 0),
617         SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
618         SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
619         SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
620         SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
621         SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
622         SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
623         SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
624         SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
625         SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
626         SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
627         SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
628         SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
629         SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
630         SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
631         SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
632         SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
633         SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
634         SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
635         SD_BUS_VTABLE_END,
636 };
637
638 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
639         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
640         int r;
641
642         assert(c);
643         assert(event);
644         assert(_bus);
645
646         r = sd_bus_default_system(&bus);
647         if (r < 0) {
648                 log_error("Failed to get system bus connection: %s", strerror(-r));
649                 return r;
650         }
651
652         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
653         if (r < 0) {
654                 log_error("Failed to register object: %s", strerror(-r));
655                 return r;
656         }
657
658         r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0);
659         if (r < 0) {
660                 log_error("Failed to register name: %s", strerror(-r));
661                 return r;
662         }
663
664         r = sd_bus_attach_event(bus, event, 0);
665         if (r < 0) {
666                 log_error("Failed to attach bus to event loop: %s", strerror(-r));
667                 return r;
668         }
669
670         *_bus = bus;
671         bus = NULL;
672
673         return 0;
674 }
675
676 int main(int argc, char *argv[]) {
677         Context context = {};
678         _cleanup_event_unref_ sd_event *event = NULL;
679         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
680         int r;
681
682         log_set_target(LOG_TARGET_AUTO);
683         log_parse_environment();
684         log_open();
685
686         umask(0022);
687         label_init("/etc");
688
689         if (argc != 1) {
690                 log_error("This program takes no arguments.");
691                 r = -EINVAL;
692                 goto finish;
693         }
694
695         if (argc != 1) {
696                 log_error("This program takes no arguments.");
697                 r = -EINVAL;
698                 goto finish;
699         }
700
701         r = sd_event_default(&event);
702         if (r < 0) {
703                 log_error("Failed to allocate event loop: %s", strerror(-r));
704                 goto finish;
705         }
706
707         sd_event_set_watchdog(event, true);
708
709         r = connect_bus(&context, event, &bus);
710         if (r < 0)
711                 goto finish;
712
713         r = context_read_data(&context);
714         if (r < 0) {
715                 log_error("Failed to read hostname and machine information: %s", strerror(-r));
716                 goto finish;
717         }
718
719         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
720         if (r < 0) {
721                 log_error("Failed to run event loop: %s", strerror(-r));
722                 goto finish;
723         }
724
725 finish:
726         context_free(&context);
727
728         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
729 }