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