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