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