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