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