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