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