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