chiark / gitweb /
742f6bbd8551eae824965606a3c54c40b0fe21a8
[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_acquire_subscribed(m, connection);
1061                 if (!s)
1062                         goto oom;
1063
1064                 client = strdup(bus_message_get_sender_with_fallback(message));
1065                 if (!client)
1066                         goto oom;
1067
1068                 r = set_consume(s, client);
1069                 if (r < 0)
1070                         return bus_send_error_reply(connection, message, NULL, r);
1071
1072                 reply = dbus_message_new_method_return(message);
1073                 if (!reply)
1074                         goto oom;
1075
1076         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
1077                 char *client;
1078
1079                 SELINUX_ACCESS_CHECK(connection, message, "status");
1080
1081                 client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message));
1082                 if (!client) {
1083                         dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
1084                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1085                 }
1086
1087                 free(client);
1088
1089                 reply = dbus_message_new_method_return(message);
1090                 if (!reply)
1091                         goto oom;
1092
1093         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
1094                 FILE *f;
1095                 char *dump = NULL;
1096                 size_t size;
1097
1098                 SELINUX_ACCESS_CHECK(connection, message, "status");
1099
1100                 reply = dbus_message_new_method_return(message);
1101                 if (!reply)
1102                         goto oom;
1103
1104                 f = open_memstream(&dump, &size);
1105                 if (!f)
1106                         goto oom;
1107
1108                 manager_dump_units(m, f, NULL);
1109                 manager_dump_jobs(m, f, NULL);
1110
1111                 if (ferror(f)) {
1112                         fclose(f);
1113                         free(dump);
1114                         goto oom;
1115                 }
1116
1117                 fclose(f);
1118
1119                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
1120                         free(dump);
1121                         goto oom;
1122                 }
1123
1124                 free(dump);
1125         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
1126                 const char *name;
1127                 dbus_bool_t cleanup;
1128                 Snapshot *s;
1129
1130                 SELINUX_ACCESS_CHECK(connection, message, "start");
1131
1132                 if (!dbus_message_get_args(
1133                                     message,
1134                                     &error,
1135                                     DBUS_TYPE_STRING, &name,
1136                                     DBUS_TYPE_BOOLEAN, &cleanup,
1137                                     DBUS_TYPE_INVALID))
1138                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1139
1140                 if (isempty(name))
1141                         name = NULL;
1142
1143                 r = snapshot_create(m, name, cleanup, &error, &s);
1144                 if (r < 0)
1145                         return bus_send_error_reply(connection, message, &error, r);
1146
1147                 reply = dbus_message_new_method_return(message);
1148                 if (!reply)
1149                         goto oom;
1150
1151                 path = unit_dbus_path(UNIT(s));
1152                 if (!path)
1153                         goto oom;
1154
1155                 if (!dbus_message_append_args(
1156                                     reply,
1157                                     DBUS_TYPE_OBJECT_PATH, &path,
1158                                     DBUS_TYPE_INVALID))
1159                         goto oom;
1160
1161         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) {
1162                 const char *name;
1163                 Unit *u;
1164
1165                 if (!dbus_message_get_args(
1166                                     message,
1167                                     &error,
1168                                     DBUS_TYPE_STRING, &name,
1169                                     DBUS_TYPE_INVALID))
1170                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1171
1172                 u = manager_get_unit(m, name);
1173                 if (!u) {
1174                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
1175                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1176                 }
1177
1178                 if (u->type != UNIT_SNAPSHOT) {
1179                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name);
1180                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1181                 }
1182
1183                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
1184                 snapshot_remove(SNAPSHOT(u));
1185
1186                 reply = dbus_message_new_method_return(message);
1187                 if (!reply)
1188                         goto oom;
1189
1190         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1191                 char *introspection = NULL;
1192                 FILE *f;
1193                 Iterator i;
1194                 Unit *u;
1195                 Job *j;
1196                 const char *k;
1197                 size_t size;
1198
1199                 SELINUX_ACCESS_CHECK(connection, message, "status");
1200
1201                 reply = dbus_message_new_method_return(message);
1202                 if (!reply)
1203                         goto oom;
1204
1205                 /* We roll our own introspection code here, instead of
1206                  * relying on bus_default_message_handler() because we
1207                  * need to generate our introspection string
1208                  * dynamically. */
1209
1210                 f = open_memstream(&introspection, &size);
1211                 if (!f)
1212                         goto oom;
1213
1214                 fputs(INTROSPECTION_BEGIN, f);
1215
1216                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1217                         char *p;
1218
1219                         if (k != u->id)
1220                                 continue;
1221
1222                         p = bus_path_escape(k);
1223                         if (!p) {
1224                                 fclose(f);
1225                                 free(introspection);
1226                                 goto oom;
1227                         }
1228
1229                         fprintf(f, "<node name=\"unit/%s\"/>", p);
1230                         free(p);
1231                 }
1232
1233                 HASHMAP_FOREACH(j, m->jobs, i)
1234                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
1235
1236                 fputs(INTROSPECTION_END, f);
1237
1238                 if (ferror(f)) {
1239                         fclose(f);
1240                         free(introspection);
1241                         goto oom;
1242                 }
1243
1244                 fclose(f);
1245
1246                 if (!introspection)
1247                         goto oom;
1248
1249                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1250                         free(introspection);
1251                         goto oom;
1252                 }
1253
1254                 free(introspection);
1255
1256         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
1257
1258                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1259
1260                 assert(!m->queued_message);
1261
1262                 /* Instead of sending the reply back right away, we
1263                  * just remember that we need to and then send it
1264                  * after the reload is finished. That way the caller
1265                  * knows when the reload finished. */
1266
1267                 m->queued_message = dbus_message_new_method_return(message);
1268                 if (!m->queued_message)
1269                         goto oom;
1270
1271                 m->queued_message_connection = connection;
1272                 m->exit_code = MANAGER_RELOAD;
1273
1274         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
1275
1276                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1277
1278                 /* We don't send a reply back here, the client should
1279                  * just wait for us disconnecting. */
1280
1281                 m->exit_code = MANAGER_REEXECUTE;
1282
1283         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
1284
1285                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1286
1287                 if (m->running_as == SYSTEMD_SYSTEM) {
1288                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
1289                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1290                 }
1291
1292                 reply = dbus_message_new_method_return(message);
1293                 if (!reply)
1294                         goto oom;
1295
1296                 m->exit_code = MANAGER_EXIT;
1297
1298         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
1299
1300                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1301
1302                 if (m->running_as != SYSTEMD_SYSTEM) {
1303                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
1304                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1305                 }
1306
1307                 reply = dbus_message_new_method_return(message);
1308                 if (!reply)
1309                         goto oom;
1310
1311                 m->exit_code = MANAGER_REBOOT;
1312
1313         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
1314
1315                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1316
1317                 if (m->running_as != SYSTEMD_SYSTEM) {
1318                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
1319                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1320                 }
1321
1322                 reply = dbus_message_new_method_return(message);
1323                 if (!reply)
1324                         goto oom;
1325
1326                 m->exit_code = MANAGER_POWEROFF;
1327
1328         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
1329
1330                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1331
1332                 if (m->running_as != SYSTEMD_SYSTEM) {
1333                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
1334                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1335                 }
1336
1337                 reply = dbus_message_new_method_return(message);
1338                 if (!reply)
1339                         goto oom;
1340
1341                 m->exit_code = MANAGER_HALT;
1342
1343         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
1344
1345                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1346
1347                 if (m->running_as != SYSTEMD_SYSTEM) {
1348                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
1349                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1350                 }
1351
1352                 reply = dbus_message_new_method_return(message);
1353                 if (!reply)
1354                         goto oom;
1355
1356                 m->exit_code = MANAGER_KEXEC;
1357
1358         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
1359                 const char *switch_root, *switch_root_init;
1360                 char *u, *v;
1361                 bool good;
1362
1363                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1364
1365                 if (!dbus_message_get_args(
1366                                     message,
1367                                     &error,
1368                                     DBUS_TYPE_STRING, &switch_root,
1369                                     DBUS_TYPE_STRING, &switch_root_init,
1370                                     DBUS_TYPE_INVALID))
1371                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1372
1373                 if (path_equal(switch_root, "/") || !path_is_absolute(switch_root))
1374                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1375
1376                 if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
1377                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1378
1379                 if (m->running_as != SYSTEMD_SYSTEM) {
1380                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
1381                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1382                 }
1383
1384                 /* Safety check */
1385                 if (isempty(switch_root_init)) {
1386                         good = path_is_os_tree(switch_root);
1387                         if (!good)
1388                                 log_error("Not switching root: %s does not seem to be an OS tree. /etc/os-release is missing.", switch_root);
1389                 }
1390                 else {
1391                         _cleanup_free_ char *p = NULL;
1392
1393                         p = strjoin(switch_root, "/", switch_root_init, NULL);
1394                         if (!p)
1395                                 goto oom;
1396
1397                         good = access(p, X_OK) >= 0;
1398                         if (!good)
1399                                 log_error("Not switching root: cannot execute new init %s", p);
1400                 }
1401                 if (!good)
1402                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1403
1404                 u = strdup(switch_root);
1405                 if (!u)
1406                         goto oom;
1407
1408                 if (!isempty(switch_root_init)) {
1409                         v = strdup(switch_root_init);
1410                         if (!v) {
1411                                 free(u);
1412                                 goto oom;
1413                         }
1414                 } else
1415                         v = NULL;
1416
1417                 free(m->switch_root);
1418                 free(m->switch_root_init);
1419                 m->switch_root = u;
1420                 m->switch_root_init = v;
1421
1422                 reply = dbus_message_new_method_return(message);
1423                 if (!reply)
1424                         goto oom;
1425
1426                 m->exit_code = MANAGER_SWITCH_ROOT;
1427
1428         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
1429                 _cleanup_strv_free_ char **l = NULL;
1430                 char **e = NULL;
1431
1432                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1433
1434                 r = bus_parse_strv(message, &l);
1435                 if (r == -ENOMEM)
1436                         goto oom;
1437                 if (r < 0)
1438                         return bus_send_error_reply(connection, message, NULL, r);
1439                 if (!strv_env_is_valid(l))
1440                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1441
1442                 e = strv_env_merge(2, m->environment, l);
1443                 if (!e)
1444                         goto oom;
1445
1446                 reply = dbus_message_new_method_return(message);
1447                 if (!reply) {
1448                         strv_free(e);
1449                         goto oom;
1450                 }
1451
1452                 strv_free(m->environment);
1453                 m->environment = e;
1454
1455         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
1456                 _cleanup_strv_free_ char **l = NULL;
1457                 char **e = NULL;
1458
1459                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1460
1461                 r = bus_parse_strv(message, &l);
1462                 if (r == -ENOMEM)
1463                         goto oom;
1464                 if (r < 0)
1465                         return bus_send_error_reply(connection, message, NULL, r);
1466                 if (!strv_env_name_or_assignment_is_valid(l))
1467                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1468
1469                 e = strv_env_delete(m->environment, 1, l);
1470                 if (!e)
1471                         goto oom;
1472
1473                 reply = dbus_message_new_method_return(message);
1474                 if (!reply) {
1475                         strv_free(e);
1476                         goto oom;
1477                 }
1478
1479                 strv_free(m->environment);
1480                 m->environment = e;
1481
1482         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
1483                 _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL;
1484                 char **f = NULL;
1485                 DBusMessageIter iter;
1486
1487                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1488
1489                 if (!dbus_message_iter_init(message, &iter))
1490                         goto oom;
1491
1492                 r = bus_parse_strv_iter(&iter, &l_unset);
1493                 if (r == -ENOMEM)
1494                         goto oom;
1495                 if (r < 0)
1496                         return bus_send_error_reply(connection, message, NULL, r);
1497                 if (!strv_env_name_or_assignment_is_valid(l_unset))
1498                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1499
1500                 if (!dbus_message_iter_next(&iter))
1501                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1502
1503                 r = bus_parse_strv_iter(&iter, &l_set);
1504                 if (r == -ENOMEM)
1505                         goto oom;
1506                 if (r < 0)
1507                         return bus_send_error_reply(connection, message, NULL, r);
1508                 if (!strv_env_is_valid(l_set))
1509                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1510
1511                 e = strv_env_delete(m->environment, 1, l_unset);
1512                 if (!e)
1513                         goto oom;
1514
1515                 f = strv_env_merge(2, e, l_set);
1516                 if (!f)
1517                         goto oom;
1518
1519                 reply = dbus_message_new_method_return(message);
1520                 if (!reply) {
1521                         strv_free(f);
1522                         goto oom;
1523                 }
1524
1525                 strv_free(m->environment);
1526                 m->environment = f;
1527         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
1528                 DBusMessageIter iter, sub, sub2;
1529                 Hashmap *h;
1530                 Iterator i;
1531                 UnitFileList *item;
1532
1533                 SELINUX_ACCESS_CHECK(connection, message, "status");
1534
1535                 reply = dbus_message_new_method_return(message);
1536                 if (!reply)
1537                         goto oom;
1538
1539                 h = hashmap_new(string_hash_func, string_compare_func);
1540                 if (!h)
1541                         goto oom;
1542
1543                 r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
1544                 if (r < 0) {
1545                         unit_file_list_free(h);
1546                         return bus_send_error_reply(connection, message, NULL, r);
1547                 }
1548
1549                 dbus_message_iter_init_append(reply, &iter);
1550
1551                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
1552                         unit_file_list_free(h);
1553                         goto oom;
1554                 }
1555
1556                 HASHMAP_FOREACH(item, h, i) {
1557                         const char *state;
1558
1559                         state = unit_file_state_to_string(item->state);
1560                         assert(state);
1561
1562                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
1563                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
1564                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1565                             !dbus_message_iter_close_container(&sub, &sub2)) {
1566                                 unit_file_list_free(h);
1567                                 goto oom;
1568                         }
1569                 }
1570
1571                 unit_file_list_free(h);
1572
1573                 if (!dbus_message_iter_close_container(&iter, &sub))
1574                         goto oom;
1575
1576         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
1577                 const char *name;
1578                 UnitFileState state;
1579                 const char *s;
1580
1581                 SELINUX_ACCESS_CHECK(connection, message, "status");
1582
1583                 if (!dbus_message_get_args(
1584                                     message,
1585                                     &error,
1586                                     DBUS_TYPE_STRING, &name,
1587                                     DBUS_TYPE_INVALID))
1588                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1589
1590                 state = unit_file_get_state(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
1591                 if (state < 0)
1592                         return bus_send_error_reply(connection, message, NULL, state);
1593
1594                 s = unit_file_state_to_string(state);
1595                 assert(s);
1596
1597                 reply = dbus_message_new_method_return(message);
1598                 if (!reply)
1599                         goto oom;
1600
1601                 if (!dbus_message_append_args(
1602                                     reply,
1603                                     DBUS_TYPE_STRING, &s,
1604                                     DBUS_TYPE_INVALID))
1605                         goto oom;
1606         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
1607                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
1608                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
1609                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
1610                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles") ||
1611                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetDefaultTarget")) {
1612
1613                 char **l = NULL;
1614                 DBusMessageIter iter;
1615                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1616                 UnitFileChange *changes = NULL;
1617                 unsigned n_changes = 0;
1618                 dbus_bool_t runtime, force;
1619                 int carries_install_info = -1;
1620
1621                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "MaskUnitFiles") ? "disable" : "enable");
1622
1623                 if (!dbus_message_iter_init(message, &iter))
1624                         goto oom;
1625
1626                 r = bus_parse_strv_iter(&iter, &l);
1627                 if (r < 0) {
1628                         if (r == -ENOMEM)
1629                                 goto oom;
1630
1631                         return bus_send_error_reply(connection, message, NULL, r);
1632                 }
1633
1634                 if (!dbus_message_iter_next(&iter) ||
1635                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
1636                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
1637                         strv_free(l);
1638                         return bus_send_error_reply(connection, message, NULL, -EIO);
1639                 }
1640
1641                 if (streq(member, "EnableUnitFiles")) {
1642                         r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
1643                         carries_install_info = r;
1644                 } else if (streq(member, "ReenableUnitFiles")) {
1645                         r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
1646                         carries_install_info = r;
1647                 } else if (streq(member, "LinkUnitFiles"))
1648                         r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
1649                 else if (streq(member, "PresetUnitFiles")) {
1650                         r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
1651                         carries_install_info = r;
1652                 } else if (streq(member, "MaskUnitFiles"))
1653                         r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
1654                 else if (streq(member, "SetDefaultTarget"))
1655                         r = unit_file_set_default(scope, NULL, l[0], &changes, &n_changes);
1656                 else
1657                         assert_not_reached("Uh? Wrong method");
1658
1659                 strv_free(l);
1660                 bus_manager_send_unit_files_changed(m);
1661
1662                 if (r < 0) {
1663                         unit_file_changes_free(changes, n_changes);
1664                         return bus_send_error_reply(connection, message, NULL, r);
1665                 }
1666
1667                 reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
1668                 unit_file_changes_free(changes, n_changes);
1669
1670                 if (!reply)
1671                         goto oom;
1672
1673         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
1674                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
1675
1676                 char **l = NULL;
1677                 DBusMessageIter iter;
1678                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1679                 UnitFileChange *changes = NULL;
1680                 unsigned n_changes = 0;
1681                 dbus_bool_t runtime;
1682
1683                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "UnmaskUnitFiles") ? "enable" : "disable");
1684
1685                 if (!dbus_message_iter_init(message, &iter))
1686                         goto oom;
1687
1688                 r = bus_parse_strv_iter(&iter, &l);
1689                 if (r < 0) {
1690                         if (r == -ENOMEM)
1691                                 goto oom;
1692
1693                         return bus_send_error_reply(connection, message, NULL, r);
1694                 }
1695
1696                 if (!dbus_message_iter_next(&iter) ||
1697                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
1698                         strv_free(l);
1699                         return bus_send_error_reply(connection, message, NULL, -EIO);
1700                 }
1701
1702                 if (streq(member, "DisableUnitFiles"))
1703                         r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
1704                 else if (streq(member, "UnmaskUnitFiles"))
1705                         r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
1706                 else
1707                         assert_not_reached("Uh? Wrong method");
1708
1709                 strv_free(l);
1710                 bus_manager_send_unit_files_changed(m);
1711
1712                 if (r < 0) {
1713                         unit_file_changes_free(changes, n_changes);
1714                         return bus_send_error_reply(connection, message, NULL, r);
1715                 }
1716
1717                 reply = message_from_file_changes(message, changes, n_changes, -1);
1718                 unit_file_changes_free(changes, n_changes);
1719
1720                 if (!reply)
1721                         goto oom;
1722
1723         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetDefaultTarget")) {
1724                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1725                 _cleanup_free_ char *default_target = NULL;
1726
1727                 reply = dbus_message_new_method_return(message);
1728                 if (!reply)
1729                         goto oom;
1730
1731                 r = unit_file_get_default(scope, NULL, &default_target);
1732                 if (r < 0)
1733                         return bus_send_error_reply(connection, message, NULL, r);
1734
1735                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_target, DBUS_TYPE_INVALID)) {
1736                         goto oom;
1737                 }
1738
1739         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitProperties")) {
1740                 DBusMessageIter iter;
1741                 dbus_bool_t runtime;
1742                 const char *name;
1743                 Unit *u;
1744
1745                 if (!dbus_message_iter_init(message, &iter))
1746                         goto oom;
1747
1748                 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true) < 0 ||
1749                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
1750                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1751
1752                 u = manager_get_unit(m, name);
1753                 if (!u) {
1754                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
1755                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1756                 }
1757
1758                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
1759
1760                 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
1761                 if (r < 0)
1762                         return bus_send_error_reply(connection, message, &error, r);
1763
1764                 reply = dbus_message_new_method_return(message);
1765                 if (!reply)
1766                         goto oom;
1767
1768         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartTransientUnit")) {
1769                 const char *name, *smode;
1770                 DBusMessageIter iter;
1771                 JobMode mode;
1772                 UnitType t;
1773                 Unit *u;
1774
1775                 if (!dbus_message_iter_init(message, &iter))
1776                         goto oom;
1777
1778                 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true) < 0 ||
1779                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &smode, true) < 0)
1780                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1781
1782                 t = unit_name_to_type(name);
1783                 if (t < 0)
1784                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1785                 if (!unit_vtable[t]->can_transient) {
1786                         dbus_set_error(&error, DBUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
1787                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1788                 }
1789
1790                 mode = job_mode_from_string(smode);
1791                 if (mode < 0) {
1792                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
1793                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1794                 }
1795
1796                 r = manager_load_unit(m, name, NULL, NULL, &u);
1797                 if (r < 0)
1798                         return bus_send_error_reply(connection, message, &error, r);
1799
1800                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
1801
1802                 if (u->load_state != UNIT_NOT_FOUND || set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0) {
1803                         dbus_set_error(&error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
1804                         return bus_send_error_reply(connection, message, &error, -EEXIST);
1805                 }
1806
1807                 /* OK, the unit failed to load and is unreferenced,
1808                  * now let's fill in the transient data instead */
1809                 r = unit_make_transient(u);
1810                 if (r < 0)
1811                         return bus_send_error_reply(connection, message, &error, r);
1812
1813                 /* Set our properties */
1814                 r = bus_unit_set_properties(u, &iter, UNIT_RUNTIME, false, &error);
1815                 if (r < 0)
1816                         return bus_send_error_reply(connection, message, &error, r);
1817
1818                 /* And load this stub fully */
1819                 r = unit_load(u);
1820                 if (r < 0)
1821                         return bus_send_error_reply(connection, message, &error, r);
1822
1823                 manager_dispatch_load_queue(m);
1824
1825                 /* Finally, start it */
1826                 return bus_unit_queue_job(connection, message, u, JOB_START, mode, false);
1827
1828         } else {
1829                 const BusBoundProperties bps[] = {
1830                         { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
1831                         { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
1832                         { NULL, }
1833                 };
1834
1835                 SELINUX_ACCESS_CHECK(connection, message, "status");
1836
1837                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1838         }
1839
1840         if (job_type != _JOB_TYPE_INVALID) {
1841                 const char *name, *smode, *old_name = NULL;
1842                 JobMode mode;
1843                 Unit *u;
1844                 dbus_bool_t b;
1845
1846                 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
1847                         b = dbus_message_get_args(
1848                                         message,
1849                                         &error,
1850                                         DBUS_TYPE_STRING, &old_name,
1851                                         DBUS_TYPE_STRING, &name,
1852                                         DBUS_TYPE_STRING, &smode,
1853                                         DBUS_TYPE_INVALID);
1854                 else
1855                         b = dbus_message_get_args(
1856                                         message,
1857                                         &error,
1858                                         DBUS_TYPE_STRING, &name,
1859                                         DBUS_TYPE_STRING, &smode,
1860                                         DBUS_TYPE_INVALID);
1861                 if (!b)
1862                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1863
1864                 if (old_name) {
1865                         u = manager_get_unit(m, old_name);
1866                         if (!u || !u->job || u->job->type != JOB_START) {
1867                                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
1868                                 return bus_send_error_reply(connection, message, &error, -ENOENT);
1869                         }
1870                 }
1871
1872                 mode = job_mode_from_string(smode);
1873                 if (mode < 0) {
1874                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
1875                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1876                 }
1877
1878                 r = manager_load_unit(m, name, NULL, &error, &u);
1879                 if (r < 0)
1880                         return bus_send_error_reply(connection, message, &error, r);
1881
1882                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
1883         }
1884
1885         if (reply)
1886                 if (!bus_maybe_send_reply(connection, message, reply))
1887                         goto oom;
1888
1889         return DBUS_HANDLER_RESULT_HANDLED;
1890
1891 oom:
1892         dbus_error_free(&error);
1893
1894         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1895 }
1896
1897 const DBusObjectPathVTable bus_manager_vtable = {
1898         .message_function = bus_manager_message_handler
1899 };