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