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