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