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