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