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