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