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