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