chiark / gitweb /
Small cleanup
[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_consume(s, client);
1190                 if (r < 0)
1191                         return bus_send_error_reply(connection, message, NULL, r);
1192
1193                 reply = dbus_message_new_method_return(message);
1194                 if (!reply)
1195                         goto oom;
1196
1197         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
1198                 char *client;
1199
1200                 SELINUX_ACCESS_CHECK(connection, message, "status");
1201
1202                 client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message));
1203                 if (!client) {
1204                         dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
1205                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1206                 }
1207
1208                 free(client);
1209
1210                 reply = dbus_message_new_method_return(message);
1211                 if (!reply)
1212                         goto oom;
1213
1214         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
1215                 FILE *f;
1216                 char *dump = NULL;
1217                 size_t size;
1218
1219                 SELINUX_ACCESS_CHECK(connection, message, "status");
1220
1221                 reply = dbus_message_new_method_return(message);
1222                 if (!reply)
1223                         goto oom;
1224
1225                 f = open_memstream(&dump, &size);
1226                 if (!f)
1227                         goto oom;
1228
1229                 manager_dump_units(m, f, NULL);
1230                 manager_dump_jobs(m, f, NULL);
1231
1232                 if (ferror(f)) {
1233                         fclose(f);
1234                         free(dump);
1235                         goto oom;
1236                 }
1237
1238                 fclose(f);
1239
1240                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
1241                         free(dump);
1242                         goto oom;
1243                 }
1244
1245                 free(dump);
1246         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
1247                 const char *name;
1248                 dbus_bool_t cleanup;
1249                 Snapshot *s;
1250
1251                 SELINUX_ACCESS_CHECK(connection, message, "start");
1252
1253                 if (!dbus_message_get_args(
1254                                     message,
1255                                     &error,
1256                                     DBUS_TYPE_STRING, &name,
1257                                     DBUS_TYPE_BOOLEAN, &cleanup,
1258                                     DBUS_TYPE_INVALID))
1259                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1260
1261                 if (isempty(name))
1262                         name = NULL;
1263
1264                 r = snapshot_create(m, name, cleanup, &error, &s);
1265                 if (r < 0)
1266                         return bus_send_error_reply(connection, message, &error, r);
1267
1268                 reply = dbus_message_new_method_return(message);
1269                 if (!reply)
1270                         goto oom;
1271
1272                 path = unit_dbus_path(UNIT(s));
1273                 if (!path)
1274                         goto oom;
1275
1276                 if (!dbus_message_append_args(
1277                                     reply,
1278                                     DBUS_TYPE_OBJECT_PATH, &path,
1279                                     DBUS_TYPE_INVALID))
1280                         goto oom;
1281
1282         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) {
1283                 const char *name;
1284                 Unit *u;
1285
1286                 if (!dbus_message_get_args(
1287                                     message,
1288                                     &error,
1289                                     DBUS_TYPE_STRING, &name,
1290                                     DBUS_TYPE_INVALID))
1291                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1292
1293                 u = manager_get_unit(m, name);
1294                 if (!u) {
1295                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
1296                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1297                 }
1298
1299                 if (u->type != UNIT_SNAPSHOT) {
1300                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name);
1301                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1302                 }
1303
1304                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
1305                 snapshot_remove(SNAPSHOT(u));
1306
1307                 reply = dbus_message_new_method_return(message);
1308                 if (!reply)
1309                         goto oom;
1310
1311         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1312                 char *introspection = NULL;
1313                 FILE *f;
1314                 Iterator i;
1315                 Unit *u;
1316                 Job *j;
1317                 const char *k;
1318                 size_t size;
1319
1320                 SELINUX_ACCESS_CHECK(connection, message, "status");
1321
1322                 reply = dbus_message_new_method_return(message);
1323                 if (!reply)
1324                         goto oom;
1325
1326                 /* We roll our own introspection code here, instead of
1327                  * relying on bus_default_message_handler() because we
1328                  * need to generate our introspection string
1329                  * dynamically. */
1330
1331                 f = open_memstream(&introspection, &size);
1332                 if (!f)
1333                         goto oom;
1334
1335                 fputs(INTROSPECTION_BEGIN, f);
1336
1337                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1338                         char *p;
1339
1340                         if (k != u->id)
1341                                 continue;
1342
1343                         p = bus_path_escape(k);
1344                         if (!p) {
1345                                 fclose(f);
1346                                 free(introspection);
1347                                 goto oom;
1348                         }
1349
1350                         fprintf(f, "<node name=\"unit/%s\"/>", p);
1351                         free(p);
1352                 }
1353
1354                 HASHMAP_FOREACH(j, m->jobs, i)
1355                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
1356
1357                 fputs(INTROSPECTION_END, f);
1358
1359                 if (ferror(f)) {
1360                         fclose(f);
1361                         free(introspection);
1362                         goto oom;
1363                 }
1364
1365                 fclose(f);
1366
1367                 if (!introspection)
1368                         goto oom;
1369
1370                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1371                         free(introspection);
1372                         goto oom;
1373                 }
1374
1375                 free(introspection);
1376
1377         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
1378
1379                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1380
1381                 assert(!m->queued_message);
1382
1383                 /* Instead of sending the reply back right away, we
1384                  * just remember that we need to and then send it
1385                  * after the reload is finished. That way the caller
1386                  * knows when the reload finished. */
1387
1388                 m->queued_message = dbus_message_new_method_return(message);
1389                 if (!m->queued_message)
1390                         goto oom;
1391
1392                 m->queued_message_connection = connection;
1393                 m->exit_code = MANAGER_RELOAD;
1394
1395         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
1396
1397                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1398
1399                 /* We don't send a reply back here, the client should
1400                  * just wait for us disconnecting. */
1401
1402                 m->exit_code = MANAGER_REEXECUTE;
1403
1404         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
1405
1406                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1407
1408                 if (m->running_as == SYSTEMD_SYSTEM) {
1409                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
1410                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1411                 }
1412
1413                 reply = dbus_message_new_method_return(message);
1414                 if (!reply)
1415                         goto oom;
1416
1417                 m->exit_code = MANAGER_EXIT;
1418
1419         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
1420
1421                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1422
1423                 if (m->running_as != SYSTEMD_SYSTEM) {
1424                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
1425                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1426                 }
1427
1428                 reply = dbus_message_new_method_return(message);
1429                 if (!reply)
1430                         goto oom;
1431
1432                 m->exit_code = MANAGER_REBOOT;
1433
1434         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
1435
1436                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1437
1438                 if (m->running_as != SYSTEMD_SYSTEM) {
1439                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
1440                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1441                 }
1442
1443                 reply = dbus_message_new_method_return(message);
1444                 if (!reply)
1445                         goto oom;
1446
1447                 m->exit_code = MANAGER_POWEROFF;
1448
1449         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
1450
1451                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1452
1453                 if (m->running_as != SYSTEMD_SYSTEM) {
1454                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
1455                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1456                 }
1457
1458                 reply = dbus_message_new_method_return(message);
1459                 if (!reply)
1460                         goto oom;
1461
1462                 m->exit_code = MANAGER_HALT;
1463
1464         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
1465
1466                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1467
1468                 if (m->running_as != SYSTEMD_SYSTEM) {
1469                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
1470                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1471                 }
1472
1473                 reply = dbus_message_new_method_return(message);
1474                 if (!reply)
1475                         goto oom;
1476
1477                 m->exit_code = MANAGER_KEXEC;
1478
1479         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
1480                 const char *switch_root, *switch_root_init;
1481                 char *u, *v;
1482                 bool good;
1483
1484                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1485
1486                 if (!dbus_message_get_args(
1487                                     message,
1488                                     &error,
1489                                     DBUS_TYPE_STRING, &switch_root,
1490                                     DBUS_TYPE_STRING, &switch_root_init,
1491                                     DBUS_TYPE_INVALID))
1492                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1493
1494                 if (path_equal(switch_root, "/") || !path_is_absolute(switch_root))
1495                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1496
1497                 if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
1498                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1499
1500                 if (m->running_as != SYSTEMD_SYSTEM) {
1501                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
1502                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1503                 }
1504
1505                 /* Safety check */
1506                 if (isempty(switch_root_init))
1507                         good = path_is_os_tree(switch_root);
1508                 else {
1509                         _cleanup_free_ char *p = NULL;
1510
1511                         p = strjoin(switch_root, "/", switch_root_init, NULL);
1512                         if (!p)
1513                                 goto oom;
1514
1515                         good = access(p, X_OK) >= 0;
1516                 }
1517                 if (!good)
1518                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1519
1520                 u = strdup(switch_root);
1521                 if (!u)
1522                         goto oom;
1523
1524                 if (!isempty(switch_root_init)) {
1525                         v = strdup(switch_root_init);
1526                         if (!v) {
1527                                 free(u);
1528                                 goto oom;
1529                         }
1530                 } else
1531                         v = NULL;
1532
1533                 free(m->switch_root);
1534                 free(m->switch_root_init);
1535                 m->switch_root = u;
1536                 m->switch_root_init = v;
1537
1538                 reply = dbus_message_new_method_return(message);
1539                 if (!reply)
1540                         goto oom;
1541
1542                 m->exit_code = MANAGER_SWITCH_ROOT;
1543
1544         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
1545                 _cleanup_strv_free_ char **l = NULL;
1546                 char **e = NULL;
1547
1548                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1549
1550                 r = bus_parse_strv(message, &l);
1551                 if (r == -ENOMEM)
1552                         goto oom;
1553                 if (r < 0)
1554                         return bus_send_error_reply(connection, message, NULL, r);
1555                 if (!strv_env_is_valid(l))
1556                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1557
1558                 e = strv_env_merge(2, m->environment, l);
1559                 if (!e)
1560                         goto oom;
1561
1562                 reply = dbus_message_new_method_return(message);
1563                 if (!reply) {
1564                         strv_free(e);
1565                         goto oom;
1566                 }
1567
1568                 strv_free(m->environment);
1569                 m->environment = e;
1570
1571         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
1572                 _cleanup_strv_free_ char **l = NULL;
1573                 char **e = NULL;
1574
1575                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1576
1577                 r = bus_parse_strv(message, &l);
1578                 if (r == -ENOMEM)
1579                         goto oom;
1580                 if (r < 0)
1581                         return bus_send_error_reply(connection, message, NULL, r);
1582                 if (!strv_env_name_or_assignment_is_valid(l))
1583                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1584
1585                 e = strv_env_delete(m->environment, 1, l);
1586                 if (!e)
1587                         goto oom;
1588
1589                 reply = dbus_message_new_method_return(message);
1590                 if (!reply) {
1591                         strv_free(e);
1592                         goto oom;
1593                 }
1594
1595                 strv_free(m->environment);
1596                 m->environment = e;
1597
1598         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
1599                 _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL;
1600                 char **f = NULL;
1601                 DBusMessageIter iter;
1602
1603                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1604
1605                 if (!dbus_message_iter_init(message, &iter))
1606                         goto oom;
1607
1608                 r = bus_parse_strv_iter(&iter, &l_unset);
1609                 if (r == -ENOMEM)
1610                         goto oom;
1611                 if (r < 0)
1612                         return bus_send_error_reply(connection, message, NULL, r);
1613                 if (!strv_env_name_or_assignment_is_valid(l_unset))
1614                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1615
1616                 if (!dbus_message_iter_next(&iter))
1617                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1618
1619                 r = bus_parse_strv_iter(&iter, &l_set);
1620                 if (r == -ENOMEM)
1621                         goto oom;
1622                 if (r < 0)
1623                         return bus_send_error_reply(connection, message, NULL, r);
1624                 if (!strv_env_is_valid(l_set))
1625                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1626
1627                 e = strv_env_delete(m->environment, 1, l_unset);
1628                 if (!e)
1629                         goto oom;
1630
1631                 f = strv_env_merge(2, e, l_set);
1632                 if (!f)
1633                         goto oom;
1634
1635                 reply = dbus_message_new_method_return(message);
1636                 if (!reply) {
1637                         strv_free(f);
1638                         goto oom;
1639                 }
1640
1641                 strv_free(m->environment);
1642                 m->environment = f;
1643         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
1644                 DBusMessageIter iter, sub, sub2;
1645                 Hashmap *h;
1646                 Iterator i;
1647                 UnitFileList *item;
1648
1649                 SELINUX_ACCESS_CHECK(connection, message, "status");
1650
1651                 reply = dbus_message_new_method_return(message);
1652                 if (!reply)
1653                         goto oom;
1654
1655                 h = hashmap_new(string_hash_func, string_compare_func);
1656                 if (!h)
1657                         goto oom;
1658
1659                 r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
1660                 if (r < 0) {
1661                         unit_file_list_free(h);
1662                         return bus_send_error_reply(connection, message, NULL, r);
1663                 }
1664
1665                 dbus_message_iter_init_append(reply, &iter);
1666
1667                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
1668                         unit_file_list_free(h);
1669                         goto oom;
1670                 }
1671
1672                 HASHMAP_FOREACH(item, h, i) {
1673                         const char *state;
1674
1675                         state = unit_file_state_to_string(item->state);
1676                         assert(state);
1677
1678                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
1679                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
1680                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1681                             !dbus_message_iter_close_container(&sub, &sub2)) {
1682                                 unit_file_list_free(h);
1683                                 goto oom;
1684                         }
1685                 }
1686
1687                 unit_file_list_free(h);
1688
1689                 if (!dbus_message_iter_close_container(&iter, &sub))
1690                         goto oom;
1691
1692         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
1693                 const char *name;
1694                 UnitFileState state;
1695                 const char *s;
1696
1697                 SELINUX_ACCESS_CHECK(connection, message, "status");
1698
1699                 if (!dbus_message_get_args(
1700                                     message,
1701                                     &error,
1702                                     DBUS_TYPE_STRING, &name,
1703                                     DBUS_TYPE_INVALID))
1704                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1705
1706                 state = unit_file_get_state(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
1707                 if (state < 0)
1708                         return bus_send_error_reply(connection, message, NULL, state);
1709
1710                 s = unit_file_state_to_string(state);
1711                 assert(s);
1712
1713                 reply = dbus_message_new_method_return(message);
1714                 if (!reply)
1715                         goto oom;
1716
1717                 if (!dbus_message_append_args(
1718                                     reply,
1719                                     DBUS_TYPE_STRING, &s,
1720                                     DBUS_TYPE_INVALID))
1721                         goto oom;
1722         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
1723                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
1724                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
1725                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
1726                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) {
1727
1728                 char **l = NULL;
1729                 DBusMessageIter iter;
1730                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1731                 UnitFileChange *changes = NULL;
1732                 unsigned n_changes = 0;
1733                 dbus_bool_t runtime, force;
1734                 int carries_install_info = -1;
1735
1736                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "MaskUnitFiles") ? "disable" : "enable");
1737
1738                 if (!dbus_message_iter_init(message, &iter))
1739                         goto oom;
1740
1741                 r = bus_parse_strv_iter(&iter, &l);
1742                 if (r < 0) {
1743                         if (r == -ENOMEM)
1744                                 goto oom;
1745
1746                         return bus_send_error_reply(connection, message, NULL, r);
1747                 }
1748
1749                 if (!dbus_message_iter_next(&iter) ||
1750                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
1751                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
1752                         strv_free(l);
1753                         return bus_send_error_reply(connection, message, NULL, -EIO);
1754                 }
1755
1756                 if (streq(member, "EnableUnitFiles")) {
1757                         r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
1758                         carries_install_info = r;
1759                 } else if (streq(member, "ReenableUnitFiles")) {
1760                         r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
1761                         carries_install_info = r;
1762                 } else if (streq(member, "LinkUnitFiles"))
1763                         r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
1764                 else if (streq(member, "PresetUnitFiles")) {
1765                         r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
1766                         carries_install_info = r;
1767                 } else if (streq(member, "MaskUnitFiles"))
1768                         r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
1769                 else
1770                         assert_not_reached("Uh? Wrong method");
1771
1772                 strv_free(l);
1773                 bus_manager_send_unit_files_changed(m);
1774
1775                 if (r < 0) {
1776                         unit_file_changes_free(changes, n_changes);
1777                         return bus_send_error_reply(connection, message, NULL, r);
1778                 }
1779
1780                 reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
1781                 unit_file_changes_free(changes, n_changes);
1782
1783                 if (!reply)
1784                         goto oom;
1785
1786         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
1787                    dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
1788
1789                 char **l = NULL;
1790                 DBusMessageIter iter;
1791                 UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
1792                 UnitFileChange *changes = NULL;
1793                 unsigned n_changes = 0;
1794                 dbus_bool_t runtime;
1795
1796                 SELINUX_ACCESS_CHECK(connection, message, streq(member, "UnmaskUnitFiles") ? "enable" : "disable");
1797
1798                 if (!dbus_message_iter_init(message, &iter))
1799                         goto oom;
1800
1801                 r = bus_parse_strv_iter(&iter, &l);
1802                 if (r < 0) {
1803                         if (r == -ENOMEM)
1804                                 goto oom;
1805
1806                         return bus_send_error_reply(connection, message, NULL, r);
1807                 }
1808
1809                 if (!dbus_message_iter_next(&iter) ||
1810                     bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
1811                         strv_free(l);
1812                         return bus_send_error_reply(connection, message, NULL, -EIO);
1813                 }
1814
1815                 if (streq(member, "DisableUnitFiles"))
1816                         r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
1817                 else if (streq(member, "UnmaskUnitFiles"))
1818                         r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
1819                 else
1820                         assert_not_reached("Uh? Wrong method");
1821
1822                 strv_free(l);
1823                 bus_manager_send_unit_files_changed(m);
1824
1825                 if (r < 0) {
1826                         unit_file_changes_free(changes, n_changes);
1827                         return bus_send_error_reply(connection, message, NULL, r);
1828                 }
1829
1830                 reply = message_from_file_changes(message, changes, n_changes, -1);
1831                 unit_file_changes_free(changes, n_changes);
1832
1833                 if (!reply)
1834                         goto oom;
1835
1836         } else {
1837                 const BusBoundProperties bps[] = {
1838                         { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
1839                         { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
1840                         { NULL, }
1841                 };
1842
1843                 SELINUX_ACCESS_CHECK(connection, message, "status");
1844
1845                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
1846         }
1847
1848         if (job_type != _JOB_TYPE_INVALID) {
1849                 const char *name, *smode, *old_name = NULL;
1850                 JobMode mode;
1851                 Unit *u;
1852                 dbus_bool_t b;
1853
1854                 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
1855                         b = dbus_message_get_args(
1856                                         message,
1857                                         &error,
1858                                         DBUS_TYPE_STRING, &old_name,
1859                                         DBUS_TYPE_STRING, &name,
1860                                         DBUS_TYPE_STRING, &smode,
1861                                         DBUS_TYPE_INVALID);
1862                 else
1863                         b = dbus_message_get_args(
1864                                         message,
1865                                         &error,
1866                                         DBUS_TYPE_STRING, &name,
1867                                         DBUS_TYPE_STRING, &smode,
1868                                         DBUS_TYPE_INVALID);
1869                 if (!b)
1870                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1871
1872                 if (old_name) {
1873                         u = manager_get_unit(m, old_name);
1874                         if (!u || !u->job || u->job->type != JOB_START) {
1875                                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
1876                                 return bus_send_error_reply(connection, message, &error, -ENOENT);
1877                         }
1878                 }
1879
1880                 mode = job_mode_from_string(smode);
1881                 if (mode < 0) {
1882                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
1883                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1884                 }
1885
1886                 r = manager_load_unit(m, name, NULL, &error, &u);
1887                 if (r < 0)
1888                         return bus_send_error_reply(connection, message, &error, r);
1889
1890                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
1891         }
1892
1893         if (reply)
1894                 if (!bus_maybe_send_reply(connection, message, reply))
1895                         goto oom;
1896
1897         return DBUS_HANDLER_RESULT_HANDLED;
1898
1899 oom:
1900         dbus_error_free(&error);
1901
1902         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1903 }
1904
1905 const DBusObjectPathVTable bus_manager_vtable = {
1906         .message_function = bus_manager_message_handler
1907 };