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