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