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