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