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