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