chiark / gitweb /
dbus-manager: modernize style
[elogind.git] / src / core / dbus-manager.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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 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 <unistd.h>
24
25 #include "dbus.h"
26 #include "log.h"
27 #include "dbus-manager.h"
28 #include "strv.h"
29 #include "bus-errors.h"
30 #include "build.h"
31 #include "dbus-common.h"
32 #include "install.h"
33 #include "selinux-access.h"
34 #include "watchdog.h"
35 #include "hwclock.h"
36 #include "path-util.h"
37 #include "dbus-unit.h"
38 #include "virt.h"
39
40 #define BUS_MANAGER_INTERFACE_BEGIN                                     \
41         " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
42
43 #define BUS_MANAGER_INTERFACE_METHODS                                   \
44         "  <method name=\"GetUnit\">\n"                                 \
45         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
46         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
47         "  </method>\n"                                                 \
48         "  <method name=\"GetUnitByPID\">\n"                            \
49         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
50         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
51         "  </method>\n"                                                 \
52         "  <method name=\"LoadUnit\">\n"                                \
53         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
54         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
55         "  </method>\n"                                                 \
56         "  <method name=\"StartUnit\">\n"                               \
57         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
58         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
59         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
60         "  </method>\n"                                                 \
61         "  <method name=\"StartUnitReplace\">\n"                        \
62         "   <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n"     \
63         "   <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n"     \
64         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
65         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
66         "  </method>\n"                                                 \
67         "  <method name=\"StopUnit\">\n"                                \
68         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
69         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
70         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
71         "  </method>\n"                                                 \
72         "  <method name=\"ReloadUnit\">\n"                              \
73         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
74         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
75         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
76         "  </method>\n"                                                 \
77         "  <method name=\"RestartUnit\">\n"                             \
78         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
79         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
80         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
81         "  </method>\n"                                                 \
82         "  <method name=\"TryRestartUnit\">\n"                          \
83         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
84         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
85         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
86         "  </method>\n"                                                 \
87         "  <method name=\"ReloadOrRestartUnit\">\n"                     \
88         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
89         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
90         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
91         "  </method>\n"                                                 \
92         "  <method name=\"ReloadOrTryRestartUnit\">\n"                  \
93         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
94         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
95         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
96         "  </method>\n"                                                 \
97         "  <method name=\"KillUnit\">\n"                                \
98         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
99         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
100         "   <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n"       \
101         "  </method>\n"                                                 \
102         "  <method name=\"ResetFailedUnit\">\n"                         \
103         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
104         "  </method>\n"                                                 \
105         "  <method name=\"GetJob\">\n"                                  \
106         "   <arg name=\"id\" type=\"u\" direction=\"in\"/>\n"           \
107         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
108         "  </method>\n"                                                 \
109         "  <method name=\"ClearJobs\"/>\n"                              \
110         "  <method name=\"ResetFailed\"/>\n"                            \
111         "  <method name=\"ListUnits\">\n"                               \
112         "   <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
113         "  </method>\n"                                                 \
114         "  <method name=\"ListJobs\">\n"                                \
115         "   <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
116         "  </method>\n"                                                 \
117         "  <method name=\"Subscribe\"/>\n"                              \
118         "  <method name=\"Unsubscribe\"/>\n"                            \
119         "  <method name=\"Dump\">\n"                                    \
120         "   <arg name=\"dump\" type=\"s\" direction=\"out\"/>\n"        \
121         "  </method>\n"                                                 \
122         "  <method name=\"CreateSnapshot\">\n"                          \
123         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
124         "   <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n"      \
125         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
126         "  </method>\n"                                                 \
127         "  <method name=\"Reload\"/>\n"                                 \
128         "  <method name=\"Reexecute\"/>\n"                              \
129         "  <method name=\"Exit\"/>\n"                                   \
130         "  <method name=\"Reboot\"/>\n"                                 \
131         "  <method name=\"PowerOff\"/>\n"                               \
132         "  <method name=\"Halt\"/>\n"                                   \
133         "  <method name=\"KExec\"/>\n"                                  \
134         "  <method name=\"SwitchRoot\">\n"                              \
135         "   <arg name=\"new_root\" type=\"s\" direction=\"in\"/>\n"     \
136         "   <arg name=\"init\" type=\"s\" direction=\"in\"/>\n"         \
137         "  </method>\n"                                                 \
138         "  <method name=\"SetEnvironment\">\n"                          \
139         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
140         "  </method>\n"                                                 \
141         "  <method name=\"UnsetEnvironment\">\n"                        \
142         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
143         "  </method>\n"                                                 \
144         "  <method name=\"UnsetAndSetEnvironment\">\n"                  \
145         "   <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n"       \
146         "   <arg name=\"set\" type=\"as\" direction=\"in\"/>\n"         \
147         "  </method>\n"                                                 \
148         "  <method name=\"ListUnitFiles\">\n"                            \
149         "   <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
150         "  </method>\n"                                                 \
151         "  <method name=\"GetUnitFileState\">\n"                        \
152         "   <arg name=\"file\" type=\"s\" direction=\"in\"/>\n"         \
153         "   <arg name=\"state\" type=\"s\" direction=\"out\"/>\n"       \
154         "  </method>\n"                                                 \
155         "  <method name=\"EnableUnitFiles\">\n"                         \
156         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
157         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
158         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
159         "   <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
160         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
161         "  </method>\n"                                                 \
162         "  <method name=\"DisableUnitFiles\">\n"                        \
163         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
164         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
165         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
166         "  </method>\n"                                                 \
167         "  <method name=\"ReenableUnitFiles\">\n"                       \
168         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
169         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
170         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
171         "   <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
172         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
173         "  </method>\n"                                                 \
174         "  <method name=\"LinkUnitFiles\">\n"                           \
175         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
176         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
177         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
178         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
179         "  </method>\n"                                                 \
180         "  <method name=\"PresetUnitFiles\">\n"                         \
181         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
182         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
183         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
184         "   <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
185         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
186         "  </method>\n"                                                 \
187         "  <method name=\"MaskUnitFiles\">\n"                           \
188         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
189         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
190         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
191         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
192         "  </method>\n"                                                 \
193         "  <method name=\"UnmaskUnitFiles\">\n"                         \
194         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
195         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
196         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
197         "  </method>\n"
198
199 #define BUS_MANAGER_INTERFACE_SIGNALS                                   \
200         "  <signal name=\"UnitNew\">\n"                                 \
201         "   <arg name=\"id\" type=\"s\"/>\n"                            \
202         "   <arg name=\"unit\" type=\"o\"/>\n"                          \
203         "  </signal>\n"                                                 \
204         "  <signal name=\"UnitRemoved\">\n"                             \
205         "   <arg name=\"id\" type=\"s\"/>\n"                            \
206         "   <arg name=\"unit\" type=\"o\"/>\n"                          \
207         "  </signal>\n"                                                 \
208         "  <signal name=\"JobNew\">\n"                                  \
209         "   <arg name=\"id\" type=\"u\"/>\n"                            \
210         "   <arg name=\"job\" type=\"o\"/>\n"                           \
211         "   <arg name=\"unit\" type=\"s\"/>\n"                          \
212         "  </signal>\n"                                                 \
213         "  <signal name=\"JobRemoved\">\n"                              \
214         "   <arg name=\"id\" type=\"u\"/>\n"                            \
215         "   <arg name=\"job\" type=\"o\"/>\n"                           \
216         "   <arg name=\"unit\" type=\"s\"/>\n"                          \
217         "   <arg name=\"result\" type=\"s\"/>\n"                        \
218         "  </signal>"                                                   \
219         "  <signal name=\"StartupFinished\">\n"                         \
220         "   <arg name=\"firmware\" type=\"t\"/>\n"                      \
221         "   <arg name=\"loader\" type=\"t\"/>\n"                        \
222         "   <arg name=\"kernel\" type=\"t\"/>\n"                        \
223         "   <arg name=\"initrd\" type=\"t\"/>\n"                        \
224         "   <arg name=\"userspace\" type=\"t\"/>\n"                     \
225         "   <arg name=\"total\" type=\"t\"/>\n"                         \
226         "  </signal>"                                                   \
227         "  <signal name=\"UnitFilesChanged\"/>\n"
228
229 #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
230         "  <property name=\"Version\" type=\"s\" access=\"read\"/>\n"   \
231         "  <property name=\"Distribution\" type=\"s\" access=\"read\"/>\n" \
232         "  <property name=\"Features\" type=\"s\" access=\"read\"/>\n"  \
233         "  <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n"   \
234         "  <property name=\"FirmwareTimestamp\" type=\"t\" access=\"read\"/>\n" \
235         "  <property name=\"FirmwareTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
236         "  <property name=\"LoaderTimestamp\" type=\"t\" access=\"read\"/>\n" \
237         "  <property name=\"LoaderTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
238         "  <property name=\"KernelTimestamp\" type=\"t\" access=\"read\"/>\n" \
239         "  <property name=\"KernelTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
240         "  <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \
241         "  <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
242         "  <property name=\"UserspaceTimestamp\" type=\"t\" access=\"read\"/>\n" \
243         "  <property name=\"UserspaceTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
244         "  <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
245         "  <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
246         "  <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n"  \
247         "  <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \
248         "  <property name=\"NNames\" type=\"u\" access=\"read\"/>\n"    \
249         "  <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n"     \
250         "  <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
251         "  <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
252         "  <property name=\"Progress\" type=\"d\" access=\"read\"/>\n"  \
253         "  <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
254         "  <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
255         "  <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
256         "  <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
257         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
258         "  <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
259         "  <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
260         "  <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
261         "  <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
262         "  <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
263         "  <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n"
264
265 #define BUS_MANAGER_INTERFACE_END                                       \
266         " </interface>\n"
267
268 #define BUS_MANAGER_INTERFACE                                           \
269         BUS_MANAGER_INTERFACE_BEGIN                                     \
270         BUS_MANAGER_INTERFACE_METHODS                                   \
271         BUS_MANAGER_INTERFACE_SIGNALS                                   \
272         BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
273         BUS_MANAGER_INTERFACE_END
274
275 #define INTROSPECTION_BEGIN                                             \
276         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
277         "<node>\n"                                                      \
278         BUS_MANAGER_INTERFACE                                           \
279         BUS_PROPERTIES_INTERFACE                                        \
280         BUS_PEER_INTERFACE                                              \
281         BUS_INTROSPECTABLE_INTERFACE
282
283 #define INTROSPECTION_END                                               \
284         "</node>\n"
285
286 #define INTERFACES_LIST                              \
287         BUS_GENERIC_INTERFACES_LIST                  \
288         "org.freedesktop.systemd1.Manager\0"
289
290 const char bus_manager_interface[] _introspect_("Manager") = BUS_MANAGER_INTERFACE;
291
292 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_exec_output, exec_output, ExecOutput);
293
294 static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, void *data) {
295         const char *t;
296         Manager *m = data;
297         char buf[LINE_MAX] = "", *e = buf, *p = NULL;
298
299         assert(i);
300         assert(property);
301         assert(m);
302
303         if (m->taint_usr)
304                 e = stpcpy(e, "split-usr:");
305
306         if (readlink_malloc("/etc/mtab", &p) < 0)
307                 e = stpcpy(e, "mtab-not-symlink:");
308         else
309                 free(p);
310
311         if (access("/proc/cgroups", F_OK) < 0)
312                 e = stpcpy(e, "cgroups-missing:");
313
314         if (hwclock_is_localtime() > 0)
315                 e = stpcpy(e, "local-hwclock:");
316
317         /* remove the last ':' */
318         if (e != buf)
319                 e[-1] = 0;
320
321         t = buf;
322
323         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
324                 return -ENOMEM;
325
326         return 0;
327 }
328
329 static int bus_manager_append_log_target(DBusMessageIter *i, const char *property, void *data) {
330         const char *t;
331
332         assert(i);
333         assert(property);
334
335         t = log_target_to_string(log_get_target());
336
337         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
338                 return -ENOMEM;
339
340         return 0;
341 }
342
343 static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, void *data) {
344         const char *t;
345
346         assert(i);
347         assert(property);
348
349         dbus_message_iter_get_basic(i, &t);
350
351         return log_set_target_from_string(t);
352 }
353
354 static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
355         char *t;
356         int r;
357
358         assert(i);
359         assert(property);
360
361         r = log_level_to_string_alloc(log_get_max_level(), &t);
362         if (r < 0)
363                 return r;
364
365         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
366                 r = -ENOMEM;
367
368         free(t);
369         return r;
370 }
371
372 static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) {
373         const char *t;
374
375         assert(i);
376         assert(property);
377
378         dbus_message_iter_get_basic(i, &t);
379
380         return log_set_max_level_from_string(t);
381 }
382
383 static int bus_manager_append_n_names(DBusMessageIter *i, const char *property, void *data) {
384         Manager *m = data;
385         uint32_t u;
386
387         assert(i);
388         assert(property);
389         assert(m);
390
391         u = hashmap_size(m->units);
392
393         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
394                 return -ENOMEM;
395
396         return 0;
397 }
398
399 static int bus_manager_append_n_jobs(DBusMessageIter *i, const char *property, void *data) {
400         Manager *m = data;
401         uint32_t u;
402
403         assert(i);
404         assert(property);
405         assert(m);
406
407         u = hashmap_size(m->jobs);
408
409         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
410                 return -ENOMEM;
411
412         return 0;
413 }
414
415 static int bus_manager_append_progress(DBusMessageIter *i, const char *property, void *data) {
416         double d;
417         Manager *m = data;
418
419         assert(i);
420         assert(property);
421         assert(m);
422
423         if (dual_timestamp_is_set(&m->finish_timestamp))
424                 d = 1.0;
425         else
426                 d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
427
428         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
429                 return -ENOMEM;
430
431         return 0;
432 }
433
434 static int bus_manager_append_virt(DBusMessageIter *i, const char *property, void *data) {
435         Manager *m = data;
436         const char *id = "";
437
438         assert(i);
439         assert(property);
440         assert(m);
441
442         detect_virtualization(&id);
443
444         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id))
445                 return -ENOMEM;
446
447         return 0;
448 }
449
450 static DBusMessage *message_from_file_changes(
451                 DBusMessage *m,
452                 UnitFileChange *changes,
453                 unsigned n_changes,
454                 int carries_install_info) {
455
456         DBusMessageIter iter, sub, sub2;
457         DBusMessage *reply;
458         unsigned i;
459
460         reply = dbus_message_new_method_return(m);
461         if (!reply)
462                 return NULL;
463
464         dbus_message_iter_init_append(reply, &iter);
465
466         if (carries_install_info >= 0) {
467                 dbus_bool_t b;
468
469                 b = !!carries_install_info;
470                 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
471                         goto oom;
472         }
473
474         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
475                 goto oom;
476
477         for (i = 0; i < n_changes; i++) {
478                 const char *type, *path, *source;
479
480                 type = unit_file_change_type_to_string(changes[i].type);
481                 path = strempty(changes[i].path);
482                 source = strempty(changes[i].source);
483
484                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
485                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
486                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
487                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
488                     !dbus_message_iter_close_container(&sub, &sub2))
489                         goto oom;
490         }
491
492         if (!dbus_message_iter_close_container(&iter, &sub))
493                 goto oom;
494
495         return reply;
496
497 oom:
498         dbus_message_unref(reply);
499         return NULL;
500 }
501
502 static int bus_manager_send_unit_files_changed(Manager *m) {
503         DBusMessage *s;
504         int r;
505
506         s = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged");
507         if (!s)
508                 return -ENOMEM;
509
510         r = bus_broadcast(m, s);
511         dbus_message_unref(s);
512
513         return r;
514 }
515
516 static int bus_manager_set_runtime_watchdog_usec(DBusMessageIter *i, const char *property, void *data) {
517         uint64_t *t = data;
518
519         assert(i);
520         assert(property);
521
522         dbus_message_iter_get_basic(i, t);
523
524         return watchdog_set_timeout(t);
525 }
526
527 static const char systemd_property_string[] =
528         PACKAGE_STRING "\0"
529         DISTRIBUTION "\0"
530         SYSTEMD_FEATURES;
531
532 static const BusProperty bus_systemd_properties[] = {
533         { "Version",       bus_property_append_string,    "s",  0                                             },
534         { "Distribution",  bus_property_append_string,    "s",  sizeof(PACKAGE_STRING)                        },
535         { "Features",      bus_property_append_string,    "s",  sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) },
536         { NULL, }
537 };
538
539 static const BusProperty bus_manager_properties[] = {
540         { "Tainted",                     bus_manager_append_tainted,     "s",  0                                                },
541         { "FirmwareTimestamp",           bus_property_append_uint64,     "t",  offsetof(Manager, firmware_timestamp.realtime)   },
542         { "FirmwareTimestampMonotonic",  bus_property_append_uint64,     "t",  offsetof(Manager, firmware_timestamp.monotonic)  },
543         { "LoaderTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, loader_timestamp.realtime)     },
544         { "LoaderTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, loader_timestamp.monotonic)    },
545         { "KernelTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, kernel_timestamp.realtime)     },
546         { "KernelTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, kernel_timestamp.monotonic)    },
547         { "InitRDTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, initrd_timestamp.realtime)     },
548         { "InitRDTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, initrd_timestamp.monotonic)    },
549         { "UserspaceTimestamp",          bus_property_append_uint64,     "t",  offsetof(Manager, userspace_timestamp.realtime)  },
550         { "UserspaceTimestampMonotonic", bus_property_append_uint64,     "t",  offsetof(Manager, userspace_timestamp.monotonic) },
551         { "FinishTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, finish_timestamp.realtime)     },
552         { "FinishTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, finish_timestamp.monotonic)    },
553         { "LogLevel",                    bus_manager_append_log_level,   "s",  0,                                               false, bus_manager_set_log_level },
554         { "LogTarget",                   bus_manager_append_log_target,  "s",  0,                                               false, bus_manager_set_log_target },
555         { "NNames",                      bus_manager_append_n_names,     "u",  0                                                },
556         { "NJobs",                       bus_manager_append_n_jobs,      "u",  0                                                },
557         { "NInstalledJobs",              bus_property_append_uint32,     "u",  offsetof(Manager, n_installed_jobs)              },
558         { "NFailedJobs",                 bus_property_append_uint32,     "u",  offsetof(Manager, n_failed_jobs)                 },
559         { "Progress",                    bus_manager_append_progress,    "d",  0                                                },
560         { "Environment",                 bus_property_append_strv,       "as", offsetof(Manager, environment),                  true },
561         { "ConfirmSpawn",                bus_property_append_bool,       "b",  offsetof(Manager, confirm_spawn)                 },
562         { "ShowStatus",                  bus_property_append_bool,       "b",  offsetof(Manager, show_status)                   },
563         { "UnitPath",                    bus_property_append_strv,       "as", offsetof(Manager, lookup_paths.unit_path),       true },
564         { "ControlGroupHierarchy",       bus_property_append_string,     "s",  offsetof(Manager, cgroup_hierarchy),             true },
565         { "DefaultControllers",          bus_property_append_strv,       "as", offsetof(Manager, default_controllers),          true },
566         { "DefaultStandardOutput",       bus_manager_append_exec_output, "s",  offsetof(Manager, default_std_output)            },
567         { "DefaultStandardError",        bus_manager_append_exec_output, "s",  offsetof(Manager, default_std_error)             },
568         { "RuntimeWatchdogUSec",         bus_property_append_usec,       "t",  offsetof(Manager, runtime_watchdog),             false, bus_manager_set_runtime_watchdog_usec },
569         { "ShutdownWatchdogUSec",        bus_property_append_usec,       "t",  offsetof(Manager, shutdown_watchdog),            false, bus_property_set_usec },
570         { "Virtualization",              bus_manager_append_virt,        "s",  0,                                               },
571         { NULL, }
572 };
573
574 static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
575         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
576         _cleanup_free_ char * path = NULL;
577         Manager *m = data;
578         int r;
579         DBusError error;
580         JobType job_type = _JOB_TYPE_INVALID;
581         bool reload_if_possible = false;
582         const char *member;
583
584         assert(connection);
585         assert(message);
586         assert(m);
587
588         dbus_error_init(&error);
589
590         member = dbus_message_get_member(message);
591
592         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
593                 const char *name;
594                 Unit *u;
595
596                 if (!dbus_message_get_args(
597                                     message,
598                                     &error,
599                                     DBUS_TYPE_STRING, &name,
600                                     DBUS_TYPE_INVALID))
601                         return bus_send_error_reply(connection, message, &error, -EINVAL);
602
603                 u = manager_get_unit(m, name);
604                 if (!u) {
605                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
606                         return bus_send_error_reply(connection, message, &error, -ENOENT);
607                 }
608
609                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
610
611                 reply = dbus_message_new_method_return(message);
612                 if (!reply)
613                         goto oom;
614
615                 path = unit_dbus_path(u);
616                 if (!path)
617                         goto oom;
618
619                 if (!dbus_message_append_args(
620                                     reply,
621                                     DBUS_TYPE_OBJECT_PATH, &path,
622                                     DBUS_TYPE_INVALID))
623                         goto oom;
624         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
625                 Unit *u;
626                 uint32_t pid;
627
628                 if (!dbus_message_get_args(
629                                     message,
630                                     &error,
631                                     DBUS_TYPE_UINT32, &pid,
632                                     DBUS_TYPE_INVALID))
633                         return bus_send_error_reply(connection, message, &error, -EINVAL);
634
635                 u = cgroup_unit_by_pid(m, (pid_t) pid);
636                 if (!u) {
637                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
638                         return bus_send_error_reply(connection, message, &error, -ENOENT);
639                 }
640
641                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
642
643                 reply = dbus_message_new_method_return(message);
644                 if (!reply)
645                         goto oom;
646
647                 path = unit_dbus_path(u);
648                 if (!path)
649                         goto oom;
650
651                 if (!dbus_message_append_args(
652                                     reply,
653                                     DBUS_TYPE_OBJECT_PATH, &path,
654                                     DBUS_TYPE_INVALID))
655                         goto oom;
656         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
657                 const char *name;
658                 Unit *u;
659
660                 if (!dbus_message_get_args(
661                                     message,
662                                     &error,
663                                     DBUS_TYPE_STRING, &name,
664                                     DBUS_TYPE_INVALID))
665                         return bus_send_error_reply(connection, message, &error, -EINVAL);
666
667                 r = manager_load_unit(m, name, NULL, &error, &u);
668                 if (r < 0)
669                         return bus_send_error_reply(connection, message, &error, r);
670
671                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
672
673                 reply = dbus_message_new_method_return(message);
674                 if (!reply)
675                         goto oom;
676
677                 path = unit_dbus_path(u);
678                 if (!path)
679                         goto oom;
680
681                 if (!dbus_message_append_args(
682                                     reply,
683                                     DBUS_TYPE_OBJECT_PATH, &path,
684                                     DBUS_TYPE_INVALID))
685                         goto oom;
686
687         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
688                 job_type = JOB_START;
689         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
690                 job_type = JOB_START;
691         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
692                 job_type = JOB_STOP;
693         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
694                 job_type = JOB_RELOAD;
695         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
696                 job_type = JOB_RESTART;
697         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
698                 job_type = JOB_TRY_RESTART;
699         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
700                 reload_if_possible = true;
701                 job_type = JOB_RESTART;
702         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
703                 reload_if_possible = true;
704                 job_type = JOB_TRY_RESTART;
705         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
706                 const char *name, *swho;
707                 int32_t signo;
708                 Unit *u;
709                 KillWho who;
710
711                 if (!dbus_message_get_args(
712                                     message,
713                                     &error,
714                                     DBUS_TYPE_STRING, &name,
715                                     DBUS_TYPE_STRING, &swho,
716                                     DBUS_TYPE_INT32, &signo,
717                                     DBUS_TYPE_INVALID))
718                         return bus_send_error_reply(connection, message, &error, -EINVAL);
719
720                 if (isempty(swho))
721                         who = KILL_ALL;
722                 else {
723                         who = kill_who_from_string(swho);
724                         if (who < 0)
725                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
726                 }
727
728                 if (signo <= 0 || signo >= _NSIG)
729                         return bus_send_error_reply(connection, message, &error, -EINVAL);
730
731                 u = manager_get_unit(m, name);
732                 if (!u) {
733                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
734                         return bus_send_error_reply(connection, message, &error, -ENOENT);
735                 }
736
737                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
738
739                 r = unit_kill(u, who, signo, &error);
740                 if (r < 0)
741                         return bus_send_error_reply(connection, message, &error, r);
742
743                 reply = dbus_message_new_method_return(message);
744                 if (!reply)
745                         goto oom;
746
747         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
748                 uint32_t id;
749                 Job *j;
750
751                 if (!dbus_message_get_args(
752                                     message,
753                                     &error,
754                                     DBUS_TYPE_UINT32, &id,
755                                     DBUS_TYPE_INVALID))
756                         return bus_send_error_reply(connection, message, &error, -EINVAL);
757
758                 j = manager_get_job(m, id);
759                 if (!j) {
760                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
761                         return bus_send_error_reply(connection, message, &error, -ENOENT);
762                 }
763
764                 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
765
766                 reply = dbus_message_new_method_return(message);
767                 if (!reply)
768                         goto oom;
769
770                 path = job_dbus_path(j);
771                 if (!path)
772                         goto oom;
773
774                 if (!dbus_message_append_args(
775                                     reply,
776                                     DBUS_TYPE_OBJECT_PATH, &path,
777                                     DBUS_TYPE_INVALID))
778                         goto oom;
779
780         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
781
782                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
783
784                 manager_clear_jobs(m);
785
786                 reply = dbus_message_new_method_return(message);
787                 if (!reply)
788                         goto oom;
789
790         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
791
792                 SELINUX_ACCESS_CHECK(connection, message, "reload");
793
794                 manager_reset_failed(m);
795
796                 reply = dbus_message_new_method_return(message);
797                 if (!reply)
798                         goto oom;
799
800         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
801                 const char *name;
802                 Unit *u;
803
804                 if (!dbus_message_get_args(
805                                     message,
806                                     &error,
807                                     DBUS_TYPE_STRING, &name,
808                                     DBUS_TYPE_INVALID))
809                         return bus_send_error_reply(connection, message, &error, -EINVAL);
810
811                 u = manager_get_unit(m, name);
812                 if (!u) {
813                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
814                         return bus_send_error_reply(connection, message, &error, -ENOENT);
815                 }
816
817                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
818
819                 unit_reset_failed(u);
820
821                 reply = dbus_message_new_method_return(message);
822                 if (!reply)
823                         goto oom;
824
825         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
826                 DBusMessageIter iter, sub;
827                 Iterator i;
828                 Unit *u;
829                 const char *k;
830
831                 SELINUX_ACCESS_CHECK(connection, message, "status");
832
833                 reply = dbus_message_new_method_return(message);
834                 if (!reply)
835                         goto oom;
836
837                 dbus_message_iter_init_append(reply, &iter);
838
839                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
840                         goto oom;
841
842                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
843                         char *u_path, *j_path;
844                         const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
845                         DBusMessageIter sub2;
846                         uint32_t job_id;
847                         Unit *f;
848
849                         if (k != u->id)
850                                 continue;
851
852                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
853                                 goto oom;
854
855                         description = unit_description(u);
856                         load_state = unit_load_state_to_string(u->load_state);
857                         active_state = unit_active_state_to_string(unit_active_state(u));
858                         sub_state = unit_sub_state_to_string(u);
859
860                         f = unit_following(u);
861                         following = f ? f->id : "";
862
863                         u_path = unit_dbus_path(u);
864                         if (!u_path)
865                                 goto oom;
866
867                         if (u->job) {
868                                 job_id = (uint32_t) u->job->id;
869
870                                 if (!(j_path = job_dbus_path(u->job))) {
871                                         free(u_path);
872                                         goto oom;
873                                 }
874
875                                 sjob_type = job_type_to_string(u->job->type);
876                         } else {
877                                 job_id = 0;
878                                 j_path = u_path;
879                                 sjob_type = "";
880                         }
881
882                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) ||
883                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
884                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
885                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
886                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
887                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
888                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
889                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
890                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
891                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
892                                 free(u_path);
893                                 if (u->job)
894                                         free(j_path);
895                                 goto oom;
896                         }
897
898                         free(u_path);
899                         if (u->job)
900                                 free(j_path);
901
902                         if (!dbus_message_iter_close_container(&sub, &sub2))
903                                 goto oom;
904                 }
905
906                 if (!dbus_message_iter_close_container(&iter, &sub))
907                         goto oom;
908
909         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
910                 DBusMessageIter iter, sub;
911                 Iterator i;
912                 Job *j;
913
914                 SELINUX_ACCESS_CHECK(connection, message, "status");
915
916                 reply = dbus_message_new_method_return(message);
917                 if (!reply)
918                         goto oom;
919
920                 dbus_message_iter_init_append(reply, &iter);
921
922                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
923                         goto oom;
924
925                 HASHMAP_FOREACH(j, m->jobs, i) {
926                         char *u_path, *j_path;
927                         const char *state, *type;
928                         uint32_t id;
929                         DBusMessageIter sub2;
930
931                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
932                                 goto oom;
933
934                         id = (uint32_t) j->id;
935                         state = job_state_to_string(j->state);
936                         type = job_type_to_string(j->type);
937
938                         j_path = job_dbus_path(j);
939                         if (!j_path)
940                                 goto oom;
941
942                         u_path = unit_dbus_path(j->unit);
943                         if (!u_path) {
944                                 free(j_path);
945                                 goto oom;
946                         }
947
948                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
949                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) ||
950                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
951                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
952                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
953                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
954                                 free(j_path);
955                                 free(u_path);
956                                 goto oom;
957                         }
958
959                         free(j_path);
960                         free(u_path);
961
962                         if (!dbus_message_iter_close_container(&sub, &sub2))
963                                 goto oom;
964                 }
965
966                 if (!dbus_message_iter_close_container(&iter, &sub))
967                         goto oom;
968
969         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
970                 char *client;
971                 Set *s;
972
973                 SELINUX_ACCESS_CHECK(connection, message, "status");
974
975                 s = BUS_CONNECTION_SUBSCRIBED(m, connection);
976                 if (!s) {
977                         s = set_new(string_hash_func, string_compare_func);
978                         if (!s)
979                                 goto oom;
980
981                         if (!dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL)) {
982                                 set_free(s);
983                                 goto oom;
984                         }
985                 }
986
987                 client = strdup(bus_message_get_sender_with_fallback(message));
988                 if (!client)
989                         goto oom;
990
991                 r = set_put(s, client);
992                 if (r < 0) {
993                         free(client);
994                         return bus_send_error_reply(connection, message, NULL, r);
995                 }
996
997                 reply = dbus_message_new_method_return(message);
998                 if (!reply)
999                         goto oom;
1000
1001         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
1002                 char *client;
1003
1004                 SELINUX_ACCESS_CHECK(connection, message, "status");
1005
1006                 client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message));
1007                 if (!client) {
1008                         dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
1009                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1010                 }
1011
1012                 free(client);
1013
1014                 reply = dbus_message_new_method_return(message);
1015                 if (!reply)
1016                         goto oom;
1017
1018         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
1019                 FILE *f;
1020                 char *dump = NULL;
1021                 size_t size;
1022
1023                 SELINUX_ACCESS_CHECK(connection, message, "status");
1024
1025                 reply = dbus_message_new_method_return(message);
1026                 if (!reply)
1027                         goto oom;
1028
1029                 f = open_memstream(&dump, &size);
1030                 if (!f)
1031                         goto oom;
1032
1033                 manager_dump_units(m, f, NULL);
1034                 manager_dump_jobs(m, f, NULL);
1035
1036                 if (ferror(f)) {
1037                         fclose(f);
1038                         free(dump);
1039                         goto oom;
1040                 }
1041
1042                 fclose(f);
1043
1044                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
1045                         free(dump);
1046                         goto oom;
1047                 }
1048
1049                 free(dump);
1050         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
1051                 const char *name;
1052                 dbus_bool_t cleanup;
1053                 Snapshot *s;
1054
1055                 SELINUX_ACCESS_CHECK(connection, message, "start");
1056
1057                 if (!dbus_message_get_args(
1058                                     message,
1059                                     &error,
1060                                     DBUS_TYPE_STRING, &name,
1061                                     DBUS_TYPE_BOOLEAN, &cleanup,
1062                                     DBUS_TYPE_INVALID))
1063                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1064
1065                 if (isempty(name))
1066                         name = NULL;
1067
1068                 r = snapshot_create(m, name, cleanup, &error, &s);
1069                 if (r < 0)
1070                         return bus_send_error_reply(connection, message, &error, r);
1071
1072                 reply = dbus_message_new_method_return(message);
1073                 if (!reply)
1074                         goto oom;
1075
1076                 path = unit_dbus_path(UNIT(s));
1077                 if (!path)
1078                         goto oom;
1079
1080                 if (!dbus_message_append_args(
1081                                     reply,
1082                                     DBUS_TYPE_OBJECT_PATH, &path,
1083                                     DBUS_TYPE_INVALID))
1084                         goto oom;
1085
1086         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1087                 char *introspection = NULL;
1088                 FILE *f;
1089                 Iterator i;
1090                 Unit *u;
1091                 Job *j;
1092                 const char *k;
1093                 size_t size;
1094
1095                 SELINUX_ACCESS_CHECK(connection, message, "status");
1096
1097                 reply = dbus_message_new_method_return(message);
1098                 if (!reply)
1099                         goto oom;
1100
1101                 /* We roll our own introspection code here, instead of
1102                  * relying on bus_default_message_handler() because we
1103                  * need to generate our introspection string
1104                  * dynamically. */
1105
1106                 f = open_memstream(&introspection, &size);
1107                 if (!f)
1108                         goto oom;
1109
1110                 fputs(INTROSPECTION_BEGIN, f);
1111
1112                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1113                         char *p;
1114
1115                         if (k != u->id)
1116                                 continue;
1117
1118                         p = bus_path_escape(k);
1119                         if (!p) {
1120                                 fclose(f);
1121                                 free(introspection);
1122                                 goto oom;
1123                         }
1124
1125                         fprintf(f, "<node name=\"unit/%s\"/>", p);
1126                         free(p);
1127                 }
1128
1129                 HASHMAP_FOREACH(j, m->jobs, i)
1130                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
1131
1132                 fputs(INTROSPECTION_END, f);
1133
1134                 if (ferror(f)) {
1135                         fclose(f);
1136                         free(introspection);
1137                         goto oom;
1138                 }
1139
1140                 fclose(f);
1141
1142                 if (!introspection)
1143                         goto oom;
1144
1145                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1146                         free(introspection);
1147                         goto oom;
1148                 }
1149
1150                 free(introspection);
1151
1152         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
1153
1154                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1155
1156                 assert(!m->queued_message);
1157
1158                 /* Instead of sending the reply back right away, we
1159                  * just remember that we need to and then send it
1160                  * after the reload is finished. That way the caller
1161                  * knows when the reload finished. */
1162
1163                 m->queued_message = dbus_message_new_method_return(message);
1164                 if (!m->queued_message)
1165                         goto oom;
1166
1167                 m->queued_message_connection = connection;
1168                 m->exit_code = MANAGER_RELOAD;
1169
1170         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
1171
1172                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1173
1174                 /* We don't send a reply back here, the client should
1175                  * just wait for us disconnecting. */
1176
1177                 m->exit_code = MANAGER_REEXECUTE;
1178
1179         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
1180
1181                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1182
1183                 if (m->running_as == SYSTEMD_SYSTEM) {
1184                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
1185                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1186                 }
1187
1188                 reply = dbus_message_new_method_return(message);
1189                 if (!reply)
1190                         goto oom;
1191
1192                 m->exit_code = MANAGER_EXIT;
1193
1194         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
1195
1196                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1197
1198                 if (m->running_as != SYSTEMD_SYSTEM) {
1199                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
1200                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1201                 }
1202
1203                 reply = dbus_message_new_method_return(message);
1204                 if (!reply)
1205                         goto oom;
1206
1207                 m->exit_code = MANAGER_REBOOT;
1208
1209         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
1210
1211                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1212
1213                 if (m->running_as != SYSTEMD_SYSTEM) {
1214                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
1215                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1216                 }
1217
1218                 reply = dbus_message_new_method_return(message);
1219                 if (!reply)
1220                         goto oom;
1221
1222                 m->exit_code = MANAGER_POWEROFF;
1223
1224         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
1225
1226                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1227
1228                 if (m->running_as != SYSTEMD_SYSTEM) {
1229                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
1230                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1231                 }
1232
1233                 reply = dbus_message_new_method_return(message);
1234                 if (!reply)
1235                         goto oom;
1236
1237                 m->exit_code = MANAGER_HALT;
1238
1239         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
1240
1241                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1242
1243                 if (m->running_as != SYSTEMD_SYSTEM) {
1244                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
1245                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1246                 }
1247
1248                 reply = dbus_message_new_method_return(message);
1249                 if (!reply)
1250                         goto oom;
1251
1252                 m->exit_code = MANAGER_KEXEC;
1253
1254         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
1255                 const char *switch_root, *switch_root_init;
1256                 char *u, *v;
1257                 int k;
1258
1259                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1260
1261                 if (!dbus_message_get_args(
1262                                     message,
1263                                     &error,
1264                                     DBUS_TYPE_STRING, &switch_root,
1265                                     DBUS_TYPE_STRING, &switch_root_init,
1266                                     DBUS_TYPE_INVALID))
1267                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1268
1269                 if (path_equal(switch_root, "/") || !path_is_absolute(switch_root))
1270                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1271
1272                 if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
1273                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1274
1275                 if (m->running_as != SYSTEMD_SYSTEM) {
1276                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
1277                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1278                 }
1279
1280                 /* Safety check */
1281                 if (isempty(switch_root_init))
1282                         k = access(switch_root, F_OK);
1283                 else {
1284                         char *p;
1285
1286                         p = strjoin(switch_root, "/", switch_root_init, NULL);
1287                         if (!p)
1288                                 goto oom;
1289
1290                         k = access(p, X_OK);
1291                         free(p);
1292                 }
1293                 if (k < 0)
1294                         return bus_send_error_reply(connection, message, NULL, -errno);
1295
1296                 u = strdup(switch_root);
1297                 if (!u)
1298                         goto oom;
1299
1300                 if (!isempty(switch_root_init)) {
1301                         v = strdup(switch_root_init);
1302                         if (!v) {
1303                                 free(u);
1304                                 goto oom;
1305                         }
1306                 } else
1307                         v = NULL;
1308
1309                 free(m->switch_root);
1310                 free(m->switch_root_init);
1311                 m->switch_root = u;
1312                 m->switch_root_init = v;
1313
1314                 reply = dbus_message_new_method_return(message);
1315                 if (!reply)
1316                         goto oom;
1317
1318                 m->exit_code = MANAGER_SWITCH_ROOT;
1319
1320         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
1321                 char **l = NULL, **e = NULL;
1322
1323                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1324
1325                 r = bus_parse_strv(message, &l);
1326                 if (r == -ENOMEM)
1327                         goto oom;
1328                 if (r < 0)
1329                         return bus_send_error_reply(connection, message, NULL, r);
1330
1331                 e = strv_env_merge(2, m->environment, l);
1332                 strv_free(l);
1333                 if (!e)
1334                         goto oom;
1335
1336                 reply = dbus_message_new_method_return(message);
1337                 if (!reply) {
1338                         strv_free(e);
1339                         goto oom;
1340                 }
1341
1342                 strv_free(m->environment);
1343                 m->environment = e;
1344
1345         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
1346                 char **l = NULL, **e = NULL;
1347
1348                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1349
1350                 r = bus_parse_strv(message, &l);
1351                 if (r == -ENOMEM)
1352                         goto oom;
1353                 if (r < 0)
1354                         return bus_send_error_reply(connection, message, NULL, r);
1355
1356                 e = strv_env_delete(m->environment, 1, l);
1357                 strv_free(l);
1358
1359                 if (!e)
1360                         goto oom;
1361
1362                 reply = dbus_message_new_method_return(message);
1363                 if (!reply) {
1364                         strv_free(e);
1365                         goto oom;
1366                 }
1367
1368                 strv_free(m->environment);
1369                 m->environment = e;
1370
1371         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
1372                 char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
1373                 DBusMessageIter iter;
1374
1375                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1376
1377                 if (!dbus_message_iter_init(message, &iter))
1378                         goto oom;
1379
1380                 r = bus_parse_strv_iter(&iter, &l_unset);
1381                 if (r == -ENOMEM)
1382                         goto oom;
1383                 if (r < 0)
1384                         return bus_send_error_reply(connection, message, NULL, r);
1385
1386                 if (!dbus_message_iter_next(&iter)) {
1387                         strv_free(l_unset);
1388                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1389                 }
1390
1391                 r = bus_parse_strv_iter(&iter, &l_set);
1392                 if (r < 0) {
1393                         strv_free(l_unset);
1394                         if (r == -ENOMEM)
1395                                 goto oom;
1396
1397                         return bus_send_error_reply(connection, message, NULL, r);
1398                 }
1399
1400                 e = strv_env_delete(m->environment, 1, l_unset);
1401                 strv_free(l_unset);
1402
1403                 if (!e) {
1404                         strv_free(l_set);
1405                         goto oom;
1406                 }
1407
1408                 f = strv_env_merge(2, e, l_set);
1409                 strv_free(l_set);
1410                 strv_free(e);
1411
1412                 if (!f)
1413                         goto oom;
1414
1415                 reply = dbus_message_new_method_return(message);
1416                 if (!reply) {
1417                         strv_free(f);
1418                         goto oom;
1419                 }
1420
1421                 strv_free(m->environment);
1422                 m->environment = f;
1423         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
1424                 DBusMessageIter iter, sub, sub2;
1425                 Hashmap *h;
1426                 Iterator i;
1427                 UnitFileList *item;
1428
1429                 SELINUX_ACCESS_CHECK(connection, message, "status");
1430
1431                 reply = dbus_message_new_method_return(message);
1432                 if (!reply)
1433                         goto oom;
1434
1435                 h = hashmap_new(string_hash_func, string_compare_func);
1436                 if (!h)
1437                         goto oom;
1438
1439                 r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
1440                 if (r < 0) {
1441                         unit_file_list_free(h);
1442                         return bus_send_error_reply(connection, message, NULL, r);
1443                 }
1444
1445                 dbus_message_iter_init_append(reply, &iter);
1446
1447                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
1448                         unit_file_list_free(h);
1449                         goto oom;
1450                 }
1451
1452                 HASHMAP_FOREACH(item, h, i) {
1453                         const char *state;
1454
1455                         state = unit_file_state_to_string(item->state);
1456                         assert(state);
1457
1458                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
1459                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
1460                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1461                             !dbus_message_iter_close_container(&sub, &sub2)) {
1462                                 unit_file_list_free(h);
1463                                 goto oom;
1464                         }
1465                 }
1466
1467                 unit_file_list_free(h);
1468
1469                 if (!dbus_message_iter_close_container(&iter, &sub))
1470                         goto oom;
1471
1472         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
1473                 const char *name;
1474                 UnitFileState state;
1475                 const char *s;
1476
1477                 SELINUX_ACCESS_CHECK(connection, message, "status");
1478
1479                 if (!dbus_message_get_args(
1480                                     message,
1481                                     &error,
1482                                     DBUS_TYPE_STRING, &name,
1483                                     DBUS_TYPE_INVALID))
1484                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1485
1486                 state = unit_file_get_state(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
1487                 if (state < 0)
1488                         return bus_send_error_reply(connection, message, NULL, state);
1489
1490                 s = unit_file_state_to_string(state);
1491                 assert(s);
1492
1493                 reply = dbus_message_new_method_return(message);
1494                 if (!reply)
1495                         goto oom;
1496
1497                 if (!dbus_message_append_args(
1498                                     reply,
1499                                     DBUS_TYPE_STRING, &s,
1500                                     DBUS_TYPE_INVALID))
1501                         goto oom;
1502         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
1503                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
1504                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
1505                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
1506                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) {
1507
1508                 char **l = NULL;
1509                 DBusMessageIter iter;
1510                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1511                 UnitFileChange *changes = NULL;
1512                 unsigned n_changes = 0;
1513                 dbus_bool_t runtime, force;
1514                 int carries_install_info = -1;
1515
1516                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "MaskUnitFiles") ? "disable" : "enable");
1517
1518                 if (!dbus_message_iter_init(message, &iter))
1519                         goto oom;
1520
1521                 r = bus_parse_strv_iter(&iter, &l);
1522                 if (r < 0) {
1523                         if (r == -ENOMEM)
1524                                 goto oom;
1525
1526                         return bus_send_error_reply(connection, message, NULL, r);
1527                 }
1528
1529                 if (!dbus_message_iter_next(&iter) ||
1530                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
1531                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
1532                         strv_free(l);
1533                         return bus_send_error_reply(connection, message, NULL, -EIO);
1534                 }
1535
1536                 if (streq(member, "EnableUnitFiles")) {
1537                         r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
1538                         carries_install_info = r;
1539                 } else if (streq(member, "ReenableUnitFiles")) {
1540                         r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
1541                         carries_install_info = r;
1542                 } else if (streq(member, "LinkUnitFiles"))
1543                         r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
1544                 else if (streq(member, "PresetUnitFiles")) {
1545                         r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
1546                         carries_install_info = r;
1547                 } else if (streq(member, "MaskUnitFiles"))
1548                         r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
1549                 else
1550                         assert_not_reached("Uh? Wrong method");
1551
1552                 strv_free(l);
1553                 bus_manager_send_unit_files_changed(m);
1554
1555                 if (r < 0) {
1556                         unit_file_changes_free(changes, n_changes);
1557                         return bus_send_error_reply(connection, message, NULL, r);
1558                 }
1559
1560                 reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
1561                 unit_file_changes_free(changes, n_changes);
1562
1563                 if (!reply)
1564                         goto oom;
1565
1566         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
1567                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
1568
1569                 char **l = NULL;
1570                 DBusMessageIter iter;
1571                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1572                 UnitFileChange *changes = NULL;
1573                 unsigned n_changes = 0;
1574                 dbus_bool_t runtime;
1575
1576                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "UnmaskUnitFiles") ? "enable" : "disable");
1577
1578                 if (!dbus_message_iter_init(message, &iter))
1579                         goto oom;
1580
1581                 r = bus_parse_strv_iter(&iter, &l);
1582                 if (r < 0) {
1583                         if (r == -ENOMEM)
1584                                 goto oom;
1585
1586                         return bus_send_error_reply(connection, message, NULL, r);
1587                 }
1588
1589                 if (!dbus_message_iter_next(&iter) ||
1590                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
1591                         strv_free(l);
1592                         return bus_send_error_reply(connection, message, NULL, -EIO);
1593                 }
1594
1595                 if (streq(member, "DisableUnitFiles"))
1596                         r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
1597                 else if (streq(member, "UnmaskUnitFiles"))
1598                         r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
1599                 else
1600                         assert_not_reached("Uh? Wrong method");
1601
1602                 strv_free(l);
1603                 bus_manager_send_unit_files_changed(m);
1604
1605                 if (r < 0) {
1606                         unit_file_changes_free(changes, n_changes);
1607                         return bus_send_error_reply(connection, message, NULL, r);
1608                 }
1609
1610                 reply = message_from_file_changes(message, changes, n_changes, -1);
1611                 unit_file_changes_free(changes, n_changes);
1612
1613                 if (!reply)
1614                         goto oom;
1615
1616         } else {
1617                 const BusBoundProperties bps[] = {
1618                         { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
1619                         { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
1620                         { NULL, }
1621                 };
1622
1623                 SELINUX_ACCESS_CHECK(connection, message, "status");
1624
1625                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1626         }
1627
1628         if (job_type != _JOB_TYPE_INVALID) {
1629                 const char *name, *smode, *old_name = NULL;
1630                 JobMode mode;
1631                 Unit *u;
1632                 dbus_bool_t b;
1633
1634                 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
1635                         b = dbus_message_get_args(
1636                                         message,
1637                                         &error,
1638                                         DBUS_TYPE_STRING, &old_name,
1639                                         DBUS_TYPE_STRING, &name,
1640                                         DBUS_TYPE_STRING, &smode,
1641                                         DBUS_TYPE_INVALID);
1642                 else
1643                         b = dbus_message_get_args(
1644                                         message,
1645                                         &error,
1646                                         DBUS_TYPE_STRING, &name,
1647                                         DBUS_TYPE_STRING, &smode,
1648                                         DBUS_TYPE_INVALID);
1649                 if (!b)
1650                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1651
1652                 if (old_name) {
1653                         u = manager_get_unit(m, old_name);
1654                         if (!u || !u->job || u->job->type != JOB_START) {
1655                                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
1656                                 return bus_send_error_reply(connection, message, &error, -ENOENT);
1657                         }
1658                 }
1659
1660                 mode = job_mode_from_string(smode);
1661                 if (mode < 0) {
1662                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
1663                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1664                 }
1665
1666                 r = manager_load_unit(m, name, NULL, &error, &u);
1667                 if (r < 0)
1668                         return bus_send_error_reply(connection, message, &error, r);
1669
1670                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
1671         }
1672
1673         if (reply)
1674                 if (!dbus_connection_send(connection, reply, NULL))
1675                         goto oom;
1676
1677         return DBUS_HANDLER_RESULT_HANDLED;
1678
1679 oom:
1680         dbus_error_free(&error);
1681
1682         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1683 }
1684
1685 const DBusObjectPathVTable bus_manager_vtable = {
1686         .message_function = bus_manager_message_handler
1687 };