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