chiark / gitweb /
unit: don't gc units that are still in STUB state
[elogind.git] / src / dbus-manager.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 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 <errno.h>
23
24 #include "dbus.h"
25 #include "log.h"
26 #include "dbus-manager.h"
27 #include "strv.h"
28
29 #define INTROSPECTION_BEGIN                                             \
30         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
31         "<node>"                                                        \
32         " <interface name=\"org.freedesktop.systemd1.Manager\">"        \
33         "  <method name=\"GetUnit\">"                                   \
34         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>"           \
35         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
36         "  </method>"                                                   \
37         "  <method name=\"LoadUnit\">"                                  \
38         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>"           \
39         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
40         "  </method>"                                                   \
41         "  <method name=\"GetJob\">"                                    \
42         "   <arg name=\"id\" type=\"u\" direction=\"in\"/>"             \
43         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>"           \
44         "  </method>"                                                   \
45         "  <method name=\"ClearJobs\"/>"                                \
46         "  <method name=\"ListUnits\">"                                 \
47         "   <arg name=\"units\" type=\"a(sssssouso)\" direction=\"out\"/>" \
48         "  </method>"                                                   \
49         "  <method name=\"ListJobs\">"                                  \
50         "   <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>"  \
51         "  </method>"                                                   \
52         "  <method name=\"Subscribe\"/>"                                \
53         "  <method name=\"Unsubscribe\"/>"                              \
54         "  <method name=\"Dump\"/>"                                     \
55         "  <method name=\"CreateSnapshot\">"                            \
56         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>"           \
57         "   <arg nane=\"cleanup\" type=\"b\" direction=\"in\"/>"        \
58         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>"          \
59         "  </method>"                                                   \
60         "  <method name=\"Reload\"/>"                                   \
61         "  <method name=\"Reexecute\"/>"                                \
62         "  <method name=\"Exit\"/>"                                     \
63         "  <method name=\"SetEnvironment\">"                            \
64         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>"         \
65         "  </method>"                                                   \
66         "  <method name=\"UnsetEnvironment\">"                          \
67         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>"         \
68         "  </method>"                                                   \
69         "  <signal name=\"UnitNew\">"                                   \
70         "   <arg name=\"id\" type=\"s\"/>"                              \
71         "   <arg name=\"unit\" type=\"o\"/>"                            \
72         "  </signal>"                                                   \
73         "  <signal name=\"UnitRemoved\">"                               \
74         "   <arg name=\"id\" type=\"s\"/>"                              \
75         "   <arg name=\"unit\" type=\"o\"/>"                            \
76         "  </signal>"                                                   \
77         "  <signal name=\"JobNew\">"                                    \
78         "   <arg name=\"id\" type=\"u\"/>"                              \
79         "   <arg name=\"job\" type=\"o\"/>"                             \
80         "  </signal>"                                                   \
81         "  <signal name=\"JobRemoved\">"                                \
82         "   <arg name=\"id\" type=\"u\"/>"                              \
83         "   <arg name=\"job\" type=\"o\"/>"                             \
84         "  </signal>"                                                   \
85         "  <property name=\"Version\" type=\"s\" access=\"read\"/>"     \
86         "  <property name=\"RunningAs\" type=\"s\" access=\"read\"/>"   \
87         "  <property name=\"BootTimestamp\" type=\"t\" access=\"read\"/>" \
88         "  <property name=\"LogLevel\" type=\"s\" access=\"read\"/>"    \
89         "  <property name=\"LogTarget\" type=\"s\" access=\"read\"/>"   \
90         "  <property name=\"NNames\" type=\"u\" access=\"read\"/>"      \
91         "  <property name=\"NJobs\" type=\"u\" access=\"read\"/>"       \
92         "  <property name=\"Environment\" type=\"as\" access=\"read\"/>" \
93         " </interface>"                                                 \
94         BUS_PROPERTIES_INTERFACE                                        \
95         BUS_INTROSPECTABLE_INTERFACE
96
97 #define INTROSPECTION_END                                               \
98         "</node>"
99
100 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs);
101
102 static int bus_manager_append_log_target(Manager *m, DBusMessageIter *i, const char *property, void *data) {
103         const char *t;
104
105         assert(m);
106         assert(i);
107         assert(property);
108
109         t = log_target_to_string(log_get_target());
110
111         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
112                 return -ENOMEM;
113
114         return 0;
115 }
116
117 static int bus_manager_append_log_level(Manager *m, DBusMessageIter *i, const char *property, void *data) {
118         const char *t;
119
120         assert(m);
121         assert(i);
122         assert(property);
123
124         t = log_level_to_string(log_get_max_level());
125
126         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
127                 return -ENOMEM;
128
129         return 0;
130 }
131
132 static int bus_manager_append_n_names(Manager *m, DBusMessageIter *i, const char *property, void *data) {
133         uint32_t u;
134
135         assert(m);
136         assert(i);
137         assert(property);
138
139         u = hashmap_size(m->units);
140
141         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
142                 return -ENOMEM;
143
144         return 0;
145 }
146
147 static int bus_manager_append_n_jobs(Manager *m, DBusMessageIter *i, const char *property, void *data) {
148         uint32_t u;
149
150         assert(m);
151         assert(i);
152         assert(property);
153
154         u = hashmap_size(m->jobs);
155
156         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
157                 return -ENOMEM;
158
159         return 0;
160 }
161
162 static DBusHandlerResult bus_manager_message_handler(DBusConnection  *connection, DBusMessage *message, void *data) {
163         Manager *m = data;
164
165         const BusProperty properties[] = {
166                 { "org.freedesktop.systemd1.Manager", "Version",       bus_property_append_string,    "s", PACKAGE_STRING     },
167                 { "org.freedesktop.systemd1.Manager", "RunningAs",     bus_manager_append_running_as, "s", &m->running_as     },
168                 { "org.freedesktop.systemd1.Manager", "BootTimestamp", bus_property_append_uint64,    "t", &m->boot_timestamp },
169                 { "org.freedesktop.systemd1.Manager", "LogLevel",      bus_manager_append_log_level,  "s", NULL               },
170                 { "org.freedesktop.systemd1.Manager", "LogTarget",     bus_manager_append_log_target, "s", NULL               },
171                 { "org.freedesktop.systemd1.Manager", "NNames",        bus_manager_append_n_names,    "u", NULL               },
172                 { "org.freedesktop.systemd1.Manager", "NJobs",         bus_manager_append_n_jobs,     "u", NULL               },
173                 { "org.freedesktop.systemd1.Manager", "Environment",   bus_property_append_strv,      "as", m->environment   },
174                 { NULL, NULL, NULL, NULL, NULL }
175         };
176
177         int r;
178         DBusError error;
179         DBusMessage *reply = NULL;
180         char * path = NULL;
181
182         assert(connection);
183         assert(message);
184         assert(m);
185
186         dbus_error_init(&error);
187
188         log_debug("Got D-Bus request: %s.%s() on %s",
189                   dbus_message_get_interface(message),
190                   dbus_message_get_member(message),
191                   dbus_message_get_path(message));
192
193         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
194                 const char *name;
195                 Unit *u;
196
197                 if (!dbus_message_get_args(
198                                     message,
199                                     &error,
200                                     DBUS_TYPE_STRING, &name,
201                                     DBUS_TYPE_INVALID))
202                         return bus_send_error_reply(m, message, &error, -EINVAL);
203
204                 if (!(u = manager_get_unit(m, name)))
205                         return bus_send_error_reply(m, message, NULL, -ENOENT);
206
207                 if (!(reply = dbus_message_new_method_return(message)))
208                         goto oom;
209
210                 if (!(path = unit_dbus_path(u)))
211                         goto oom;
212
213                 if (!dbus_message_append_args(
214                                     reply,
215                                     DBUS_TYPE_OBJECT_PATH, &path,
216                                     DBUS_TYPE_INVALID))
217                         goto oom;
218
219         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
220                 const char *name;
221                 Unit *u;
222
223                 if (!dbus_message_get_args(
224                                     message,
225                                     &error,
226                                     DBUS_TYPE_STRING, &name,
227                                     DBUS_TYPE_INVALID))
228                         return bus_send_error_reply(m, message, &error, -EINVAL);
229
230                 if ((r = manager_load_unit(m, name, NULL, &u)) < 0)
231                         return bus_send_error_reply(m, message, NULL, r);
232
233                 if (!(reply = dbus_message_new_method_return(message)))
234                         goto oom;
235
236                 if (!(path = unit_dbus_path(u)))
237                         goto oom;
238
239                 if (!dbus_message_append_args(
240                                     reply,
241                                     DBUS_TYPE_OBJECT_PATH, &path,
242                                     DBUS_TYPE_INVALID))
243                         goto oom;
244
245         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
246                 uint32_t id;
247                 Job *j;
248
249                 if (!dbus_message_get_args(
250                                     message,
251                                     &error,
252                                     DBUS_TYPE_UINT32, &id,
253                                     DBUS_TYPE_INVALID))
254                         return bus_send_error_reply(m, message, &error, -EINVAL);
255
256                 if (!(j = manager_get_job(m, id)))
257                         return bus_send_error_reply(m, message, NULL, -ENOENT);
258
259                 if (!(reply = dbus_message_new_method_return(message)))
260                         goto oom;
261
262                 if (!(path = job_dbus_path(j)))
263                         goto oom;
264
265                 if (!dbus_message_append_args(
266                                     reply,
267                                     DBUS_TYPE_OBJECT_PATH, &path,
268                                     DBUS_TYPE_INVALID))
269                         goto oom;
270
271         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
272
273                 manager_clear_jobs(m);
274
275                 if (!(reply = dbus_message_new_method_return(message)))
276                         goto oom;
277
278         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
279                 DBusMessageIter iter, sub;
280                 Iterator i;
281                 Unit *u;
282                 const char *k;
283
284                 if (!(reply = dbus_message_new_method_return(message)))
285                         goto oom;
286
287                 dbus_message_iter_init_append(reply, &iter);
288
289                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssssouso)", &sub))
290                         goto oom;
291
292                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
293                         char *u_path, *j_path;
294                         const char *description, *load_state, *active_state, *sub_state, *job_type;
295                         DBusMessageIter sub2;
296                         uint32_t job_id;
297
298                         if (k != u->meta.id)
299                                 continue;
300
301                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
302                                 goto oom;
303
304                         description = unit_description(u);
305                         load_state = unit_load_state_to_string(u->meta.load_state);
306                         active_state = unit_active_state_to_string(unit_active_state(u));
307                         sub_state = unit_sub_state_to_string(u);
308
309                         if (!(u_path = unit_dbus_path(u)))
310                                 goto oom;
311
312                         if (u->meta.job) {
313                                 job_id = (uint32_t) u->meta.job->id;
314
315                                 if (!(j_path = job_dbus_path(u->meta.job))) {
316                                         free(u_path);
317                                         goto oom;
318                                 }
319
320                                 job_type = job_type_to_string(u->meta.job->type);
321                         } else {
322                                 job_id = 0;
323                                 j_path = u_path;
324                                 job_type = "";
325                         }
326
327                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) ||
328                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
329                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
330                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
331                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
332                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
333                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
334                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &job_type) ||
335                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
336                                 free(u_path);
337                                 if (u->meta.job)
338                                         free(j_path);
339                                 goto oom;
340                         }
341
342                         free(u_path);
343                         if (u->meta.job)
344                                 free(j_path);
345
346                         if (!dbus_message_iter_close_container(&sub, &sub2))
347                                 goto oom;
348                 }
349
350                 if (!dbus_message_iter_close_container(&iter, &sub))
351                         goto oom;
352
353         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
354                 DBusMessageIter iter, sub;
355                 Iterator i;
356                 Job *j;
357
358                 if (!(reply = dbus_message_new_method_return(message)))
359                         goto oom;
360
361                 dbus_message_iter_init_append(reply, &iter);
362
363                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
364                         goto oom;
365
366                 HASHMAP_FOREACH(j, m->jobs, i) {
367                         char *u_path, *j_path;
368                         const char *state, *type;
369                         uint32_t id;
370                         DBusMessageIter sub2;
371
372                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
373                                 goto oom;
374
375                         id = (uint32_t) j->id;
376                         state = job_state_to_string(j->state);
377                         type = job_type_to_string(j->type);
378
379                         if (!(j_path = job_dbus_path(j)))
380                                 goto oom;
381
382                         if (!(u_path = unit_dbus_path(j->unit))) {
383                                 free(j_path);
384                                 goto oom;
385                         }
386
387                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
388                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) ||
389                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
390                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
391                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
392                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
393                                 free(j_path);
394                                 free(u_path);
395                                 goto oom;
396                         }
397
398                         free(j_path);
399                         free(u_path);
400
401                         if (!dbus_message_iter_close_container(&sub, &sub2))
402                                 goto oom;
403                 }
404
405                 if (!dbus_message_iter_close_container(&iter, &sub))
406                         goto oom;
407
408         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
409                 char *client;
410
411                 if (!(client = strdup(dbus_message_get_sender(message))))
412                         goto oom;
413
414                 r = set_put(m->subscribed, client);
415
416                 if (r < 0)
417                         return bus_send_error_reply(m, message, NULL, r);
418
419                 if (!(reply = dbus_message_new_method_return(message)))
420                         goto oom;
421
422         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
423                 char *client;
424
425                 if (!(client = set_remove(m->subscribed, (char*) dbus_message_get_sender(message))))
426                         return bus_send_error_reply(m, message, NULL, -ENOENT);
427
428                 free(client);
429
430                 if (!(reply = dbus_message_new_method_return(message)))
431                         goto oom;
432
433         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
434                 FILE *f;
435                 char *dump = NULL;
436                 size_t size;
437
438                 if (!(reply = dbus_message_new_method_return(message)))
439                         goto oom;
440
441                 if (!(f = open_memstream(&dump, &size)))
442                         goto oom;
443
444                 manager_dump_units(m, f, NULL);
445                 manager_dump_jobs(m, f, NULL);
446
447                 if (ferror(f)) {
448                         fclose(f);
449                         free(dump);
450                         goto oom;
451                 }
452
453                 fclose(f);
454
455                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
456                         free(dump);
457                         goto oom;
458                 }
459
460                 free(dump);
461         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
462                 const char *name;
463                 dbus_bool_t cleanup;
464                 Snapshot *s;
465
466                 if (!dbus_message_get_args(
467                                     message,
468                                     &error,
469                                     DBUS_TYPE_STRING, &name,
470                                     DBUS_TYPE_BOOLEAN, &cleanup,
471                                     DBUS_TYPE_INVALID))
472                         return bus_send_error_reply(m, message, &error, -EINVAL);
473
474                 if (name && name[0] == 0)
475                         name = NULL;
476
477                 if ((r = snapshot_create(m, name, cleanup, &s)) < 0)
478                         return bus_send_error_reply(m, message, NULL, r);
479
480                 if (!(reply = dbus_message_new_method_return(message)))
481                         goto oom;
482
483                 if (!(path = unit_dbus_path(UNIT(s))))
484                         goto oom;
485
486                 if (!dbus_message_append_args(
487                                     reply,
488                                     DBUS_TYPE_OBJECT_PATH, &path,
489                                     DBUS_TYPE_INVALID))
490                         goto oom;
491
492         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
493                 char *introspection = NULL;
494                 FILE *f;
495                 Iterator i;
496                 Unit *u;
497                 Job *j;
498                 const char *k;
499                 size_t size;
500
501                 if (!(reply = dbus_message_new_method_return(message)))
502                         goto oom;
503
504                 /* We roll our own introspection code here, instead of
505                  * relying on bus_default_message_handler() because we
506                  * need to generate our introspection string
507                  * dynamically. */
508
509                 if (!(f = open_memstream(&introspection, &size)))
510                         goto oom;
511
512                 fputs(INTROSPECTION_BEGIN, f);
513
514                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
515                         char *p;
516
517                         if (k != u->meta.id)
518                                 continue;
519
520                         if (!(p = bus_path_escape(k))) {
521                                 fclose(f);
522                                 free(introspection);
523                                 goto oom;
524                         }
525
526                         fprintf(f, "<node name=\"unit/%s\"/>", p);
527                         free(p);
528                 }
529
530                 HASHMAP_FOREACH(j, m->jobs, i)
531                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
532
533                 fputs(INTROSPECTION_END, f);
534
535                 if (ferror(f)) {
536                         fclose(f);
537                         free(introspection);
538                         goto oom;
539                 }
540
541                 fclose(f);
542
543                 if (!introspection)
544                         goto oom;
545
546                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
547                         free(introspection);
548                         goto oom;
549                 }
550
551                 free(introspection);
552
553         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
554
555                 assert(!m->queued_message);
556
557                 /* Instead of sending the reply back right away, we
558                  * just remember that we need to and then send it
559                  * after the reload is finished. That way the caller
560                  * knows when the reload finished. */
561
562                 if (!(m->queued_message = dbus_message_new_method_return(message)))
563                         goto oom;
564
565                 m->exit_code = MANAGER_RELOAD;
566
567         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
568
569                 if (!(reply = dbus_message_new_method_return(message)))
570                         goto oom;
571
572                 m->exit_code = MANAGER_REEXECUTE;
573
574         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
575
576                 if (m->running_as == MANAGER_INIT)
577                         return bus_send_error_reply(m, message, NULL, -ENOTSUP);
578
579                 if (!(reply = dbus_message_new_method_return(message)))
580                         goto oom;
581
582                 m->exit_code = MANAGER_EXIT;
583
584         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
585                 char **l = NULL, **e = NULL;
586
587                 if ((r = bus_parse_strv(message, &l)) < 0) {
588                         if (r == -ENOMEM)
589                                 goto oom;
590
591                         return bus_send_error_reply(m, message, NULL, r);
592                 }
593
594                 e = strv_env_merge(m->environment, l, NULL);
595                 strv_free(l);
596
597                 if (!e)
598                         goto oom;
599
600                 if (!(reply = dbus_message_new_method_return(message))) {
601                         strv_free(e);
602                         goto oom;
603                 }
604
605                 strv_free(m->environment);
606                 m->environment = e;
607
608         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
609                 char **l = NULL, **e = NULL;
610
611                 if ((r = bus_parse_strv(message, &l)) < 0) {
612                         if (r == -ENOMEM)
613                                 goto oom;
614
615                         return bus_send_error_reply(m, message, NULL, r);
616                 }
617
618                 e = strv_env_delete(m->environment, l, NULL);
619                 strv_free(l);
620
621                 if (!e)
622                         goto oom;
623
624                 if (!(reply = dbus_message_new_method_return(message)))
625                         goto oom;
626
627                 strv_free(m->environment);
628                 m->environment = e;
629
630         } else
631                 return bus_default_message_handler(m, message, NULL, properties);
632
633         free(path);
634
635         if (reply) {
636                 if (!dbus_connection_send(connection, reply, NULL))
637                         goto oom;
638
639                 dbus_message_unref(reply);
640         }
641
642         return DBUS_HANDLER_RESULT_HANDLED;
643
644 oom:
645         free(path);
646
647         if (reply)
648                 dbus_message_unref(reply);
649
650         dbus_error_free(&error);
651
652         return DBUS_HANDLER_RESULT_NEED_MEMORY;
653 }
654
655 const DBusObjectPathVTable bus_manager_vtable = {
656         .message_function = bus_manager_message_handler
657 };