chiark / gitweb /
logind: add infrastructure to keep track of machines, and move to slices
[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         { "DefaultControllers",          bus_property_append_strv,       "as", offsetof(Manager, default_controllers),          true },
617         { "DefaultStandardOutput",       bus_manager_append_exec_output, "s",  offsetof(Manager, default_std_output)            },
618         { "DefaultStandardError",        bus_manager_append_exec_output, "s",  offsetof(Manager, default_std_error)             },
619         { "RuntimeWatchdogUSec",         bus_property_append_usec,       "t",  offsetof(Manager, runtime_watchdog),             false, bus_manager_set_runtime_watchdog_usec },
620         { "ShutdownWatchdogUSec",        bus_property_append_usec,       "t",  offsetof(Manager, shutdown_watchdog),            false, bus_property_set_usec },
621         { "Virtualization",              bus_manager_append_virt,        "s",  0,                                               },
622         { NULL, }
623 };
624
625 static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
626         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
627         _cleanup_free_ char * path = NULL;
628         Manager *m = data;
629         int r;
630         DBusError error;
631         JobType job_type = _JOB_TYPE_INVALID;
632         bool reload_if_possible = false;
633         const char *member;
634
635         assert(connection);
636         assert(message);
637         assert(m);
638
639         dbus_error_init(&error);
640
641         member = dbus_message_get_member(message);
642
643         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
644                 const char *name;
645                 Unit *u;
646
647                 if (!dbus_message_get_args(
648                                     message,
649                                     &error,
650                                     DBUS_TYPE_STRING, &name,
651                                     DBUS_TYPE_INVALID))
652                         return bus_send_error_reply(connection, message, &error, -EINVAL);
653
654                 u = manager_get_unit(m, name);
655                 if (!u) {
656                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
657                         return bus_send_error_reply(connection, message, &error, -ENOENT);
658                 }
659
660                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
661
662                 reply = dbus_message_new_method_return(message);
663                 if (!reply)
664                         goto oom;
665
666                 path = unit_dbus_path(u);
667                 if (!path)
668                         goto oom;
669
670                 if (!dbus_message_append_args(
671                                     reply,
672                                     DBUS_TYPE_OBJECT_PATH, &path,
673                                     DBUS_TYPE_INVALID))
674                         goto oom;
675         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
676                 Unit *u;
677                 uint32_t pid;
678
679                 if (!dbus_message_get_args(
680                                     message,
681                                     &error,
682                                     DBUS_TYPE_UINT32, &pid,
683                                     DBUS_TYPE_INVALID))
684                         return bus_send_error_reply(connection, message, &error, -EINVAL);
685
686                 u = cgroup_unit_by_pid(m, (pid_t) pid);
687                 if (!u) {
688                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
689                         return bus_send_error_reply(connection, message, &error, -ENOENT);
690                 }
691
692                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
693
694                 reply = dbus_message_new_method_return(message);
695                 if (!reply)
696                         goto oom;
697
698                 path = unit_dbus_path(u);
699                 if (!path)
700                         goto oom;
701
702                 if (!dbus_message_append_args(
703                                     reply,
704                                     DBUS_TYPE_OBJECT_PATH, &path,
705                                     DBUS_TYPE_INVALID))
706                         goto oom;
707         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
708                 const char *name;
709                 Unit *u;
710
711                 if (!dbus_message_get_args(
712                                     message,
713                                     &error,
714                                     DBUS_TYPE_STRING, &name,
715                                     DBUS_TYPE_INVALID))
716                         return bus_send_error_reply(connection, message, &error, -EINVAL);
717
718                 r = manager_load_unit(m, name, NULL, &error, &u);
719                 if (r < 0)
720                         return bus_send_error_reply(connection, message, &error, r);
721
722                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
723
724                 reply = dbus_message_new_method_return(message);
725                 if (!reply)
726                         goto oom;
727
728                 path = unit_dbus_path(u);
729                 if (!path)
730                         goto oom;
731
732                 if (!dbus_message_append_args(
733                                     reply,
734                                     DBUS_TYPE_OBJECT_PATH, &path,
735                                     DBUS_TYPE_INVALID))
736                         goto oom;
737
738         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
739                 job_type = JOB_START;
740         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
741                 job_type = JOB_START;
742         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
743                 job_type = JOB_STOP;
744         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
745                 job_type = JOB_RELOAD;
746         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
747                 job_type = JOB_RESTART;
748         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
749                 job_type = JOB_TRY_RESTART;
750         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
751                 reload_if_possible = true;
752                 job_type = JOB_RESTART;
753         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
754                 reload_if_possible = true;
755                 job_type = JOB_TRY_RESTART;
756         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
757                 const char *name, *swho;
758                 int32_t signo;
759                 Unit *u;
760                 KillWho who;
761
762                 if (!dbus_message_get_args(
763                                     message,
764                                     &error,
765                                     DBUS_TYPE_STRING, &name,
766                                     DBUS_TYPE_STRING, &swho,
767                                     DBUS_TYPE_INT32, &signo,
768                                     DBUS_TYPE_INVALID))
769                         return bus_send_error_reply(connection, message, &error, -EINVAL);
770
771                 if (isempty(swho))
772                         who = KILL_ALL;
773                 else {
774                         who = kill_who_from_string(swho);
775                         if (who < 0)
776                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
777                 }
778
779                 if (signo <= 0 || signo >= _NSIG)
780                         return bus_send_error_reply(connection, message, &error, -EINVAL);
781
782                 u = manager_get_unit(m, name);
783                 if (!u) {
784                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
785                         return bus_send_error_reply(connection, message, &error, -ENOENT);
786                 }
787
788                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
789
790                 r = unit_kill(u, who, signo, &error);
791                 if (r < 0)
792                         return bus_send_error_reply(connection, message, &error, r);
793
794                 reply = dbus_message_new_method_return(message);
795                 if (!reply)
796                         goto oom;
797
798         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
799                 uint32_t id;
800                 Job *j;
801
802                 if (!dbus_message_get_args(
803                                     message,
804                                     &error,
805                                     DBUS_TYPE_UINT32, &id,
806                                     DBUS_TYPE_INVALID))
807                         return bus_send_error_reply(connection, message, &error, -EINVAL);
808
809                 j = manager_get_job(m, id);
810                 if (!j) {
811                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
812                         return bus_send_error_reply(connection, message, &error, -ENOENT);
813                 }
814
815                 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
816
817                 reply = dbus_message_new_method_return(message);
818                 if (!reply)
819                         goto oom;
820
821                 path = job_dbus_path(j);
822                 if (!path)
823                         goto oom;
824
825                 if (!dbus_message_append_args(
826                                     reply,
827                                     DBUS_TYPE_OBJECT_PATH, &path,
828                                     DBUS_TYPE_INVALID))
829                         goto oom;
830
831         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CancelJob")) {
832                 uint32_t id;
833                 Job *j;
834
835                 if (!dbus_message_get_args(
836                                     message,
837                                     &error,
838                                     DBUS_TYPE_UINT32, &id,
839                                     DBUS_TYPE_INVALID))
840                         return bus_send_error_reply(connection, message, &error, -EINVAL);
841
842                 j = manager_get_job(m, id);
843                 if (!j) {
844                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
845                         return bus_send_error_reply(connection, message, &error, -ENOENT);
846                 }
847
848                 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
849                 job_finish_and_invalidate(j, JOB_CANCELED, true);
850
851                 reply = dbus_message_new_method_return(message);
852                 if (!reply)
853                         goto oom;
854
855         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
856
857                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
858                 manager_clear_jobs(m);
859
860                 reply = dbus_message_new_method_return(message);
861                 if (!reply)
862                         goto oom;
863
864         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
865
866                 SELINUX_ACCESS_CHECK(connection, message, "reload");
867
868                 manager_reset_failed(m);
869
870                 reply = dbus_message_new_method_return(message);
871                 if (!reply)
872                         goto oom;
873
874         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
875                 const char *name;
876                 Unit *u;
877
878                 if (!dbus_message_get_args(
879                                     message,
880                                     &error,
881                                     DBUS_TYPE_STRING, &name,
882                                     DBUS_TYPE_INVALID))
883                         return bus_send_error_reply(connection, message, &error, -EINVAL);
884
885                 u = manager_get_unit(m, name);
886                 if (!u) {
887                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
888                         return bus_send_error_reply(connection, message, &error, -ENOENT);
889                 }
890
891                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
892
893                 unit_reset_failed(u);
894
895                 reply = dbus_message_new_method_return(message);
896                 if (!reply)
897                         goto oom;
898
899         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) {
900                 const char *name;
901                 Unit *u;
902                 DBusMessageIter iter;
903
904                 if (!dbus_message_iter_init(message, &iter))
905                         goto oom;
906
907                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
908                 if (r < 0)
909                         return bus_send_error_reply(connection, message, NULL, r);
910
911                 u = manager_get_unit(m, name);
912                 if (!u) {
913                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
914                         return bus_send_error_reply(connection, message, &error, -ENOENT);
915                 }
916
917                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
918
919                 r = bus_unit_cgroup_set(u, &iter);
920                 if (r < 0)
921                         return bus_send_error_reply(connection, message, NULL, r);
922
923                 reply = dbus_message_new_method_return(message);
924                 if (!reply)
925                         goto oom;
926
927         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) {
928                 const char *name;
929                 Unit *u;
930                 DBusMessageIter iter;
931
932                 if (!dbus_message_iter_init(message, &iter))
933                         goto oom;
934
935                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
936                 if (r < 0)
937                         return bus_send_error_reply(connection, message, NULL, r);
938
939                 u = manager_get_unit(m, name);
940                 if (!u) {
941                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
942                         return bus_send_error_reply(connection, message, &error, -ENOENT);
943                 }
944
945                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
946
947                 r = bus_unit_cgroup_unset(u, &iter);
948                 if (r < 0)
949                         return bus_send_error_reply(connection, message, NULL, r);
950
951                 reply = dbus_message_new_method_return(message);
952                 if (!reply)
953                         goto oom;
954
955         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) {
956                 const char *name;
957                 Unit *u;
958                 DBusMessageIter iter;
959
960                 if (!dbus_message_iter_init(message, &iter))
961                         goto oom;
962
963                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
964                 if (r < 0)
965                         return bus_send_error_reply(connection, message, NULL, r);
966
967                 u = manager_get_unit(m, name);
968                 if (!u) {
969                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
970                         return bus_send_error_reply(connection, message, &error, -ENOENT);
971                 }
972
973                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
974
975                 r = bus_unit_cgroup_attribute_set(u, &iter);
976                 if (r < 0)
977                         return bus_send_error_reply(connection, message, NULL, r);
978
979                 reply = dbus_message_new_method_return(message);
980                 if (!reply)
981                         goto oom;
982
983         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) {
984                 const char *name;
985                 Unit *u;
986                 DBusMessageIter iter;
987
988                 if (!dbus_message_iter_init(message, &iter))
989                         goto oom;
990
991                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
992                 if (r < 0)
993                         return bus_send_error_reply(connection, message, NULL, r);
994
995                 u = manager_get_unit(m, name);
996                 if (!u) {
997                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
998                         return bus_send_error_reply(connection, message, &error, -ENOENT);
999                 }
1000
1001                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
1002
1003                 r = bus_unit_cgroup_attribute_unset(u, &iter);
1004                 if (r < 0)
1005                         return bus_send_error_reply(connection, message, NULL, r);
1006
1007                 reply = dbus_message_new_method_return(message);
1008                 if (!reply)
1009                         goto oom;
1010
1011         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) {
1012                 const char *name;
1013                 Unit *u;
1014                 DBusMessageIter iter;
1015                 _cleanup_strv_free_ char **list = NULL;
1016
1017                 if (!dbus_message_iter_init(message, &iter))
1018                         goto oom;
1019
1020                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
1021                 if (r < 0)
1022                         return bus_send_error_reply(connection, message, NULL, r);
1023
1024                 u = manager_get_unit(m, name);
1025                 if (!u) {
1026                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
1027                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1028                 }
1029
1030                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
1031
1032                 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
1033                 if (r < 0)
1034                         return bus_send_error_reply(connection, message, NULL, r);
1035
1036                 reply = dbus_message_new_method_return(message);
1037                 if (!reply)
1038                         goto oom;
1039
1040                 dbus_message_iter_init_append(reply, &iter);
1041                 if (bus_append_strv_iter(&iter, list) < 0)
1042                         goto oom;
1043
1044         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
1045                 DBusMessageIter iter, sub;
1046                 Iterator i;
1047                 Unit *u;
1048                 const char *k;
1049
1050                 SELINUX_ACCESS_CHECK(connection, message, "status");
1051
1052                 reply = dbus_message_new_method_return(message);
1053                 if (!reply)
1054                         goto oom;
1055
1056                 dbus_message_iter_init_append(reply, &iter);
1057
1058                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
1059                         goto oom;
1060
1061                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1062                         char *u_path, *j_path;
1063                         const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
1064                         DBusMessageIter sub2;
1065                         uint32_t job_id;
1066                         Unit *f;
1067
1068                         if (k != u->id)
1069                                 continue;
1070
1071                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1072                                 goto oom;
1073
1074                         description = unit_description(u);
1075                         load_state = unit_load_state_to_string(u->load_state);
1076                         active_state = unit_active_state_to_string(unit_active_state(u));
1077                         sub_state = unit_sub_state_to_string(u);
1078
1079                         f = unit_following(u);
1080                         following = f ? f->id : "";
1081
1082                         u_path = unit_dbus_path(u);
1083                         if (!u_path)
1084                                 goto oom;
1085
1086                         if (u->job) {
1087                                 job_id = (uint32_t) u->job->id;
1088
1089                                 if (!(j_path = job_dbus_path(u->job))) {
1090                                         free(u_path);
1091                                         goto oom;
1092                                 }
1093
1094                                 sjob_type = job_type_to_string(u->job->type);
1095                         } else {
1096                                 job_id = 0;
1097                                 j_path = u_path;
1098                                 sjob_type = "";
1099                         }
1100
1101                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) ||
1102                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
1103                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
1104                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
1105                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
1106                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
1107                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
1108                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
1109                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
1110                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
1111                                 free(u_path);
1112                                 if (u->job)
1113                                         free(j_path);
1114                                 goto oom;
1115                         }
1116
1117                         free(u_path);
1118                         if (u->job)
1119                                 free(j_path);
1120
1121                         if (!dbus_message_iter_close_container(&sub, &sub2))
1122                                 goto oom;
1123                 }
1124
1125                 if (!dbus_message_iter_close_container(&iter, &sub))
1126                         goto oom;
1127
1128         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
1129                 DBusMessageIter iter, sub;
1130                 Iterator i;
1131                 Job *j;
1132
1133                 SELINUX_ACCESS_CHECK(connection, message, "status");
1134
1135                 reply = dbus_message_new_method_return(message);
1136                 if (!reply)
1137                         goto oom;
1138
1139                 dbus_message_iter_init_append(reply, &iter);
1140
1141                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
1142                         goto oom;
1143
1144                 HASHMAP_FOREACH(j, m->jobs, i) {
1145                         char *u_path, *j_path;
1146                         const char *state, *type;
1147                         uint32_t id;
1148                         DBusMessageIter sub2;
1149
1150                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1151                                 goto oom;
1152
1153                         id = (uint32_t) j->id;
1154                         state = job_state_to_string(j->state);
1155                         type = job_type_to_string(j->type);
1156
1157                         j_path = job_dbus_path(j);
1158                         if (!j_path)
1159                                 goto oom;
1160
1161                         u_path = unit_dbus_path(j->unit);
1162                         if (!u_path) {
1163                                 free(j_path);
1164                                 goto oom;
1165                         }
1166
1167                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
1168                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) ||
1169                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
1170                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1171                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
1172                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
1173                                 free(j_path);
1174                                 free(u_path);
1175                                 goto oom;
1176                         }
1177
1178                         free(j_path);
1179                         free(u_path);
1180
1181                         if (!dbus_message_iter_close_container(&sub, &sub2))
1182                                 goto oom;
1183                 }
1184
1185                 if (!dbus_message_iter_close_container(&iter, &sub))
1186                         goto oom;
1187
1188         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
1189                 char *client;
1190                 Set *s;
1191
1192                 SELINUX_ACCESS_CHECK(connection, message, "status");
1193
1194                 s = BUS_CONNECTION_SUBSCRIBED(m, connection);
1195                 if (!s) {
1196                         s = set_new(string_hash_func, string_compare_func);
1197                         if (!s)
1198                                 goto oom;
1199
1200                         if (!dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL)) {
1201                                 set_free(s);
1202                                 goto oom;
1203                         }
1204                 }
1205
1206                 client = strdup(bus_message_get_sender_with_fallback(message));
1207                 if (!client)
1208                         goto oom;
1209
1210                 r = set_consume(s, client);
1211                 if (r < 0)
1212                         return bus_send_error_reply(connection, message, NULL, r);
1213
1214                 reply = dbus_message_new_method_return(message);
1215                 if (!reply)
1216                         goto oom;
1217
1218         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
1219                 char *client;
1220
1221                 SELINUX_ACCESS_CHECK(connection, message, "status");
1222
1223                 client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message));
1224                 if (!client) {
1225                         dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
1226                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1227                 }
1228
1229                 free(client);
1230
1231                 reply = dbus_message_new_method_return(message);
1232                 if (!reply)
1233                         goto oom;
1234
1235         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
1236                 FILE *f;
1237                 char *dump = NULL;
1238                 size_t size;
1239
1240                 SELINUX_ACCESS_CHECK(connection, message, "status");
1241
1242                 reply = dbus_message_new_method_return(message);
1243                 if (!reply)
1244                         goto oom;
1245
1246                 f = open_memstream(&dump, &size);
1247                 if (!f)
1248                         goto oom;
1249
1250                 manager_dump_units(m, f, NULL);
1251                 manager_dump_jobs(m, f, NULL);
1252
1253                 if (ferror(f)) {
1254                         fclose(f);
1255                         free(dump);
1256                         goto oom;
1257                 }
1258
1259                 fclose(f);
1260
1261                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
1262                         free(dump);
1263                         goto oom;
1264                 }
1265
1266                 free(dump);
1267         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
1268                 const char *name;
1269                 dbus_bool_t cleanup;
1270                 Snapshot *s;
1271
1272                 SELINUX_ACCESS_CHECK(connection, message, "start");
1273
1274                 if (!dbus_message_get_args(
1275                                     message,
1276                                     &error,
1277                                     DBUS_TYPE_STRING, &name,
1278                                     DBUS_TYPE_BOOLEAN, &cleanup,
1279                                     DBUS_TYPE_INVALID))
1280                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1281
1282                 if (isempty(name))
1283                         name = NULL;
1284
1285                 r = snapshot_create(m, name, cleanup, &error, &s);
1286                 if (r < 0)
1287                         return bus_send_error_reply(connection, message, &error, r);
1288
1289                 reply = dbus_message_new_method_return(message);
1290                 if (!reply)
1291                         goto oom;
1292
1293                 path = unit_dbus_path(UNIT(s));
1294                 if (!path)
1295                         goto oom;
1296
1297                 if (!dbus_message_append_args(
1298                                     reply,
1299                                     DBUS_TYPE_OBJECT_PATH, &path,
1300                                     DBUS_TYPE_INVALID))
1301                         goto oom;
1302
1303         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) {
1304                 const char *name;
1305                 Unit *u;
1306
1307                 if (!dbus_message_get_args(
1308                                     message,
1309                                     &error,
1310                                     DBUS_TYPE_STRING, &name,
1311                                     DBUS_TYPE_INVALID))
1312                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1313
1314                 u = manager_get_unit(m, name);
1315                 if (!u) {
1316                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
1317                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1318                 }
1319
1320                 if (u->type != UNIT_SNAPSHOT) {
1321                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name);
1322                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1323                 }
1324
1325                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
1326                 snapshot_remove(SNAPSHOT(u));
1327
1328                 reply = dbus_message_new_method_return(message);
1329                 if (!reply)
1330                         goto oom;
1331
1332         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1333                 char *introspection = NULL;
1334                 FILE *f;
1335                 Iterator i;
1336                 Unit *u;
1337                 Job *j;
1338                 const char *k;
1339                 size_t size;
1340
1341                 SELINUX_ACCESS_CHECK(connection, message, "status");
1342
1343                 reply = dbus_message_new_method_return(message);
1344                 if (!reply)
1345                         goto oom;
1346
1347                 /* We roll our own introspection code here, instead of
1348                  * relying on bus_default_message_handler() because we
1349                  * need to generate our introspection string
1350                  * dynamically. */
1351
1352                 f = open_memstream(&introspection, &size);
1353                 if (!f)
1354                         goto oom;
1355
1356                 fputs(INTROSPECTION_BEGIN, f);
1357
1358                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1359                         char *p;
1360
1361                         if (k != u->id)
1362                                 continue;
1363
1364                         p = bus_path_escape(k);
1365                         if (!p) {
1366                                 fclose(f);
1367                                 free(introspection);
1368                                 goto oom;
1369                         }
1370
1371                         fprintf(f, "<node name=\"unit/%s\"/>", p);
1372                         free(p);
1373                 }
1374
1375                 HASHMAP_FOREACH(j, m->jobs, i)
1376                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
1377
1378                 fputs(INTROSPECTION_END, f);
1379
1380                 if (ferror(f)) {
1381                         fclose(f);
1382                         free(introspection);
1383                         goto oom;
1384                 }
1385
1386                 fclose(f);
1387
1388                 if (!introspection)
1389                         goto oom;
1390
1391                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1392                         free(introspection);
1393                         goto oom;
1394                 }
1395
1396                 free(introspection);
1397
1398         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
1399
1400                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1401
1402                 assert(!m->queued_message);
1403
1404                 /* Instead of sending the reply back right away, we
1405                  * just remember that we need to and then send it
1406                  * after the reload is finished. That way the caller
1407                  * knows when the reload finished. */
1408
1409                 m->queued_message = dbus_message_new_method_return(message);
1410                 if (!m->queued_message)
1411                         goto oom;
1412
1413                 m->queued_message_connection = connection;
1414                 m->exit_code = MANAGER_RELOAD;
1415
1416         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
1417
1418                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1419
1420                 /* We don't send a reply back here, the client should
1421                  * just wait for us disconnecting. */
1422
1423                 m->exit_code = MANAGER_REEXECUTE;
1424
1425         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
1426
1427                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1428
1429                 if (m->running_as == SYSTEMD_SYSTEM) {
1430                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
1431                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1432                 }
1433
1434                 reply = dbus_message_new_method_return(message);
1435                 if (!reply)
1436                         goto oom;
1437
1438                 m->exit_code = MANAGER_EXIT;
1439
1440         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
1441
1442                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1443
1444                 if (m->running_as != SYSTEMD_SYSTEM) {
1445                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
1446                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1447                 }
1448
1449                 reply = dbus_message_new_method_return(message);
1450                 if (!reply)
1451                         goto oom;
1452
1453                 m->exit_code = MANAGER_REBOOT;
1454
1455         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
1456
1457                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1458
1459                 if (m->running_as != SYSTEMD_SYSTEM) {
1460                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
1461                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1462                 }
1463
1464                 reply = dbus_message_new_method_return(message);
1465                 if (!reply)
1466                         goto oom;
1467
1468                 m->exit_code = MANAGER_POWEROFF;
1469
1470         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
1471
1472                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1473
1474                 if (m->running_as != SYSTEMD_SYSTEM) {
1475                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
1476                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1477                 }
1478
1479                 reply = dbus_message_new_method_return(message);
1480                 if (!reply)
1481                         goto oom;
1482
1483                 m->exit_code = MANAGER_HALT;
1484
1485         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
1486
1487                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1488
1489                 if (m->running_as != SYSTEMD_SYSTEM) {
1490                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
1491                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1492                 }
1493
1494                 reply = dbus_message_new_method_return(message);
1495                 if (!reply)
1496                         goto oom;
1497
1498                 m->exit_code = MANAGER_KEXEC;
1499
1500         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
1501                 const char *switch_root, *switch_root_init;
1502                 char *u, *v;
1503                 bool good;
1504
1505                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1506
1507                 if (!dbus_message_get_args(
1508                                     message,
1509                                     &error,
1510                                     DBUS_TYPE_STRING, &switch_root,
1511                                     DBUS_TYPE_STRING, &switch_root_init,
1512                                     DBUS_TYPE_INVALID))
1513                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1514
1515                 if (path_equal(switch_root, "/") || !path_is_absolute(switch_root))
1516                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1517
1518                 if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
1519                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1520
1521                 if (m->running_as != SYSTEMD_SYSTEM) {
1522                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
1523                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1524                 }
1525
1526                 /* Safety check */
1527                 if (isempty(switch_root_init)) {
1528                         good = path_is_os_tree(switch_root);
1529                         if (!good)
1530                                 log_error("Not switching root: %s does not seem to be an OS tree. /etc/os-release is missing.", switch_root);
1531                 }
1532                 else {
1533                         _cleanup_free_ char *p = NULL;
1534
1535                         p = strjoin(switch_root, "/", switch_root_init, NULL);
1536                         if (!p)
1537                                 goto oom;
1538
1539                         good = access(p, X_OK) >= 0;
1540                         if (!good)
1541                                 log_error("Not switching root: cannot execute new init %s", p);
1542                 }
1543                 if (!good)
1544                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1545
1546                 u = strdup(switch_root);
1547                 if (!u)
1548                         goto oom;
1549
1550                 if (!isempty(switch_root_init)) {
1551                         v = strdup(switch_root_init);
1552                         if (!v) {
1553                                 free(u);
1554                                 goto oom;
1555                         }
1556                 } else
1557                         v = NULL;
1558
1559                 free(m->switch_root);
1560                 free(m->switch_root_init);
1561                 m->switch_root = u;
1562                 m->switch_root_init = v;
1563
1564                 reply = dbus_message_new_method_return(message);
1565                 if (!reply)
1566                         goto oom;
1567
1568                 m->exit_code = MANAGER_SWITCH_ROOT;
1569
1570         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
1571                 _cleanup_strv_free_ char **l = NULL;
1572                 char **e = NULL;
1573
1574                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1575
1576                 r = bus_parse_strv(message, &l);
1577                 if (r == -ENOMEM)
1578                         goto oom;
1579                 if (r < 0)
1580                         return bus_send_error_reply(connection, message, NULL, r);
1581                 if (!strv_env_is_valid(l))
1582                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1583
1584                 e = strv_env_merge(2, m->environment, l);
1585                 if (!e)
1586                         goto oom;
1587
1588                 reply = dbus_message_new_method_return(message);
1589                 if (!reply) {
1590                         strv_free(e);
1591                         goto oom;
1592                 }
1593
1594                 strv_free(m->environment);
1595                 m->environment = e;
1596
1597         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
1598                 _cleanup_strv_free_ char **l = NULL;
1599                 char **e = NULL;
1600
1601                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1602
1603                 r = bus_parse_strv(message, &l);
1604                 if (r == -ENOMEM)
1605                         goto oom;
1606                 if (r < 0)
1607                         return bus_send_error_reply(connection, message, NULL, r);
1608                 if (!strv_env_name_or_assignment_is_valid(l))
1609                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1610
1611                 e = strv_env_delete(m->environment, 1, l);
1612                 if (!e)
1613                         goto oom;
1614
1615                 reply = dbus_message_new_method_return(message);
1616                 if (!reply) {
1617                         strv_free(e);
1618                         goto oom;
1619                 }
1620
1621                 strv_free(m->environment);
1622                 m->environment = e;
1623
1624         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
1625                 _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL;
1626                 char **f = NULL;
1627                 DBusMessageIter iter;
1628
1629                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1630
1631                 if (!dbus_message_iter_init(message, &iter))
1632                         goto oom;
1633
1634                 r = bus_parse_strv_iter(&iter, &l_unset);
1635                 if (r == -ENOMEM)
1636                         goto oom;
1637                 if (r < 0)
1638                         return bus_send_error_reply(connection, message, NULL, r);
1639                 if (!strv_env_name_or_assignment_is_valid(l_unset))
1640                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1641
1642                 if (!dbus_message_iter_next(&iter))
1643                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1644
1645                 r = bus_parse_strv_iter(&iter, &l_set);
1646                 if (r == -ENOMEM)
1647                         goto oom;
1648                 if (r < 0)
1649                         return bus_send_error_reply(connection, message, NULL, r);
1650                 if (!strv_env_is_valid(l_set))
1651                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1652
1653                 e = strv_env_delete(m->environment, 1, l_unset);
1654                 if (!e)
1655                         goto oom;
1656
1657                 f = strv_env_merge(2, e, l_set);
1658                 if (!f)
1659                         goto oom;
1660
1661                 reply = dbus_message_new_method_return(message);
1662                 if (!reply) {
1663                         strv_free(f);
1664                         goto oom;
1665                 }
1666
1667                 strv_free(m->environment);
1668                 m->environment = f;
1669         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
1670                 DBusMessageIter iter, sub, sub2;
1671                 Hashmap *h;
1672                 Iterator i;
1673                 UnitFileList *item;
1674
1675                 SELINUX_ACCESS_CHECK(connection, message, "status");
1676
1677                 reply = dbus_message_new_method_return(message);
1678                 if (!reply)
1679                         goto oom;
1680
1681                 h = hashmap_new(string_hash_func, string_compare_func);
1682                 if (!h)
1683                         goto oom;
1684
1685                 r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
1686                 if (r < 0) {
1687                         unit_file_list_free(h);
1688                         return bus_send_error_reply(connection, message, NULL, r);
1689                 }
1690
1691                 dbus_message_iter_init_append(reply, &iter);
1692
1693                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
1694                         unit_file_list_free(h);
1695                         goto oom;
1696                 }
1697
1698                 HASHMAP_FOREACH(item, h, i) {
1699                         const char *state;
1700
1701                         state = unit_file_state_to_string(item->state);
1702                         assert(state);
1703
1704                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
1705                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
1706                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1707                             !dbus_message_iter_close_container(&sub, &sub2)) {
1708                                 unit_file_list_free(h);
1709                                 goto oom;
1710                         }
1711                 }
1712
1713                 unit_file_list_free(h);
1714
1715                 if (!dbus_message_iter_close_container(&iter, &sub))
1716                         goto oom;
1717
1718         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
1719                 const char *name;
1720                 UnitFileState state;
1721                 const char *s;
1722
1723                 SELINUX_ACCESS_CHECK(connection, message, "status");
1724
1725                 if (!dbus_message_get_args(
1726                                     message,
1727                                     &error,
1728                                     DBUS_TYPE_STRING, &name,
1729                                     DBUS_TYPE_INVALID))
1730                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1731
1732                 state = unit_file_get_state(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
1733                 if (state < 0)
1734                         return bus_send_error_reply(connection, message, NULL, state);
1735
1736                 s = unit_file_state_to_string(state);
1737                 assert(s);
1738
1739                 reply = dbus_message_new_method_return(message);
1740                 if (!reply)
1741                         goto oom;
1742
1743                 if (!dbus_message_append_args(
1744                                     reply,
1745                                     DBUS_TYPE_STRING, &s,
1746                                     DBUS_TYPE_INVALID))
1747                         goto oom;
1748         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
1749                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
1750                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
1751                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
1752                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles") ||
1753                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetDefaultTarget")) {
1754
1755                 char **l = NULL;
1756                 DBusMessageIter iter;
1757                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1758                 UnitFileChange *changes = NULL;
1759                 unsigned n_changes = 0;
1760                 dbus_bool_t runtime, force;
1761                 int carries_install_info = -1;
1762
1763                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "MaskUnitFiles") ? "disable" : "enable");
1764
1765                 if (!dbus_message_iter_init(message, &iter))
1766                         goto oom;
1767
1768                 r = bus_parse_strv_iter(&iter, &l);
1769                 if (r < 0) {
1770                         if (r == -ENOMEM)
1771                                 goto oom;
1772
1773                         return bus_send_error_reply(connection, message, NULL, r);
1774                 }
1775
1776                 if (!dbus_message_iter_next(&iter) ||
1777                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
1778                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
1779                         strv_free(l);
1780                         return bus_send_error_reply(connection, message, NULL, -EIO);
1781                 }
1782
1783                 if (streq(member, "EnableUnitFiles")) {
1784                         r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
1785                         carries_install_info = r;
1786                 } else if (streq(member, "ReenableUnitFiles")) {
1787                         r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
1788                         carries_install_info = r;
1789                 } else if (streq(member, "LinkUnitFiles"))
1790                         r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
1791                 else if (streq(member, "PresetUnitFiles")) {
1792                         r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
1793                         carries_install_info = r;
1794                 } else if (streq(member, "MaskUnitFiles"))
1795                         r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
1796                 else if (streq(member, "SetDefaultTarget"))
1797                         r = unit_file_set_default(scope, NULL, l[0], &changes, &n_changes);
1798                 else
1799                         assert_not_reached("Uh? Wrong method");
1800
1801                 strv_free(l);
1802                 bus_manager_send_unit_files_changed(m);
1803
1804                 if (r < 0) {
1805                         unit_file_changes_free(changes, n_changes);
1806                         return bus_send_error_reply(connection, message, NULL, r);
1807                 }
1808
1809                 reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
1810                 unit_file_changes_free(changes, n_changes);
1811
1812                 if (!reply)
1813                         goto oom;
1814
1815         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
1816                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
1817
1818                 char **l = NULL;
1819                 DBusMessageIter iter;
1820                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1821                 UnitFileChange *changes = NULL;
1822                 unsigned n_changes = 0;
1823                 dbus_bool_t runtime;
1824
1825                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "UnmaskUnitFiles") ? "enable" : "disable");
1826
1827                 if (!dbus_message_iter_init(message, &iter))
1828                         goto oom;
1829
1830                 r = bus_parse_strv_iter(&iter, &l);
1831                 if (r < 0) {
1832                         if (r == -ENOMEM)
1833                                 goto oom;
1834
1835                         return bus_send_error_reply(connection, message, NULL, r);
1836                 }
1837
1838                 if (!dbus_message_iter_next(&iter) ||
1839                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
1840                         strv_free(l);
1841                         return bus_send_error_reply(connection, message, NULL, -EIO);
1842                 }
1843
1844                 if (streq(member, "DisableUnitFiles"))
1845                         r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
1846                 else if (streq(member, "UnmaskUnitFiles"))
1847                         r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
1848                 else
1849                         assert_not_reached("Uh? Wrong method");
1850
1851                 strv_free(l);
1852                 bus_manager_send_unit_files_changed(m);
1853
1854                 if (r < 0) {
1855                         unit_file_changes_free(changes, n_changes);
1856                         return bus_send_error_reply(connection, message, NULL, r);
1857                 }
1858
1859                 reply = message_from_file_changes(message, changes, n_changes, -1);
1860                 unit_file_changes_free(changes, n_changes);
1861
1862                 if (!reply)
1863                         goto oom;
1864
1865         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetDefaultTarget")) {
1866                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1867                 _cleanup_free_ char *default_target = NULL;
1868
1869                 reply = dbus_message_new_method_return(message);
1870                 if (!reply)
1871                         goto oom;
1872
1873                 r = unit_file_get_default(scope, NULL, &default_target);
1874
1875                 if (r < 0)
1876                         return bus_send_error_reply(connection, message, NULL, r);
1877
1878                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_target, DBUS_TYPE_INVALID)) {
1879                         goto oom;
1880                 }
1881         } else {
1882                 const BusBoundProperties bps[] = {
1883                         { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
1884                         { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
1885                         { NULL, }
1886                 };
1887
1888                 SELINUX_ACCESS_CHECK(connection, message, "status");
1889
1890                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1891         }
1892
1893         if (job_type != _JOB_TYPE_INVALID) {
1894                 const char *name, *smode, *old_name = NULL;
1895                 JobMode mode;
1896                 Unit *u;
1897                 dbus_bool_t b;
1898
1899                 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
1900                         b = dbus_message_get_args(
1901                                         message,
1902                                         &error,
1903                                         DBUS_TYPE_STRING, &old_name,
1904                                         DBUS_TYPE_STRING, &name,
1905                                         DBUS_TYPE_STRING, &smode,
1906                                         DBUS_TYPE_INVALID);
1907                 else
1908                         b = dbus_message_get_args(
1909                                         message,
1910                                         &error,
1911                                         DBUS_TYPE_STRING, &name,
1912                                         DBUS_TYPE_STRING, &smode,
1913                                         DBUS_TYPE_INVALID);
1914                 if (!b)
1915                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1916
1917                 if (old_name) {
1918                         u = manager_get_unit(m, old_name);
1919                         if (!u || !u->job || u->job->type != JOB_START) {
1920                                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
1921                                 return bus_send_error_reply(connection, message, &error, -ENOENT);
1922                         }
1923                 }
1924
1925                 mode = job_mode_from_string(smode);
1926                 if (mode < 0) {
1927                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
1928                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1929                 }
1930
1931                 r = manager_load_unit(m, name, NULL, &error, &u);
1932                 if (r < 0)
1933                         return bus_send_error_reply(connection, message, &error, r);
1934
1935                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
1936         }
1937
1938         if (reply)
1939                 if (!bus_maybe_send_reply(connection, message, reply))
1940                         goto oom;
1941
1942         return DBUS_HANDLER_RESULT_HANDLED;
1943
1944 oom:
1945         dbus_error_free(&error);
1946
1947         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1948 }
1949
1950 const DBusObjectPathVTable bus_manager_vtable = {
1951         .message_function = bus_manager_message_handler
1952 };