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