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