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