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