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