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