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