chiark / gitweb /
systemctl: add new "get-cgroup-attr" to query current cgroup attribute value
[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
40 #define BUS_MANAGER_INTERFACE_BEGIN                                     \
41         " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
42
43 #define BUS_MANAGER_INTERFACE_METHODS                                   \
44         "  <method name=\"GetUnit\">\n"                                 \
45         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
46         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
47         "  </method>\n"                                                 \
48         "  <method name=\"GetUnitByPID\">\n"                            \
49         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
50         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
51         "  </method>\n"                                                 \
52         "  <method name=\"LoadUnit\">\n"                                \
53         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
54         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
55         "  </method>\n"                                                 \
56         "  <method name=\"StartUnit\">\n"                               \
57         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
58         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
59         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
60         "  </method>\n"                                                 \
61         "  <method name=\"StartUnitReplace\">\n"                        \
62         "   <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n"     \
63         "   <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n"     \
64         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
65         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
66         "  </method>\n"                                                 \
67         "  <method name=\"StopUnit\">\n"                                \
68         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
69         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
70         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
71         "  </method>\n"                                                 \
72         "  <method name=\"ReloadUnit\">\n"                              \
73         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
74         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
75         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
76         "  </method>\n"                                                 \
77         "  <method name=\"RestartUnit\">\n"                             \
78         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
79         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
80         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
81         "  </method>\n"                                                 \
82         "  <method name=\"TryRestartUnit\">\n"                          \
83         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
84         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
85         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
86         "  </method>\n"                                                 \
87         "  <method name=\"ReloadOrRestartUnit\">\n"                     \
88         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
89         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
90         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
91         "  </method>\n"                                                 \
92         "  <method name=\"ReloadOrTryRestartUnit\">\n"                  \
93         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
94         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
95         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
96         "  </method>\n"                                                 \
97         "  <method name=\"KillUnit\">\n"                                \
98         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
99         "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
100         "   <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n"       \
101         "  </method>\n"                                                 \
102         "  <method name=\"ResetFailedUnit\">\n"                         \
103         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
104         "  </method>\n"                                                 \
105         "  <method name=\"GetUnitControlGroupAttributes\">\n"           \
106         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
107         "   <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n"  \
108         "   <arg name=\"values\" type=\"as\" direction=\"out\"/>\n"      \
109         "  </method>\n"                                                 \
110         "  <method name=\"SetUnitControlGroupAttributes\">\n"           \
111         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
112         "   <arg name=\"attributes\" type=\"a(sss)\" direction=\"in\"/>\n" \
113         "   <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>"         \
114         "  </method>\n"                                                 \
115         "  <method name=\"UnsetUnitControlGroupAttributes\">\n"         \
116         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
117         "   <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \
118         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
119         "  </method>\n"                                                 \
120         "  <method name=\"SetUnitControlGroups\">\n"                    \
121         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
122         "   <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n"      \
123         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
124         "  </method>\n"                                                 \
125         "  <method name=\"UnsetUnitControlGroups\">\n"                  \
126         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
127         "   <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n"      \
128         "   <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>"         \
129         "  </method>\n"                                                 \
130         "  <method name=\"GetJob\">\n"                                  \
131         "   <arg name=\"id\" type=\"u\" direction=\"in\"/>\n"           \
132         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
133         "  </method>\n"                                                 \
134         "  <method name=\"CancelJob\">\n"                               \
135         "   <arg name=\"id\" type=\"u\" direction=\"in\"/>\n"           \
136         "  </method>\n"                                                 \
137         "  <method name=\"ClearJobs\"/>\n"                              \
138         "  <method name=\"ResetFailed\"/>\n"                            \
139         "  <method name=\"ListUnits\">\n"                               \
140         "   <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
141         "  </method>\n"                                                 \
142         "  <method name=\"ListJobs\">\n"                                \
143         "   <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
144         "  </method>\n"                                                 \
145         "  <method name=\"Subscribe\"/>\n"                              \
146         "  <method name=\"Unsubscribe\"/>\n"                            \
147         "  <method name=\"Dump\">\n"                                    \
148         "   <arg name=\"dump\" type=\"s\" direction=\"out\"/>\n"        \
149         "  </method>\n"                                                 \
150         "  <method name=\"CreateSnapshot\">\n"                          \
151         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
152         "   <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n"      \
153         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
154         "  </method>\n"                                                 \
155         "  <method name=\"RemoveSnapshot\">\n"                          \
156         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
157         "  </method>\n"                                                 \
158         "  <method name=\"Reload\"/>\n"                                 \
159         "  <method name=\"Reexecute\"/>\n"                              \
160         "  <method name=\"Exit\"/>\n"                                   \
161         "  <method name=\"Reboot\"/>\n"                                 \
162         "  <method name=\"PowerOff\"/>\n"                               \
163         "  <method name=\"Halt\"/>\n"                                   \
164         "  <method name=\"KExec\"/>\n"                                  \
165         "  <method name=\"SwitchRoot\">\n"                              \
166         "   <arg name=\"new_root\" type=\"s\" direction=\"in\"/>\n"     \
167         "   <arg name=\"init\" type=\"s\" direction=\"in\"/>\n"         \
168         "  </method>\n"                                                 \
169         "  <method name=\"SetEnvironment\">\n"                          \
170         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
171         "  </method>\n"                                                 \
172         "  <method name=\"UnsetEnvironment\">\n"                        \
173         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
174         "  </method>\n"                                                 \
175         "  <method name=\"UnsetAndSetEnvironment\">\n"                  \
176         "   <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n"       \
177         "   <arg name=\"set\" type=\"as\" direction=\"in\"/>\n"         \
178         "  </method>\n"                                                 \
179         "  <method name=\"ListUnitFiles\">\n"                            \
180         "   <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
181         "  </method>\n"                                                 \
182         "  <method name=\"GetUnitFileState\">\n"                        \
183         "   <arg name=\"file\" type=\"s\" direction=\"in\"/>\n"         \
184         "   <arg name=\"state\" type=\"s\" direction=\"out\"/>\n"       \
185         "  </method>\n"                                                 \
186         "  <method name=\"EnableUnitFiles\">\n"                         \
187         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
188         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
189         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
190         "   <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
191         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
192         "  </method>\n"                                                 \
193         "  <method name=\"DisableUnitFiles\">\n"                        \
194         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
195         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
196         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
197         "  </method>\n"                                                 \
198         "  <method name=\"ReenableUnitFiles\">\n"                       \
199         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
200         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
201         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
202         "   <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
203         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
204         "  </method>\n"                                                 \
205         "  <method name=\"LinkUnitFiles\">\n"                           \
206         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
207         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
208         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
209         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
210         "  </method>\n"                                                 \
211         "  <method name=\"PresetUnitFiles\">\n"                         \
212         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
213         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
214         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
215         "   <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
216         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
217         "  </method>\n"                                                 \
218         "  <method name=\"MaskUnitFiles\">\n"                           \
219         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
220         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
221         "   <arg name=\"force\" type=\"b\" direction=\"in\"/>\n"        \
222         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
223         "  </method>\n"                                                 \
224         "  <method name=\"UnmaskUnitFiles\">\n"                         \
225         "   <arg name=\"files\" type=\"as\" direction=\"in\"/>\n"       \
226         "   <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n"      \
227         "   <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
228         "  </method>\n"
229
230 #define BUS_MANAGER_INTERFACE_SIGNALS                                   \
231         "  <signal name=\"UnitNew\">\n"                                 \
232         "   <arg name=\"id\" type=\"s\"/>\n"                            \
233         "   <arg name=\"unit\" type=\"o\"/>\n"                          \
234         "  </signal>\n"                                                 \
235         "  <signal name=\"UnitRemoved\">\n"                             \
236         "   <arg name=\"id\" type=\"s\"/>\n"                            \
237         "   <arg name=\"unit\" type=\"o\"/>\n"                          \
238         "  </signal>\n"                                                 \
239         "  <signal name=\"JobNew\">\n"                                  \
240         "   <arg name=\"id\" type=\"u\"/>\n"                            \
241         "   <arg name=\"job\" type=\"o\"/>\n"                           \
242         "   <arg name=\"unit\" type=\"s\"/>\n"                          \
243         "  </signal>\n"                                                 \
244         "  <signal name=\"JobRemoved\">\n"                              \
245         "   <arg name=\"id\" type=\"u\"/>\n"                            \
246         "   <arg name=\"job\" type=\"o\"/>\n"                           \
247         "   <arg name=\"unit\" type=\"s\"/>\n"                          \
248         "   <arg name=\"result\" type=\"s\"/>\n"                        \
249         "  </signal>"                                                   \
250         "  <signal name=\"StartupFinished\">\n"                         \
251         "   <arg name=\"firmware\" type=\"t\"/>\n"                      \
252         "   <arg name=\"loader\" type=\"t\"/>\n"                        \
253         "   <arg name=\"kernel\" type=\"t\"/>\n"                        \
254         "   <arg name=\"initrd\" type=\"t\"/>\n"                        \
255         "   <arg name=\"userspace\" type=\"t\"/>\n"                     \
256         "   <arg name=\"total\" type=\"t\"/>\n"                         \
257         "  </signal>"                                                   \
258         "  <signal name=\"UnitFilesChanged\"/>\n"
259
260 #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
261         "  <property name=\"Version\" type=\"s\" access=\"read\"/>\n"   \
262         "  <property name=\"Features\" type=\"s\" access=\"read\"/>\n"  \
263         "  <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n"   \
264         "  <property name=\"FirmwareTimestamp\" type=\"t\" access=\"read\"/>\n" \
265         "  <property name=\"FirmwareTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
266         "  <property name=\"LoaderTimestamp\" type=\"t\" access=\"read\"/>\n" \
267         "  <property name=\"LoaderTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
268         "  <property name=\"KernelTimestamp\" type=\"t\" access=\"read\"/>\n" \
269         "  <property name=\"KernelTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
270         "  <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \
271         "  <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
272         "  <property name=\"UserspaceTimestamp\" type=\"t\" access=\"read\"/>\n" \
273         "  <property name=\"UserspaceTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
274         "  <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
275         "  <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
276         "  <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n"  \
277         "  <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \
278         "  <property name=\"NNames\" type=\"u\" access=\"read\"/>\n"    \
279         "  <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n"     \
280         "  <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
281         "  <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
282         "  <property name=\"Progress\" type=\"d\" access=\"read\"/>\n"  \
283         "  <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
284         "  <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
285         "  <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
286         "  <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
287         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
288         "  <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
289         "  <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
290         "  <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
291         "  <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
292         "  <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
293         "  <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n"
294
295 #define BUS_MANAGER_INTERFACE_END                                       \
296         " </interface>\n"
297
298 #define BUS_MANAGER_INTERFACE                                           \
299         BUS_MANAGER_INTERFACE_BEGIN                                     \
300         BUS_MANAGER_INTERFACE_METHODS                                   \
301         BUS_MANAGER_INTERFACE_SIGNALS                                   \
302         BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
303         BUS_MANAGER_INTERFACE_END
304
305 #define INTROSPECTION_BEGIN                                             \
306         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
307         "<node>\n"                                                      \
308         BUS_MANAGER_INTERFACE                                           \
309         BUS_PROPERTIES_INTERFACE                                        \
310         BUS_PEER_INTERFACE                                              \
311         BUS_INTROSPECTABLE_INTERFACE
312
313 #define INTROSPECTION_END                                               \
314         "</node>\n"
315
316 #define INTERFACES_LIST                              \
317         BUS_GENERIC_INTERFACES_LIST                  \
318         "org.freedesktop.systemd1.Manager\0"
319
320 const char bus_manager_interface[] _introspect_("Manager") = BUS_MANAGER_INTERFACE;
321
322 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_exec_output, exec_output, ExecOutput);
323
324 static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, void *data) {
325         const char *t;
326         Manager *m = data;
327         char buf[LINE_MAX] = "", *e = buf, *p = NULL;
328
329         assert(i);
330         assert(property);
331         assert(m);
332
333         if (m->taint_usr)
334                 e = stpcpy(e, "split-usr:");
335
336         if (readlink_malloc("/etc/mtab", &p) < 0)
337                 e = stpcpy(e, "mtab-not-symlink:");
338         else
339                 free(p);
340
341         if (access("/proc/cgroups", F_OK) < 0)
342                 e = stpcpy(e, "cgroups-missing:");
343
344         if (hwclock_is_localtime() > 0)
345                 e = stpcpy(e, "local-hwclock:");
346
347         /* remove the last ':' */
348         if (e != buf)
349                 e[-1] = 0;
350
351         t = buf;
352
353         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
354                 return -ENOMEM;
355
356         return 0;
357 }
358
359 static int bus_manager_append_log_target(DBusMessageIter *i, const char *property, void *data) {
360         const char *t;
361
362         assert(i);
363         assert(property);
364
365         t = log_target_to_string(log_get_target());
366
367         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
368                 return -ENOMEM;
369
370         return 0;
371 }
372
373 static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, void *data) {
374         const char *t;
375
376         assert(i);
377         assert(property);
378
379         dbus_message_iter_get_basic(i, &t);
380
381         return log_set_target_from_string(t);
382 }
383
384 static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
385         char *t;
386         int r;
387
388         assert(i);
389         assert(property);
390
391         r = log_level_to_string_alloc(log_get_max_level(), &t);
392         if (r < 0)
393                 return r;
394
395         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
396                 r = -ENOMEM;
397
398         free(t);
399         return r;
400 }
401
402 static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) {
403         const char *t;
404
405         assert(i);
406         assert(property);
407
408         dbus_message_iter_get_basic(i, &t);
409
410         return log_set_max_level_from_string(t);
411 }
412
413 static int bus_manager_append_n_names(DBusMessageIter *i, const char *property, void *data) {
414         Manager *m = data;
415         uint32_t u;
416
417         assert(i);
418         assert(property);
419         assert(m);
420
421         u = hashmap_size(m->units);
422
423         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
424                 return -ENOMEM;
425
426         return 0;
427 }
428
429 static int bus_manager_append_n_jobs(DBusMessageIter *i, const char *property, void *data) {
430         Manager *m = data;
431         uint32_t u;
432
433         assert(i);
434         assert(property);
435         assert(m);
436
437         u = hashmap_size(m->jobs);
438
439         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
440                 return -ENOMEM;
441
442         return 0;
443 }
444
445 static int bus_manager_append_progress(DBusMessageIter *i, const char *property, void *data) {
446         double d;
447         Manager *m = data;
448
449         assert(i);
450         assert(property);
451         assert(m);
452
453         if (dual_timestamp_is_set(&m->finish_timestamp))
454                 d = 1.0;
455         else
456                 d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
457
458         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
459                 return -ENOMEM;
460
461         return 0;
462 }
463
464 static int bus_manager_append_virt(DBusMessageIter *i, const char *property, void *data) {
465         Manager *m = data;
466         const char *id = "";
467
468         assert(i);
469         assert(property);
470         assert(m);
471
472         detect_virtualization(&id);
473
474         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id))
475                 return -ENOMEM;
476
477         return 0;
478 }
479
480 static DBusMessage *message_from_file_changes(
481                 DBusMessage *m,
482                 UnitFileChange *changes,
483                 unsigned n_changes,
484                 int carries_install_info) {
485
486         DBusMessageIter iter, sub, sub2;
487         DBusMessage *reply;
488         unsigned i;
489
490         reply = dbus_message_new_method_return(m);
491         if (!reply)
492                 return NULL;
493
494         dbus_message_iter_init_append(reply, &iter);
495
496         if (carries_install_info >= 0) {
497                 dbus_bool_t b;
498
499                 b = !!carries_install_info;
500                 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
501                         goto oom;
502         }
503
504         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
505                 goto oom;
506
507         for (i = 0; i < n_changes; i++) {
508                 const char *type, *path, *source;
509
510                 type = unit_file_change_type_to_string(changes[i].type);
511                 path = strempty(changes[i].path);
512                 source = strempty(changes[i].source);
513
514                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
515                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
516                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
517                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
518                     !dbus_message_iter_close_container(&sub, &sub2))
519                         goto oom;
520         }
521
522         if (!dbus_message_iter_close_container(&iter, &sub))
523                 goto oom;
524
525         return reply;
526
527 oom:
528         dbus_message_unref(reply);
529         return NULL;
530 }
531
532 static int bus_manager_send_unit_files_changed(Manager *m) {
533         DBusMessage *s;
534         int r;
535
536         s = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged");
537         if (!s)
538                 return -ENOMEM;
539
540         r = bus_broadcast(m, s);
541         dbus_message_unref(s);
542
543         return r;
544 }
545
546 static int bus_manager_set_runtime_watchdog_usec(DBusMessageIter *i, const char *property, void *data) {
547         uint64_t *t = data;
548
549         assert(i);
550         assert(property);
551
552         dbus_message_iter_get_basic(i, t);
553
554         return watchdog_set_timeout(t);
555 }
556
557 static const char systemd_property_string[] =
558         PACKAGE_STRING "\0"
559         SYSTEMD_FEATURES;
560
561 static const BusProperty bus_systemd_properties[] = {
562         { "Version",       bus_property_append_string,    "s",  0                      },
563         { "Features",      bus_property_append_string,    "s",  sizeof(PACKAGE_STRING) },
564         { NULL, }
565 };
566
567 static const BusProperty bus_manager_properties[] = {
568         { "Tainted",                     bus_manager_append_tainted,     "s",  0                                                },
569         { "FirmwareTimestamp",           bus_property_append_uint64,     "t",  offsetof(Manager, firmware_timestamp.realtime)   },
570         { "FirmwareTimestampMonotonic",  bus_property_append_uint64,     "t",  offsetof(Manager, firmware_timestamp.monotonic)  },
571         { "LoaderTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, loader_timestamp.realtime)     },
572         { "LoaderTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, loader_timestamp.monotonic)    },
573         { "KernelTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, kernel_timestamp.realtime)     },
574         { "KernelTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, kernel_timestamp.monotonic)    },
575         { "InitRDTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, initrd_timestamp.realtime)     },
576         { "InitRDTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, initrd_timestamp.monotonic)    },
577         { "UserspaceTimestamp",          bus_property_append_uint64,     "t",  offsetof(Manager, userspace_timestamp.realtime)  },
578         { "UserspaceTimestampMonotonic", bus_property_append_uint64,     "t",  offsetof(Manager, userspace_timestamp.monotonic) },
579         { "FinishTimestamp",             bus_property_append_uint64,     "t",  offsetof(Manager, finish_timestamp.realtime)     },
580         { "FinishTimestampMonotonic",    bus_property_append_uint64,     "t",  offsetof(Manager, finish_timestamp.monotonic)    },
581         { "LogLevel",                    bus_manager_append_log_level,   "s",  0,                                               false, bus_manager_set_log_level },
582         { "LogTarget",                   bus_manager_append_log_target,  "s",  0,                                               false, bus_manager_set_log_target },
583         { "NNames",                      bus_manager_append_n_names,     "u",  0                                                },
584         { "NJobs",                       bus_manager_append_n_jobs,      "u",  0                                                },
585         { "NInstalledJobs",              bus_property_append_uint32,     "u",  offsetof(Manager, n_installed_jobs)              },
586         { "NFailedJobs",                 bus_property_append_uint32,     "u",  offsetof(Manager, n_failed_jobs)                 },
587         { "Progress",                    bus_manager_append_progress,    "d",  0                                                },
588         { "Environment",                 bus_property_append_strv,       "as", offsetof(Manager, environment),                  true },
589         { "ConfirmSpawn",                bus_property_append_bool,       "b",  offsetof(Manager, confirm_spawn)                 },
590         { "ShowStatus",                  bus_property_append_bool,       "b",  offsetof(Manager, show_status)                   },
591         { "UnitPath",                    bus_property_append_strv,       "as", offsetof(Manager, lookup_paths.unit_path),       true },
592         { "ControlGroupHierarchy",       bus_property_append_string,     "s",  offsetof(Manager, cgroup_hierarchy),             true },
593         { "DefaultControllers",          bus_property_append_strv,       "as", offsetof(Manager, default_controllers),          true },
594         { "DefaultStandardOutput",       bus_manager_append_exec_output, "s",  offsetof(Manager, default_std_output)            },
595         { "DefaultStandardError",        bus_manager_append_exec_output, "s",  offsetof(Manager, default_std_error)             },
596         { "RuntimeWatchdogUSec",         bus_property_append_usec,       "t",  offsetof(Manager, runtime_watchdog),             false, bus_manager_set_runtime_watchdog_usec },
597         { "ShutdownWatchdogUSec",        bus_property_append_usec,       "t",  offsetof(Manager, shutdown_watchdog),            false, bus_property_set_usec },
598         { "Virtualization",              bus_manager_append_virt,        "s",  0,                                               },
599         { NULL, }
600 };
601
602 static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
603         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
604         _cleanup_free_ char * path = NULL;
605         Manager *m = data;
606         int r;
607         DBusError error;
608         JobType job_type = _JOB_TYPE_INVALID;
609         bool reload_if_possible = false;
610         const char *member;
611
612         assert(connection);
613         assert(message);
614         assert(m);
615
616         dbus_error_init(&error);
617
618         member = dbus_message_get_member(message);
619
620         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
621                 const char *name;
622                 Unit *u;
623
624                 if (!dbus_message_get_args(
625                                     message,
626                                     &error,
627                                     DBUS_TYPE_STRING, &name,
628                                     DBUS_TYPE_INVALID))
629                         return bus_send_error_reply(connection, message, &error, -EINVAL);
630
631                 u = manager_get_unit(m, name);
632                 if (!u) {
633                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
634                         return bus_send_error_reply(connection, message, &error, -ENOENT);
635                 }
636
637                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
638
639                 reply = dbus_message_new_method_return(message);
640                 if (!reply)
641                         goto oom;
642
643                 path = unit_dbus_path(u);
644                 if (!path)
645                         goto oom;
646
647                 if (!dbus_message_append_args(
648                                     reply,
649                                     DBUS_TYPE_OBJECT_PATH, &path,
650                                     DBUS_TYPE_INVALID))
651                         goto oom;
652         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
653                 Unit *u;
654                 uint32_t pid;
655
656                 if (!dbus_message_get_args(
657                                     message,
658                                     &error,
659                                     DBUS_TYPE_UINT32, &pid,
660                                     DBUS_TYPE_INVALID))
661                         return bus_send_error_reply(connection, message, &error, -EINVAL);
662
663                 u = cgroup_unit_by_pid(m, (pid_t) pid);
664                 if (!u) {
665                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
666                         return bus_send_error_reply(connection, message, &error, -ENOENT);
667                 }
668
669                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
670
671                 reply = dbus_message_new_method_return(message);
672                 if (!reply)
673                         goto oom;
674
675                 path = unit_dbus_path(u);
676                 if (!path)
677                         goto oom;
678
679                 if (!dbus_message_append_args(
680                                     reply,
681                                     DBUS_TYPE_OBJECT_PATH, &path,
682                                     DBUS_TYPE_INVALID))
683                         goto oom;
684         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
685                 const char *name;
686                 Unit *u;
687
688                 if (!dbus_message_get_args(
689                                     message,
690                                     &error,
691                                     DBUS_TYPE_STRING, &name,
692                                     DBUS_TYPE_INVALID))
693                         return bus_send_error_reply(connection, message, &error, -EINVAL);
694
695                 r = manager_load_unit(m, name, NULL, &error, &u);
696                 if (r < 0)
697                         return bus_send_error_reply(connection, message, &error, r);
698
699                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
700
701                 reply = dbus_message_new_method_return(message);
702                 if (!reply)
703                         goto oom;
704
705                 path = unit_dbus_path(u);
706                 if (!path)
707                         goto oom;
708
709                 if (!dbus_message_append_args(
710                                     reply,
711                                     DBUS_TYPE_OBJECT_PATH, &path,
712                                     DBUS_TYPE_INVALID))
713                         goto oom;
714
715         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
716                 job_type = JOB_START;
717         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
718                 job_type = JOB_START;
719         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
720                 job_type = JOB_STOP;
721         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
722                 job_type = JOB_RELOAD;
723         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
724                 job_type = JOB_RESTART;
725         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
726                 job_type = JOB_TRY_RESTART;
727         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
728                 reload_if_possible = true;
729                 job_type = JOB_RESTART;
730         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
731                 reload_if_possible = true;
732                 job_type = JOB_TRY_RESTART;
733         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
734                 const char *name, *swho;
735                 int32_t signo;
736                 Unit *u;
737                 KillWho who;
738
739                 if (!dbus_message_get_args(
740                                     message,
741                                     &error,
742                                     DBUS_TYPE_STRING, &name,
743                                     DBUS_TYPE_STRING, &swho,
744                                     DBUS_TYPE_INT32, &signo,
745                                     DBUS_TYPE_INVALID))
746                         return bus_send_error_reply(connection, message, &error, -EINVAL);
747
748                 if (isempty(swho))
749                         who = KILL_ALL;
750                 else {
751                         who = kill_who_from_string(swho);
752                         if (who < 0)
753                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
754                 }
755
756                 if (signo <= 0 || signo >= _NSIG)
757                         return bus_send_error_reply(connection, message, &error, -EINVAL);
758
759                 u = manager_get_unit(m, name);
760                 if (!u) {
761                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
762                         return bus_send_error_reply(connection, message, &error, -ENOENT);
763                 }
764
765                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
766
767                 r = unit_kill(u, who, signo, &error);
768                 if (r < 0)
769                         return bus_send_error_reply(connection, message, &error, r);
770
771                 reply = dbus_message_new_method_return(message);
772                 if (!reply)
773                         goto oom;
774
775         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
776                 uint32_t id;
777                 Job *j;
778
779                 if (!dbus_message_get_args(
780                                     message,
781                                     &error,
782                                     DBUS_TYPE_UINT32, &id,
783                                     DBUS_TYPE_INVALID))
784                         return bus_send_error_reply(connection, message, &error, -EINVAL);
785
786                 j = manager_get_job(m, id);
787                 if (!j) {
788                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
789                         return bus_send_error_reply(connection, message, &error, -ENOENT);
790                 }
791
792                 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
793
794                 reply = dbus_message_new_method_return(message);
795                 if (!reply)
796                         goto oom;
797
798                 path = job_dbus_path(j);
799                 if (!path)
800                         goto oom;
801
802                 if (!dbus_message_append_args(
803                                     reply,
804                                     DBUS_TYPE_OBJECT_PATH, &path,
805                                     DBUS_TYPE_INVALID))
806                         goto oom;
807
808         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CancelJob")) {
809                 uint32_t id;
810                 Job *j;
811
812                 if (!dbus_message_get_args(
813                                     message,
814                                     &error,
815                                     DBUS_TYPE_UINT32, &id,
816                                     DBUS_TYPE_INVALID))
817                         return bus_send_error_reply(connection, message, &error, -EINVAL);
818
819                 j = manager_get_job(m, id);
820                 if (!j) {
821                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
822                         return bus_send_error_reply(connection, message, &error, -ENOENT);
823                 }
824
825                 SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
826                 job_finish_and_invalidate(j, JOB_CANCELED, true);
827
828                 reply = dbus_message_new_method_return(message);
829                 if (!reply)
830                         goto oom;
831
832         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
833
834                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
835                 manager_clear_jobs(m);
836
837                 reply = dbus_message_new_method_return(message);
838                 if (!reply)
839                         goto oom;
840
841         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
842
843                 SELINUX_ACCESS_CHECK(connection, message, "reload");
844
845                 manager_reset_failed(m);
846
847                 reply = dbus_message_new_method_return(message);
848                 if (!reply)
849                         goto oom;
850
851         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
852                 const char *name;
853                 Unit *u;
854
855                 if (!dbus_message_get_args(
856                                     message,
857                                     &error,
858                                     DBUS_TYPE_STRING, &name,
859                                     DBUS_TYPE_INVALID))
860                         return bus_send_error_reply(connection, message, &error, -EINVAL);
861
862                 u = manager_get_unit(m, name);
863                 if (!u) {
864                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
865                         return bus_send_error_reply(connection, message, &error, -ENOENT);
866                 }
867
868                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
869
870                 unit_reset_failed(u);
871
872                 reply = dbus_message_new_method_return(message);
873                 if (!reply)
874                         goto oom;
875
876         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroups")) {
877                 const char *name;
878                 Unit *u;
879                 DBusMessageIter iter;
880
881                 if (!dbus_message_iter_init(message, &iter))
882                         goto oom;
883
884                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
885                 if (r < 0)
886                         return bus_send_error_reply(connection, message, NULL, r);
887
888                 u = manager_get_unit(m, name);
889                 if (!u) {
890                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
891                         return bus_send_error_reply(connection, message, &error, -ENOENT);
892                 }
893
894                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
895
896                 r = bus_unit_cgroup_set(u, &iter);
897                 if (r < 0)
898                         return bus_send_error_reply(connection, message, NULL, r);
899
900                 reply = dbus_message_new_method_return(message);
901                 if (!reply)
902                         goto oom;
903
904         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroups")) {
905                 const char *name;
906                 Unit *u;
907                 DBusMessageIter iter;
908
909                 if (!dbus_message_iter_init(message, &iter))
910                         goto oom;
911
912                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
913                 if (r < 0)
914                         return bus_send_error_reply(connection, message, NULL, r);
915
916                 u = manager_get_unit(m, name);
917                 if (!u) {
918                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
919                         return bus_send_error_reply(connection, message, &error, -ENOENT);
920                 }
921
922                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
923
924                 r = bus_unit_cgroup_unset(u, &iter);
925                 if (r < 0)
926                         return bus_send_error_reply(connection, message, NULL, r);
927
928                 reply = dbus_message_new_method_return(message);
929                 if (!reply)
930                         goto oom;
931
932         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttributes")) {
933                 const char *name;
934                 Unit *u;
935                 DBusMessageIter iter;
936
937                 if (!dbus_message_iter_init(message, &iter))
938                         goto oom;
939
940                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
941                 if (r < 0)
942                         return bus_send_error_reply(connection, message, NULL, r);
943
944                 u = manager_get_unit(m, name);
945                 if (!u) {
946                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
947                         return bus_send_error_reply(connection, message, &error, -ENOENT);
948                 }
949
950                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
951                 r = bus_unit_cgroup_attribute_set(u, &iter);
952                 if (r < 0)
953                         return bus_send_error_reply(connection, message, NULL, r);
954
955                 reply = dbus_message_new_method_return(message);
956                 if (!reply)
957                         goto oom;
958
959         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttributes")) {
960                 const char *name;
961                 Unit *u;
962                 DBusMessageIter iter;
963
964                 if (!dbus_message_iter_init(message, &iter))
965                         goto oom;
966
967                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
968                 if (r < 0)
969                         return bus_send_error_reply(connection, message, NULL, r);
970
971                 u = manager_get_unit(m, name);
972                 if (!u) {
973                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
974                         return bus_send_error_reply(connection, message, &error, -ENOENT);
975                 }
976
977                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
978
979                 r = bus_unit_cgroup_attribute_unset(u, &iter);
980                 if (r < 0)
981                         return bus_send_error_reply(connection, message, NULL, r);
982
983                 reply = dbus_message_new_method_return(message);
984                 if (!reply)
985                         goto oom;
986
987         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttributes")) {
988                 const char *name;
989                 Unit *u;
990                 DBusMessageIter iter;
991                 _cleanup_strv_free_ char **list = NULL;
992
993                 if (!dbus_message_iter_init(message, &iter))
994                         goto oom;
995
996                 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
997                 if (r < 0)
998                         return bus_send_error_reply(connection, message, NULL, r);
999
1000                 u = manager_get_unit(m, name);
1001                 if (!u) {
1002                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
1003                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1004                 }
1005
1006                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
1007                 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
1008                 if (r < 0)
1009                         return bus_send_error_reply(connection, message, NULL, r);
1010
1011                 reply = dbus_message_new_method_return(message);
1012                 if (!reply)
1013                         goto oom;
1014
1015                 dbus_message_iter_init_append(reply, &iter);
1016                 if (bus_append_strv_iter(&iter, list) < 0)
1017                         goto oom;
1018
1019         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
1020                 DBusMessageIter iter, sub;
1021                 Iterator i;
1022                 Unit *u;
1023                 const char *k;
1024
1025                 SELINUX_ACCESS_CHECK(connection, message, "status");
1026
1027                 reply = dbus_message_new_method_return(message);
1028                 if (!reply)
1029                         goto oom;
1030
1031                 dbus_message_iter_init_append(reply, &iter);
1032
1033                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
1034                         goto oom;
1035
1036                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1037                         char *u_path, *j_path;
1038                         const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
1039                         DBusMessageIter sub2;
1040                         uint32_t job_id;
1041                         Unit *f;
1042
1043                         if (k != u->id)
1044                                 continue;
1045
1046                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1047                                 goto oom;
1048
1049                         description = unit_description(u);
1050                         load_state = unit_load_state_to_string(u->load_state);
1051                         active_state = unit_active_state_to_string(unit_active_state(u));
1052                         sub_state = unit_sub_state_to_string(u);
1053
1054                         f = unit_following(u);
1055                         following = f ? f->id : "";
1056
1057                         u_path = unit_dbus_path(u);
1058                         if (!u_path)
1059                                 goto oom;
1060
1061                         if (u->job) {
1062                                 job_id = (uint32_t) u->job->id;
1063
1064                                 if (!(j_path = job_dbus_path(u->job))) {
1065                                         free(u_path);
1066                                         goto oom;
1067                                 }
1068
1069                                 sjob_type = job_type_to_string(u->job->type);
1070                         } else {
1071                                 job_id = 0;
1072                                 j_path = u_path;
1073                                 sjob_type = "";
1074                         }
1075
1076                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) ||
1077                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
1078                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
1079                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
1080                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
1081                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
1082                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
1083                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
1084                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
1085                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
1086                                 free(u_path);
1087                                 if (u->job)
1088                                         free(j_path);
1089                                 goto oom;
1090                         }
1091
1092                         free(u_path);
1093                         if (u->job)
1094                                 free(j_path);
1095
1096                         if (!dbus_message_iter_close_container(&sub, &sub2))
1097                                 goto oom;
1098                 }
1099
1100                 if (!dbus_message_iter_close_container(&iter, &sub))
1101                         goto oom;
1102
1103         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
1104                 DBusMessageIter iter, sub;
1105                 Iterator i;
1106                 Job *j;
1107
1108                 SELINUX_ACCESS_CHECK(connection, message, "status");
1109
1110                 reply = dbus_message_new_method_return(message);
1111                 if (!reply)
1112                         goto oom;
1113
1114                 dbus_message_iter_init_append(reply, &iter);
1115
1116                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
1117                         goto oom;
1118
1119                 HASHMAP_FOREACH(j, m->jobs, i) {
1120                         char *u_path, *j_path;
1121                         const char *state, *type;
1122                         uint32_t id;
1123                         DBusMessageIter sub2;
1124
1125                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
1126                                 goto oom;
1127
1128                         id = (uint32_t) j->id;
1129                         state = job_state_to_string(j->state);
1130                         type = job_type_to_string(j->type);
1131
1132                         j_path = job_dbus_path(j);
1133                         if (!j_path)
1134                                 goto oom;
1135
1136                         u_path = unit_dbus_path(j->unit);
1137                         if (!u_path) {
1138                                 free(j_path);
1139                                 goto oom;
1140                         }
1141
1142                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
1143                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) ||
1144                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
1145                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
1146                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
1147                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
1148                                 free(j_path);
1149                                 free(u_path);
1150                                 goto oom;
1151                         }
1152
1153                         free(j_path);
1154                         free(u_path);
1155
1156                         if (!dbus_message_iter_close_container(&sub, &sub2))
1157                                 goto oom;
1158                 }
1159
1160                 if (!dbus_message_iter_close_container(&iter, &sub))
1161                         goto oom;
1162
1163         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
1164                 char *client;
1165                 Set *s;
1166
1167                 SELINUX_ACCESS_CHECK(connection, message, "status");
1168
1169                 s = BUS_CONNECTION_SUBSCRIBED(m, connection);
1170                 if (!s) {
1171                         s = set_new(string_hash_func, string_compare_func);
1172                         if (!s)
1173                                 goto oom;
1174
1175                         if (!dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL)) {
1176                                 set_free(s);
1177                                 goto oom;
1178                         }
1179                 }
1180
1181                 client = strdup(bus_message_get_sender_with_fallback(message));
1182                 if (!client)
1183                         goto oom;
1184
1185                 r = set_put(s, client);
1186                 if (r < 0) {
1187                         free(client);
1188                         return bus_send_error_reply(connection, message, NULL, r);
1189                 }
1190
1191                 reply = dbus_message_new_method_return(message);
1192                 if (!reply)
1193                         goto oom;
1194
1195         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
1196                 char *client;
1197
1198                 SELINUX_ACCESS_CHECK(connection, message, "status");
1199
1200                 client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message));
1201                 if (!client) {
1202                         dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
1203                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1204                 }
1205
1206                 free(client);
1207
1208                 reply = dbus_message_new_method_return(message);
1209                 if (!reply)
1210                         goto oom;
1211
1212         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
1213                 FILE *f;
1214                 char *dump = NULL;
1215                 size_t size;
1216
1217                 SELINUX_ACCESS_CHECK(connection, message, "status");
1218
1219                 reply = dbus_message_new_method_return(message);
1220                 if (!reply)
1221                         goto oom;
1222
1223                 f = open_memstream(&dump, &size);
1224                 if (!f)
1225                         goto oom;
1226
1227                 manager_dump_units(m, f, NULL);
1228                 manager_dump_jobs(m, f, NULL);
1229
1230                 if (ferror(f)) {
1231                         fclose(f);
1232                         free(dump);
1233                         goto oom;
1234                 }
1235
1236                 fclose(f);
1237
1238                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
1239                         free(dump);
1240                         goto oom;
1241                 }
1242
1243                 free(dump);
1244         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
1245                 const char *name;
1246                 dbus_bool_t cleanup;
1247                 Snapshot *s;
1248
1249                 SELINUX_ACCESS_CHECK(connection, message, "start");
1250
1251                 if (!dbus_message_get_args(
1252                                     message,
1253                                     &error,
1254                                     DBUS_TYPE_STRING, &name,
1255                                     DBUS_TYPE_BOOLEAN, &cleanup,
1256                                     DBUS_TYPE_INVALID))
1257                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1258
1259                 if (isempty(name))
1260                         name = NULL;
1261
1262                 r = snapshot_create(m, name, cleanup, &error, &s);
1263                 if (r < 0)
1264                         return bus_send_error_reply(connection, message, &error, r);
1265
1266                 reply = dbus_message_new_method_return(message);
1267                 if (!reply)
1268                         goto oom;
1269
1270                 path = unit_dbus_path(UNIT(s));
1271                 if (!path)
1272                         goto oom;
1273
1274                 if (!dbus_message_append_args(
1275                                     reply,
1276                                     DBUS_TYPE_OBJECT_PATH, &path,
1277                                     DBUS_TYPE_INVALID))
1278                         goto oom;
1279
1280         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) {
1281                 const char *name;
1282                 Unit *u;
1283
1284                 if (!dbus_message_get_args(
1285                                     message,
1286                                     &error,
1287                                     DBUS_TYPE_STRING, &name,
1288                                     DBUS_TYPE_INVALID))
1289                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1290
1291                 u = manager_get_unit(m, name);
1292                 if (!u) {
1293                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
1294                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1295                 }
1296
1297                 if (u->type != UNIT_SNAPSHOT) {
1298                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name);
1299                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1300                 }
1301
1302                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
1303                 snapshot_remove(SNAPSHOT(u));
1304
1305                 reply = dbus_message_new_method_return(message);
1306                 if (!reply)
1307                         goto oom;
1308
1309         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1310                 char *introspection = NULL;
1311                 FILE *f;
1312                 Iterator i;
1313                 Unit *u;
1314                 Job *j;
1315                 const char *k;
1316                 size_t size;
1317
1318                 SELINUX_ACCESS_CHECK(connection, message, "status");
1319
1320                 reply = dbus_message_new_method_return(message);
1321                 if (!reply)
1322                         goto oom;
1323
1324                 /* We roll our own introspection code here, instead of
1325                  * relying on bus_default_message_handler() because we
1326                  * need to generate our introspection string
1327                  * dynamically. */
1328
1329                 f = open_memstream(&introspection, &size);
1330                 if (!f)
1331                         goto oom;
1332
1333                 fputs(INTROSPECTION_BEGIN, f);
1334
1335                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1336                         char *p;
1337
1338                         if (k != u->id)
1339                                 continue;
1340
1341                         p = bus_path_escape(k);
1342                         if (!p) {
1343                                 fclose(f);
1344                                 free(introspection);
1345                                 goto oom;
1346                         }
1347
1348                         fprintf(f, "<node name=\"unit/%s\"/>", p);
1349                         free(p);
1350                 }
1351
1352                 HASHMAP_FOREACH(j, m->jobs, i)
1353                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
1354
1355                 fputs(INTROSPECTION_END, f);
1356
1357                 if (ferror(f)) {
1358                         fclose(f);
1359                         free(introspection);
1360                         goto oom;
1361                 }
1362
1363                 fclose(f);
1364
1365                 if (!introspection)
1366                         goto oom;
1367
1368                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1369                         free(introspection);
1370                         goto oom;
1371                 }
1372
1373                 free(introspection);
1374
1375         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
1376
1377                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1378
1379                 assert(!m->queued_message);
1380
1381                 /* Instead of sending the reply back right away, we
1382                  * just remember that we need to and then send it
1383                  * after the reload is finished. That way the caller
1384                  * knows when the reload finished. */
1385
1386                 m->queued_message = dbus_message_new_method_return(message);
1387                 if (!m->queued_message)
1388                         goto oom;
1389
1390                 m->queued_message_connection = connection;
1391                 m->exit_code = MANAGER_RELOAD;
1392
1393         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
1394
1395                 SELINUX_ACCESS_CHECK(connection, message, "reload");
1396
1397                 /* We don't send a reply back here, the client should
1398                  * just wait for us disconnecting. */
1399
1400                 m->exit_code = MANAGER_REEXECUTE;
1401
1402         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
1403
1404                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1405
1406                 if (m->running_as == SYSTEMD_SYSTEM) {
1407                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
1408                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1409                 }
1410
1411                 reply = dbus_message_new_method_return(message);
1412                 if (!reply)
1413                         goto oom;
1414
1415                 m->exit_code = MANAGER_EXIT;
1416
1417         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
1418
1419                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1420
1421                 if (m->running_as != SYSTEMD_SYSTEM) {
1422                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
1423                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1424                 }
1425
1426                 reply = dbus_message_new_method_return(message);
1427                 if (!reply)
1428                         goto oom;
1429
1430                 m->exit_code = MANAGER_REBOOT;
1431
1432         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
1433
1434                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1435
1436                 if (m->running_as != SYSTEMD_SYSTEM) {
1437                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
1438                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1439                 }
1440
1441                 reply = dbus_message_new_method_return(message);
1442                 if (!reply)
1443                         goto oom;
1444
1445                 m->exit_code = MANAGER_POWEROFF;
1446
1447         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
1448
1449                 SELINUX_ACCESS_CHECK(connection, message, "halt");
1450
1451                 if (m->running_as != SYSTEMD_SYSTEM) {
1452                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
1453                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1454                 }
1455
1456                 reply = dbus_message_new_method_return(message);
1457                 if (!reply)
1458                         goto oom;
1459
1460                 m->exit_code = MANAGER_HALT;
1461
1462         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
1463
1464                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1465
1466                 if (m->running_as != SYSTEMD_SYSTEM) {
1467                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
1468                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1469                 }
1470
1471                 reply = dbus_message_new_method_return(message);
1472                 if (!reply)
1473                         goto oom;
1474
1475                 m->exit_code = MANAGER_KEXEC;
1476
1477         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
1478                 const char *switch_root, *switch_root_init;
1479                 char *u, *v;
1480                 int k;
1481
1482                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1483
1484                 if (!dbus_message_get_args(
1485                                     message,
1486                                     &error,
1487                                     DBUS_TYPE_STRING, &switch_root,
1488                                     DBUS_TYPE_STRING, &switch_root_init,
1489                                     DBUS_TYPE_INVALID))
1490                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1491
1492                 if (path_equal(switch_root, "/") || !path_is_absolute(switch_root))
1493                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1494
1495                 if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
1496                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1497
1498                 if (m->running_as != SYSTEMD_SYSTEM) {
1499                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
1500                         return bus_send_error_reply(connection, message, &error, -ENOTSUP);
1501                 }
1502
1503                 /* Safety check */
1504                 if (isempty(switch_root_init))
1505                         k = access(switch_root, F_OK);
1506                 else {
1507                         char *p;
1508
1509                         p = strjoin(switch_root, "/", switch_root_init, NULL);
1510                         if (!p)
1511                                 goto oom;
1512
1513                         k = access(p, X_OK);
1514                         free(p);
1515                 }
1516                 if (k < 0)
1517                         return bus_send_error_reply(connection, message, NULL, -errno);
1518
1519                 u = strdup(switch_root);
1520                 if (!u)
1521                         goto oom;
1522
1523                 if (!isempty(switch_root_init)) {
1524                         v = strdup(switch_root_init);
1525                         if (!v) {
1526                                 free(u);
1527                                 goto oom;
1528                         }
1529                 } else
1530                         v = NULL;
1531
1532                 free(m->switch_root);
1533                 free(m->switch_root_init);
1534                 m->switch_root = u;
1535                 m->switch_root_init = v;
1536
1537                 reply = dbus_message_new_method_return(message);
1538                 if (!reply)
1539                         goto oom;
1540
1541                 m->exit_code = MANAGER_SWITCH_ROOT;
1542
1543         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
1544                 char **l = NULL, **e = NULL;
1545
1546                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1547
1548                 r = bus_parse_strv(message, &l);
1549                 if (r == -ENOMEM)
1550                         goto oom;
1551                 if (r < 0)
1552                         return bus_send_error_reply(connection, message, NULL, r);
1553
1554                 e = strv_env_merge(2, m->environment, l);
1555                 strv_free(l);
1556                 if (!e)
1557                         goto oom;
1558
1559                 reply = dbus_message_new_method_return(message);
1560                 if (!reply) {
1561                         strv_free(e);
1562                         goto oom;
1563                 }
1564
1565                 strv_free(m->environment);
1566                 m->environment = e;
1567
1568         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
1569                 char **l = NULL, **e = NULL;
1570
1571                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1572
1573                 r = bus_parse_strv(message, &l);
1574                 if (r == -ENOMEM)
1575                         goto oom;
1576                 if (r < 0)
1577                         return bus_send_error_reply(connection, message, NULL, r);
1578
1579                 e = strv_env_delete(m->environment, 1, l);
1580                 strv_free(l);
1581
1582                 if (!e)
1583                         goto oom;
1584
1585                 reply = dbus_message_new_method_return(message);
1586                 if (!reply) {
1587                         strv_free(e);
1588                         goto oom;
1589                 }
1590
1591                 strv_free(m->environment);
1592                 m->environment = e;
1593
1594         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
1595                 char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
1596                 DBusMessageIter iter;
1597
1598                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
1599
1600                 if (!dbus_message_iter_init(message, &iter))
1601                         goto oom;
1602
1603                 r = bus_parse_strv_iter(&iter, &l_unset);
1604                 if (r == -ENOMEM)
1605                         goto oom;
1606                 if (r < 0)
1607                         return bus_send_error_reply(connection, message, NULL, r);
1608
1609                 if (!dbus_message_iter_next(&iter)) {
1610                         strv_free(l_unset);
1611                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1612                 }
1613
1614                 r = bus_parse_strv_iter(&iter, &l_set);
1615                 if (r < 0) {
1616                         strv_free(l_unset);
1617                         if (r == -ENOMEM)
1618                                 goto oom;
1619
1620                         return bus_send_error_reply(connection, message, NULL, r);
1621                 }
1622
1623                 e = strv_env_delete(m->environment, 1, l_unset);
1624                 strv_free(l_unset);
1625
1626                 if (!e) {
1627                         strv_free(l_set);
1628                         goto oom;
1629                 }
1630
1631                 f = strv_env_merge(2, e, l_set);
1632                 strv_free(l_set);
1633                 strv_free(e);
1634
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 };