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