chiark / gitweb /
logind: open up most bus calls for unpriviliged processes, using PolicyKit
[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 #include "selinux-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(
431                         m,
432                         CAP_SYS_ADMIN,
433                         "org.freedesktop.hostname1.set-hostname",
434                         interactive,
435                         UID_INVALID,
436                         &c->polkit_registry,
437                         error);
438         if (r < 0)
439                 return r;
440         if (r == 0)
441                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
442
443         h = strdup(name);
444         if (!h)
445                 return -ENOMEM;
446
447         free(c->data[PROP_HOSTNAME]);
448         c->data[PROP_HOSTNAME] = h;
449
450         r = context_update_kernel_hostname(c);
451         if (r < 0) {
452                 log_error_errno(r, "Failed to set host name: %m");
453                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
454         }
455
456         log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
457
458         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
459
460         return sd_bus_reply_method_return(m, NULL);
461 }
462
463 static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
464         Context *c = userdata;
465         const char *name;
466         int interactive;
467         int r;
468
469         r = sd_bus_message_read(m, "sb", &name, &interactive);
470         if (r < 0)
471                 return r;
472
473         if (isempty(name))
474                 name = NULL;
475
476         if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
477                 return sd_bus_reply_method_return(m, NULL);
478
479         r = bus_verify_polkit_async(
480                         m,
481                         CAP_SYS_ADMIN,
482                         "org.freedesktop.hostname1.set-static-hostname",
483                         interactive,
484                         UID_INVALID,
485                         &c->polkit_registry,
486                         error);
487         if (r < 0)
488                 return r;
489         if (r == 0)
490                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
491
492         if (isempty(name)) {
493                 free(c->data[PROP_STATIC_HOSTNAME]);
494                 c->data[PROP_STATIC_HOSTNAME] = NULL;
495         } else {
496                 char *h;
497
498                 if (!hostname_is_valid(name))
499                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
500
501                 h = strdup(name);
502                 if (!h)
503                         return -ENOMEM;
504
505                 free(c->data[PROP_STATIC_HOSTNAME]);
506                 c->data[PROP_STATIC_HOSTNAME] = h;
507         }
508
509         r = context_update_kernel_hostname(c);
510         if (r < 0) {
511                 log_error_errno(r, "Failed to set host name: %m");
512                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r));
513         }
514
515         r = context_write_data_static_hostname(c);
516         if (r < 0) {
517                 log_error_errno(r, "Failed to write static host name: %m");
518                 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r));
519         }
520
521         log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
522
523         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
524
525         return sd_bus_reply_method_return(m, NULL);
526 }
527
528 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) {
529         int interactive;
530         const char *name;
531         int r;
532
533         assert(c);
534         assert(bus);
535         assert(m);
536
537         r = sd_bus_message_read(m, "sb", &name, &interactive);
538         if (r < 0)
539                 return r;
540
541         if (isempty(name))
542                 name = NULL;
543
544         if (streq_ptr(name, c->data[prop]))
545                 return sd_bus_reply_method_return(m, NULL);
546
547         /* Since the pretty hostname should always be changed at the
548          * same time as the static one, use the same policy action for
549          * both... */
550
551         r = bus_verify_polkit_async(
552                         m,
553                         CAP_SYS_ADMIN,
554                         prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
555                         interactive,
556                         UID_INVALID,
557                         &c->polkit_registry,
558                         error);
559         if (r < 0)
560                 return r;
561         if (r == 0)
562                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
563
564         if (isempty(name)) {
565                 free(c->data[prop]);
566                 c->data[prop] = NULL;
567         } else {
568                 char *h;
569
570                 /* The icon name might ultimately be used as file
571                  * name, so better be safe than sorry */
572
573                 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
574                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
575                 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
576                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
577                 if (prop == PROP_CHASSIS && !valid_chassis(name))
578                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
579                 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
580                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
581                 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
582                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
583
584                 h = strdup(name);
585                 if (!h)
586                         return -ENOMEM;
587
588                 free(c->data[prop]);
589                 c->data[prop] = h;
590         }
591
592         r = context_write_data_machine_info(c);
593         if (r < 0) {
594                 log_error_errno(r, "Failed to write machine info: %m");
595                 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r));
596         }
597
598         log_info("Changed %s to '%s'",
599                  prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
600                  prop == PROP_DEPLOYMENT ? "deployment" :
601                  prop == PROP_LOCATION ? "location" :
602                  prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
603
604         sd_bus_emit_properties_changed(bus, "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
605                                        prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
606                                        prop == PROP_DEPLOYMENT ? "Deployment" :
607                                        prop == PROP_LOCATION ? "Location" :
608                                        prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
609
610         return sd_bus_reply_method_return(m, NULL);
611 }
612
613 static int method_set_pretty_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
614         return set_machine_info(userdata, bus, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
615 }
616
617 static int method_set_icon_name(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
618         return set_machine_info(userdata, bus, m, PROP_ICON_NAME, method_set_icon_name, error);
619 }
620
621 static int method_set_chassis(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
622         return set_machine_info(userdata, bus, m, PROP_CHASSIS, method_set_chassis, error);
623 }
624
625 static int method_set_deployment(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
626         return set_machine_info(userdata, bus, m, PROP_DEPLOYMENT, method_set_deployment, error);
627 }
628
629 static int method_set_location(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
630         return set_machine_info(userdata, bus, m, PROP_LOCATION, method_set_location, error);
631 }
632
633 static const sd_bus_vtable hostname_vtable[] = {
634         SD_BUS_VTABLE_START(0),
635         SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
636         SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
637         SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
638         SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
639         SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
640         SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
641         SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
642         SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
643         SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
644         SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
645         SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
646         SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
647         SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
648         SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
649         SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
650         SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
651         SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
652         SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
653         SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
654         SD_BUS_VTABLE_END,
655 };
656
657 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
658         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
659         int r;
660
661         assert(c);
662         assert(event);
663         assert(_bus);
664
665         r = sd_bus_default_system(&bus);
666         if (r < 0)
667                 return log_error_errno(r, "Failed to get system bus connection: %m");
668
669         r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
670         if (r < 0)
671                 return log_error_errno(r, "Failed to register object: %m");
672
673         r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0);
674         if (r < 0)
675                 return log_error_errno(r, "Failed to register name: %m");
676
677         r = sd_bus_attach_event(bus, event, 0);
678         if (r < 0)
679                 return log_error_errno(r, "Failed to attach bus to event loop: %m");
680
681         *_bus = bus;
682         bus = NULL;
683
684         return 0;
685 }
686
687 int main(int argc, char *argv[]) {
688         Context context = {};
689         _cleanup_event_unref_ sd_event *event = NULL;
690         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
691         int r;
692
693         log_set_target(LOG_TARGET_AUTO);
694         log_parse_environment();
695         log_open();
696
697         umask(0022);
698         mac_selinux_init("/etc");
699
700         if (argc != 1) {
701                 log_error("This program takes no arguments.");
702                 r = -EINVAL;
703                 goto finish;
704         }
705
706         if (argc != 1) {
707                 log_error("This program takes no arguments.");
708                 r = -EINVAL;
709                 goto finish;
710         }
711
712         r = sd_event_default(&event);
713         if (r < 0) {
714                 log_error_errno(r, "Failed to allocate event loop: %m");
715                 goto finish;
716         }
717
718         sd_event_set_watchdog(event, true);
719
720         r = connect_bus(&context, event, &bus);
721         if (r < 0)
722                 goto finish;
723
724         r = context_read_data(&context);
725         if (r < 0) {
726                 log_error_errno(r, "Failed to read hostname and machine information: %m");
727                 goto finish;
728         }
729
730         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
731         if (r < 0) {
732                 log_error_errno(r, "Failed to run event loop: %m");
733                 goto finish;
734         }
735
736 finish:
737         context_free(&context);
738
739         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
740 }