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