chiark / gitweb /
hostnamed: SetPrettyHostname() should check PK action org.freedesktop.hostname1.set...
[elogind.git] / src / 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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dbus/dbus.h>
23
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "util.h"
29 #include "strv.h"
30 #include "dbus-common.h"
31
32 #define INTROSPECTION                                                   \
33         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
34         "<node>\n"                                                      \
35         " <interface name=\"org.freedesktop.hostname1\">\n"             \
36         "  <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n"  \
37         "  <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
38         "  <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
39         "  <property name=\"IconName\" type=\"s\" access=\"read\"/>\n"  \
40         "  <method name=\"SetHostname\">\n"                             \
41         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
42         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
43         "  </method>\n"                                                 \
44         "  <method name=\"SetStaticHostname\">\n"                       \
45         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
46         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
47         "  </method>\n"                                                 \
48         "  <method name=\"SetPrettyHostname\">\n"                       \
49         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
50         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
51         "  </method>\n"                                                 \
52         "  <method name=\"SetIconName\">\n"                             \
53         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
54         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
55         "  </method>\n"                                                 \
56         " </interface>\n"                                               \
57         BUS_PROPERTIES_INTERFACE                                        \
58         BUS_INTROSPECTABLE_INTERFACE                                    \
59         BUS_PEER_INTERFACE                                              \
60         "</node>\n"
61
62 #define INTERFACES_LIST                         \
63         BUS_GENERIC_INTERFACES_LIST             \
64         "org.freedesktop.hostname1\0"
65
66 enum {
67         PROP_HOSTNAME,
68         PROP_STATIC_HOSTNAME,
69         PROP_PRETTY_HOSTNAME,
70         PROP_ICON_NAME,
71         _PROP_MAX
72 };
73
74 static char *data[_PROP_MAX] = {
75         NULL,
76         NULL,
77         NULL,
78         NULL
79 };
80
81 static void free_data(void) {
82         int p;
83
84         for (p = 0; p < _PROP_MAX; p++) {
85                 free(data[p]);
86                 data[p] = NULL;
87         }
88 }
89
90 static int read_data(void) {
91         int r;
92
93         free_data();
94
95         data[PROP_HOSTNAME] = gethostname_malloc();
96         if (!data[PROP_HOSTNAME])
97                 return -ENOMEM;
98
99         r = read_one_line_file("/etc/hostname", &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", &data[PROP_PRETTY_HOSTNAME],
105                            "ICON_NAME", &data[PROP_ICON_NAME],
106                            NULL);
107         if (r < 0 && r != -ENOENT)
108                 return r;
109
110         return 0;
111 }
112
113 static const char* fallback_icon_name(void) {
114
115 #if defined(__i386__) || defined(__x86_64__)
116         int r;
117         char *type;
118         unsigned t;
119 #endif
120
121         if (detect_virtualization(NULL) > 0)
122                 return "computer-vm";
123
124 #if defined(__i386__) || defined(__x86_64__)
125         r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
126         if (r < 0)
127                 return NULL;
128
129         r = safe_atou(type, &t);
130         free(type);
131
132         if (r < 0)
133                 return NULL;
134
135         /* We only list the really obvious cases here. The DMI data is
136            unreliable enough, so let's not do any additional guesswork
137            on top of that.
138
139            See the SMBIOS Specification 2.7.1 section 7.4.1 for
140            details about the values listed here:
141
142            http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
143          */
144
145         switch (t) {
146
147         case 0x3:
148         case 0x4:
149         case 0x6:
150         case 0x7:
151                 return "computer-desktop";
152
153         case 0x9:
154         case 0xA:
155         case 0xE:
156                 return "computer-laptop";
157
158         case 0x11:
159         case 0x1C:
160                 return "computer-server";
161         }
162
163 #endif
164         return NULL;
165 }
166
167 static int write_data_hostname(void) {
168         const char *hn;
169
170         if (isempty(data[PROP_HOSTNAME]))
171                 hn = "localhost";
172         else
173                 hn = data[PROP_HOSTNAME];
174
175         if (sethostname(hn, strlen(hn)) < 0)
176                 return -errno;
177
178         return 0;
179 }
180
181 static int write_data_static_hostname(void) {
182
183         if (isempty(data[PROP_STATIC_HOSTNAME])) {
184
185                 if (unlink("/etc/hostname") < 0)
186                         return errno == ENOENT ? 0 : -errno;
187
188                 return 0;
189         }
190
191         return write_one_line_file("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
192 }
193
194 static int write_data_other(void) {
195
196         static const char * const name[_PROP_MAX] = {
197                 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
198                 [PROP_ICON_NAME] = "ICON_NAME"
199         };
200
201         char **l = NULL;
202         int r, p;
203
204         r = load_env_file("/etc/machine-info", &l);
205         if (r < 0 && r != -ENOENT)
206                 return r;
207
208         for (p = 2; p < _PROP_MAX; p++) {
209                 char *t, **u;
210
211                 assert(name[p]);
212
213                 if (isempty(data[p]))  {
214                         l = strv_env_unset(l, name[p]);
215                         continue;
216                 }
217
218                 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
219                         strv_free(l);
220                         return -ENOMEM;
221                 }
222
223                 u = strv_env_set(l, t);
224                 free(t);
225                 strv_free(l);
226
227                 if (!u)
228                         return -ENOMEM;
229                 l = u;
230         }
231
232         if (strv_isempty(l)) {
233
234                 if (unlink("/etc/machine-info") < 0)
235                         return errno == ENOENT ? 0 : -errno;
236
237                 return 0;
238         }
239
240         r = write_env_file("/etc/machine-info", l);
241         strv_free(l);
242
243         return r;
244 }
245
246 /* This mimics dbus_bus_get_unix_user() */
247 static pid_t get_unix_process_id(
248                 DBusConnection *connection,
249                 const char *name,
250                 DBusError *error) {
251
252         DBusMessage *m = NULL, *reply = NULL;
253         uint32_t pid = 0;
254
255         m = dbus_message_new_method_call(
256                         DBUS_SERVICE_DBUS,
257                         DBUS_PATH_DBUS,
258                         DBUS_INTERFACE_DBUS,
259                         "GetConnectionUnixProcessID");
260         if (!m) {
261                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
262                 goto finish;
263         }
264
265         if (!dbus_message_append_args(
266                             m,
267                             DBUS_TYPE_STRING, &name,
268                             DBUS_TYPE_INVALID)) {
269                 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
270                 goto finish;
271         }
272
273         reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
274         if (!reply)
275                 goto finish;
276
277         if (dbus_set_error_from_message(error, reply))
278                 goto finish;
279
280         if (!dbus_message_get_args(
281                             reply, error,
282                             DBUS_TYPE_UINT32, &pid,
283                             DBUS_TYPE_INVALID))
284                 goto finish;
285
286 finish:
287         if (m)
288                 dbus_message_unref(m);
289
290         if (reply)
291                 dbus_message_unref(reply);
292
293         return (pid_t) pid;
294 }
295
296 static int verify_polkit(
297                 DBusConnection *c,
298                 DBusMessage *request,
299                 const char *action,
300                 bool interactive,
301                 DBusError *error) {
302
303         DBusMessage *m = NULL, *reply = NULL;
304         const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
305         const char *sender;
306         uint32_t flags = interactive ? 1 : 0;
307         pid_t pid_raw;
308         uint32_t pid_u32;
309         unsigned long long starttime_raw;
310         uint64_t starttime_u64;
311         DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
312         int r;
313         dbus_bool_t authorized = FALSE;
314
315         assert(c);
316         assert(request);
317
318         sender = dbus_message_get_sender(request);
319         if (!sender)
320                 return -EINVAL;
321
322         pid_raw = get_unix_process_id(c, sender, error);
323         if (pid_raw == 0)
324                 return -EINVAL;
325
326         r = get_starttime_of_pid(pid_raw, &starttime_raw);
327         if (r < 0)
328                 return r;
329
330         m = dbus_message_new_method_call(
331                         "org.freedesktop.PolicyKit1",
332                         "/org/freedesktop/PolicyKit1/Authority",
333                         "org.freedesktop.PolicyKit1.Authority",
334                         "CheckAuthorization");
335         if (!m)
336                 return -ENOMEM;
337
338         dbus_message_iter_init_append(m, &iter_msg);
339
340         pid_u32 = (uint32_t) pid_raw;
341         starttime_u64 = (uint64_t) starttime_raw;
342
343         if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) ||
344             !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) ||
345             !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) ||
346             !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
347             !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) ||
348             !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) ||
349             !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) ||
350             !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
351             !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
352             !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
353             !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) ||
354             !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) ||
355             !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) ||
356             !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
357             !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
358             !dbus_message_iter_close_container(&iter_struct, &iter_array) ||
359             !dbus_message_iter_close_container(&iter_msg, &iter_struct) ||
360             !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) ||
361             !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) ||
362             !dbus_message_iter_close_container(&iter_msg, &iter_array) ||
363             !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) ||
364             !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) {
365                 r = -ENOMEM;
366                 goto finish;
367         }
368
369         reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
370         if (!reply) {
371                 r = -EIO;
372                 goto finish;
373         }
374
375         if (dbus_set_error_from_message(error, reply)) {
376                 r = -EIO;
377                 goto finish;
378         }
379
380         if (!dbus_message_iter_init(reply, &iter_msg) ||
381             dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
382                 r = -EIO;
383                 goto finish;
384         }
385
386         dbus_message_iter_recurse(&iter_msg, &iter_struct);
387
388         if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
389                 r = -EIO;
390                 goto finish;
391         }
392
393         dbus_message_iter_get_basic(&iter_struct, &authorized);
394
395         r = authorized ? 0 : -EPERM;
396
397 finish:
398
399         if (m)
400                 dbus_message_unref(m);
401
402         if (reply)
403                 dbus_message_unref(reply);
404
405         return r;
406 }
407
408 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
409         const char *name;
410
411         assert(i);
412         assert(property);
413
414         if (isempty(data[PROP_ICON_NAME]))
415                 name = fallback_icon_name();
416         else
417                 name = data[PROP_ICON_NAME];
418
419         return bus_property_append_string(i, property, (void*) name);
420 }
421
422 static DBusHandlerResult hostname_message_handler(
423                 DBusConnection *connection,
424                 DBusMessage *message,
425                 void *userdata) {
426
427         const BusProperty properties[] = {
428                 { "org.freedesktop.hostname1", "Hostname",       bus_property_append_string,    "s", data[PROP_HOSTNAME]},
429                 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string,    "s", data[PROP_STATIC_HOSTNAME]},
430                 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string,    "s", data[PROP_PRETTY_HOSTNAME]},
431                 { "org.freedesktop.hostname1", "IconName",       bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
432                 { NULL, NULL, NULL, NULL, NULL }
433         };
434
435         DBusMessage *reply = NULL, *changed = NULL;
436         DBusError error;
437         int r;
438
439         assert(connection);
440         assert(message);
441
442         dbus_error_init(&error);
443
444         if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
445                 const char *name;
446                 dbus_bool_t interactive;
447
448                 if (!dbus_message_get_args(
449                                     message,
450                                     &error,
451                                     DBUS_TYPE_STRING, &name,
452                                     DBUS_TYPE_BOOLEAN, &interactive,
453                                     DBUS_TYPE_INVALID))
454                         return bus_send_error_reply(connection, message, &error, -EINVAL);
455
456                 if (isempty(name))
457                         name = data[PROP_STATIC_HOSTNAME];
458
459                 if (isempty(name))
460                         name = "localhost";
461
462                 if (!hostname_is_valid(name))
463                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
464
465                 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
466                         char *h;
467
468                         r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
469                         if (r < 0)
470                                 return bus_send_error_reply(connection, message, &error, r);
471
472                         h = strdup(name);
473                         if (!h)
474                                 goto oom;
475
476                         free(data[PROP_HOSTNAME]);
477                         data[PROP_HOSTNAME] = h;
478
479                         r = write_data_hostname();
480                         if (r < 0) {
481                                 log_error("Failed to set host name: %s", strerror(-r));
482                                 return bus_send_error_reply(connection, message, NULL, r);
483                         }
484
485                         log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
486
487                         changed = bus_properties_changed_new(
488                                         "/org/freedesktop/hostname1",
489                                         "org.freedesktop.hostname1",
490                                         "Hostname\0");
491                         if (!changed)
492                                 goto oom;
493                 }
494
495         } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
496                 const char *name;
497                 dbus_bool_t interactive;
498
499                 if (!dbus_message_get_args(
500                                     message,
501                                     &error,
502                                     DBUS_TYPE_STRING, &name,
503                                     DBUS_TYPE_BOOLEAN, &interactive,
504                                     DBUS_TYPE_INVALID))
505                         return bus_send_error_reply(connection, message, &error, -EINVAL);
506
507                 if (isempty(name))
508                         name = NULL;
509
510                 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
511
512                         r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
513                         if (r < 0)
514                                 return bus_send_error_reply(connection, message, &error, r);
515
516                         if (isempty(name)) {
517                                 free(data[PROP_STATIC_HOSTNAME]);
518                                 data[PROP_STATIC_HOSTNAME] = NULL;
519                         } else {
520                                 char *h;
521
522                                 if (!hostname_is_valid(name))
523                                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
524
525                                 h = strdup(name);
526                                 if (!h)
527                                         goto oom;
528
529                                 free(data[PROP_STATIC_HOSTNAME]);
530                                 data[PROP_STATIC_HOSTNAME] = h;
531                         }
532
533                         r = write_data_static_hostname();
534                         if (r < 0) {
535                                 log_error("Failed to write static host name: %s", strerror(-r));
536                                 return bus_send_error_reply(connection, message, NULL, r);
537                         }
538
539                         log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME]));
540
541                         changed = bus_properties_changed_new(
542                                         "/org/freedesktop/hostname1",
543                                         "org.freedesktop.hostname1",
544                                         "StaticHostname\0");
545                         if (!changed)
546                                 goto oom;
547                 }
548
549         } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
550                    dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
551
552                 const char *name;
553                 dbus_bool_t interactive;
554                 int k;
555
556                 if (!dbus_message_get_args(
557                                     message,
558                                     &error,
559                                     DBUS_TYPE_STRING, &name,
560                                     DBUS_TYPE_BOOLEAN, &interactive,
561                                     DBUS_TYPE_INVALID))
562                         return bus_send_error_reply(connection, message, &error, -EINVAL);
563
564                 if (isempty(name))
565                         name = NULL;
566
567                 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
568
569                 if (!streq_ptr(name, data[k])) {
570
571                         /* Since the pretty hostname should always be
572                          * changed at the same time as the static one,
573                          * use the same policy action for both... */
574
575                         r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
576                                           "org.freedesktop.hostname1.set-static-hostname" :
577                                           "org.freedesktop.hostname1.set-machine-info", interactive, &error);
578                         if (r < 0)
579                                 return bus_send_error_reply(connection, message, &error, r);
580
581                         if (isempty(name)) {
582                                 free(data[k]);
583                                 data[k] = NULL;
584                         } else {
585                                 char *h;
586
587                                 h = strdup(name);
588                                 if (!h)
589                                         goto oom;
590
591                                 free(data[k]);
592                                 data[k] = h;
593                         }
594
595                         r = write_data_other();
596                         if (r < 0) {
597                                 log_error("Failed to write machine info: %s", strerror(-r));
598                                 return bus_send_error_reply(connection, message, NULL, r);
599                         }
600
601                         log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
602
603                         changed = bus_properties_changed_new(
604                                         "/org/freedesktop/hostname1",
605                                         "org.freedesktop.hostname1",
606                                         k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
607                         if (!changed)
608                                 goto oom;
609                 }
610
611         } else
612                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
613
614         if (!(reply = dbus_message_new_method_return(message)))
615                 goto oom;
616
617         if (!dbus_connection_send(connection, reply, NULL))
618                 goto oom;
619
620         dbus_message_unref(reply);
621         reply = NULL;
622
623         if (changed) {
624
625                 if (!dbus_connection_send(connection, changed, NULL))
626                         goto oom;
627
628                 dbus_message_unref(changed);
629         }
630
631         return DBUS_HANDLER_RESULT_HANDLED;
632
633 oom:
634         if (reply)
635                 dbus_message_unref(reply);
636
637         if (changed)
638                 dbus_message_unref(changed);
639
640         dbus_error_free(&error);
641
642         return DBUS_HANDLER_RESULT_NEED_MEMORY;
643 }
644
645 int main(int argc, char *argv[]) {
646         const DBusObjectPathVTable hostname_vtable = {
647                 .message_function = hostname_message_handler
648         };
649
650         DBusConnection *bus = NULL;
651         DBusError error;
652         int r;
653
654         dbus_error_init(&error);
655
656         log_set_target(LOG_TARGET_AUTO);
657         log_parse_environment();
658         log_open();
659
660         if (argc != 1) {
661                 log_error("This program takes no arguments.");
662                 r = -EINVAL;
663                 goto finish;
664         }
665
666         umask(0022);
667
668         r = read_data();
669         if (r < 0) {
670                 log_error("Failed to read hostname data: %s", strerror(-r));
671                 goto finish;
672         }
673
674         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
675         if (!bus) {
676                 log_error("Failed to get system D-Bus connection: %s", error.message);
677                 r = -ECONNREFUSED;
678                 goto finish;
679         }
680
681         if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
682                 log_error("Not enough memory");
683                 r = -ENOMEM;
684                 goto finish;
685         }
686
687         if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
688                 log_error("Failed to register name on bus: %s", error.message);
689                 r = -EEXIST;
690                 goto finish;
691         }
692
693         while (dbus_connection_read_write_dispatch(bus, -1))
694                 ;
695
696         r = 0;
697
698 finish:
699         free_data();
700
701         if (bus) {
702                 dbus_connection_flush(bus);
703                 dbus_connection_close(bus);
704                 dbus_connection_unref(bus);
705         }
706
707         dbus_error_free(&error);
708
709         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
710 }