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