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