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