chiark / gitweb /
c700abbe22082c3f69c64c9253497c0d6dcf8585
[elogind.git] / src / 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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23
24 #include "dbus.h"
25 #include "log.h"
26 #include "dbus-manager.h"
27 #include "strv.h"
28 #include "bus-errors.h"
29
30 #define BUS_MANAGER_INTERFACE_BEGIN                                     \
31         " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
32
33 #define BUS_MANAGER_INTERFACE_METHODS                                   \
34         "  <method name=\"GetUnit\">\n"                                 \
35         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
36         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
37         "  </method>\n"                                                 \
38         "  <method name=\"GetUnitByPID\">\n"                            \
39         "   <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n"          \
40         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
41         "  </method>\n"                                                 \
42         "  <method name=\"LoadUnit\">\n"                                \
43         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
44         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
45         "  </method>\n"                                                 \
46         "  <method name=\"StartUnit\">\n"                               \
47         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
48         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
49         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
50         "  </method>\n"                                                 \
51         "  <method name=\"StopUnit\">\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=\"ReloadUnit\">\n"                              \
57         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
58         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
59         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
60         "  </method>\n"                                                 \
61         "  <method name=\"RestartUnit\">\n"                             \
62         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
63         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
64         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
65         "  </method>\n"                                                 \
66         "  <method name=\"TryRestartUnit\">\n"                          \
67         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
68         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
69         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
70         "  </method>\n"                                                 \
71         "  <method name=\"ReloadOrRestartUnit\">\n"                     \
72         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
73         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
74         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
75         "  </method>\n"                                                 \
76         "  <method name=\"ReloadOrTryRestartUnit\">\n"                  \
77         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
78         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
79         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
80         "  </method>\n"                                                 \
81         "  <method name=\"ResetFailedUnit\">\n"                         \
82         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
83         "  </method>\n"                                                 \
84         "  <method name=\"GetJob\">\n"                                  \
85         "   <arg name=\"id\" type=\"u\" direction=\"in\"/>\n"           \
86         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
87         "  </method>\n"                                                 \
88         "  <method name=\"ClearJobs\"/>\n"                              \
89         "  <method name=\"ResetFailed\"/>\n"                            \
90         "  <method name=\"ListUnits\">\n"                               \
91         "   <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
92         "  </method>\n"                                                 \
93         "  <method name=\"ListJobs\">\n"                                \
94         "   <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
95         "  </method>\n"                                                 \
96         "  <method name=\"Subscribe\"/>\n"                              \
97         "  <method name=\"Unsubscribe\"/>\n"                            \
98         "  <method name=\"Dump\"/>\n"                                   \
99         "  <method name=\"CreateSnapshot\">\n"                          \
100         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
101         "   <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n"      \
102         "   <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n"        \
103         "  </method>\n"                                                 \
104         "  <method name=\"Reload\"/>\n"                                 \
105         "  <method name=\"Reexecute\"/>\n"                              \
106         "  <method name=\"Exit\"/>\n"                                   \
107         "  <method name=\"Reboot\"/>\n"                                 \
108         "  <method name=\"PowerOff\"/>\n"                               \
109         "  <method name=\"Halt\"/>\n"                                   \
110         "  <method name=\"KExec\"/>\n"                                  \
111         "  <method name=\"SetEnvironment\">\n"                          \
112         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
113         "  </method>\n"                                                 \
114         "  <method name=\"UnsetEnvironment\">\n"                        \
115         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
116         "  </method>\n"
117
118 #define BUS_MANAGER_INTERFACE_SIGNALS                                   \
119         "  <signal name=\"UnitNew\">\n"                                 \
120         "   <arg name=\"id\" type=\"s\"/>\n"                            \
121         "   <arg name=\"unit\" type=\"o\"/>\n"                          \
122         "  </signal>\n"                                                 \
123         "  <signal name=\"UnitRemoved\">\n"                             \
124         "   <arg name=\"id\" type=\"s\"/>\n"                            \
125         "   <arg name=\"unit\" type=\"o\"/>\n"                          \
126         "  </signal>\n"                                                 \
127         "  <signal name=\"JobNew\">\n"                                  \
128         "   <arg name=\"id\" type=\"u\"/>\n"                            \
129         "   <arg name=\"job\" type=\"o\"/>\n"                           \
130         "  </signal>\n"                                                 \
131         "  <signal name=\"JobRemoved\">\n"                              \
132         "   <arg name=\"id\" type=\"u\"/>\n"                            \
133         "   <arg name=\"job\" type=\"o\"/>\n"                           \
134         "   <arg name=\"success\" type=\"b\"/>\n"                       \
135         "  </signal>"
136
137
138 #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
139         "  <property name=\"Version\" type=\"s\" access=\"read\"/>\n"   \
140         "  <property name=\"RunningAs\" type=\"s\" access=\"read\"/>\n" \
141         "  <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \
142         "  <property name=\"LogLevel\" type=\"s\" access=\"read\"/>\n"  \
143         "  <property name=\"LogTarget\" type=\"s\" access=\"read\"/>\n" \
144         "  <property name=\"NNames\" type=\"u\" access=\"read\"/>\n"    \
145         "  <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n"     \
146         "  <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
147         "  <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
148         "  <property name=\"Progress\" type=\"d\" access=\"read\"/>\n"  \
149         "  <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
150         "  <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
151         "  <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
152         "  <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
153         "  <property name=\"NotifySocket\" type=\"s\" access=\"read\"/>\n" \
154         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
155         "  <property name=\"MountAuto\" type=\"b\" access=\"read\"/>\n" \
156         "  <property name=\"SwapAuto\" type=\"b\" access=\"read\"/>\n"
157
158 #ifdef HAVE_SYSV_COMPAT
159 #define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV                           \
160         "  <property name=\"SysVConsole\" type=\"b\" access=\"read\"/>\n" \
161         "  <property name=\"SysVInitPath\" type=\"as\" access=\"read\"/>\n" \
162         "  <property name=\"SysVRcndPath\" type=\"as\" access=\"read\"/>\n"
163 #else
164 #define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV
165 #endif
166
167 #define BUS_MANAGER_INTERFACE_END                                       \
168         " </interface>\n"
169
170 #define BUS_MANAGER_INTERFACE                                           \
171         BUS_MANAGER_INTERFACE_BEGIN                                     \
172         BUS_MANAGER_INTERFACE_METHODS                                   \
173         BUS_MANAGER_INTERFACE_SIGNALS                                   \
174         BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
175         BUS_MANAGER_INTERFACE_PROPERTIES_SYSV                           \
176         BUS_MANAGER_INTERFACE_END
177
178 #define INTROSPECTION_BEGIN                                             \
179         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
180         "<node>\n"                                                      \
181         BUS_MANAGER_INTERFACE                                           \
182         BUS_PROPERTIES_INTERFACE                                        \
183         BUS_PEER_INTERFACE                                              \
184         BUS_INTROSPECTABLE_INTERFACE
185
186 #define INTROSPECTION_END                                               \
187         "</node>\n"
188
189 const char bus_manager_interface[] = BUS_MANAGER_INTERFACE;
190
191 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs);
192
193 static int bus_manager_append_log_target(Manager *m, DBusMessageIter *i, const char *property, void *data) {
194         const char *t;
195
196         assert(m);
197         assert(i);
198         assert(property);
199
200         t = log_target_to_string(log_get_target());
201
202         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
203                 return -ENOMEM;
204
205         return 0;
206 }
207
208 static int bus_manager_append_log_level(Manager *m, DBusMessageIter *i, const char *property, void *data) {
209         const char *t;
210
211         assert(m);
212         assert(i);
213         assert(property);
214
215         t = log_level_to_string(log_get_max_level());
216
217         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
218                 return -ENOMEM;
219
220         return 0;
221 }
222
223 static int bus_manager_append_n_names(Manager *m, DBusMessageIter *i, const char *property, void *data) {
224         uint32_t u;
225
226         assert(m);
227         assert(i);
228         assert(property);
229
230         u = hashmap_size(m->units);
231
232         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
233                 return -ENOMEM;
234
235         return 0;
236 }
237
238 static int bus_manager_append_n_jobs(Manager *m, DBusMessageIter *i, const char *property, void *data) {
239         uint32_t u;
240
241         assert(m);
242         assert(i);
243         assert(property);
244
245         u = hashmap_size(m->jobs);
246
247         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
248                 return -ENOMEM;
249
250         return 0;
251 }
252
253 static int bus_manager_append_progress(Manager *m, DBusMessageIter *i, const char *property, void *data) {
254         double d;
255
256         assert(m);
257         assert(i);
258         assert(property);
259
260         if (dual_timestamp_is_set(&m->finish_timestamp))
261                 d = 1.0;
262         else
263                 d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
264
265         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
266                 return -ENOMEM;
267
268         return 0;
269 }
270
271 static const char *message_get_sender_with_fallback(DBusMessage *m) {
272         const char *s;
273
274         assert(m);
275
276         if ((s = dbus_message_get_sender(m)))
277                 return s;
278
279         /* When the message came in from a direct connection the
280          * message will have no sender. We fix that here. */
281
282         return ":no-sender";
283 }
284
285 static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
286         Manager *m = data;
287
288         const BusProperty properties[] = {
289                 { "org.freedesktop.systemd1.Manager", "Version",       bus_property_append_string,    "s",  PACKAGE_STRING     },
290                 { "org.freedesktop.systemd1.Manager", "RunningAs",     bus_manager_append_running_as, "s",  &m->running_as     },
291                 { "org.freedesktop.systemd1.Manager", "StartupTimestamp", bus_property_append_uint64, "t",  &m->startup_timestamp.realtime },
292                 { "org.freedesktop.systemd1.Manager", "FinishTimestamp", bus_property_append_uint64,  "t",  &m->finish_timestamp.realtime },
293                 { "org.freedesktop.systemd1.Manager", "LogLevel",      bus_manager_append_log_level,  "s",  NULL               },
294                 { "org.freedesktop.systemd1.Manager", "LogTarget",     bus_manager_append_log_target, "s",  NULL               },
295                 { "org.freedesktop.systemd1.Manager", "NNames",        bus_manager_append_n_names,    "u",  NULL               },
296                 { "org.freedesktop.systemd1.Manager", "NJobs",         bus_manager_append_n_jobs,     "u",  NULL               },
297                 { "org.freedesktop.systemd1.Manager", "NInstalledJobs",bus_property_append_uint32,    "u",  &m->n_installed_jobs },
298                 { "org.freedesktop.systemd1.Manager", "NFailedJobs",   bus_property_append_uint32,    "u",  &m->n_failed_jobs  },
299                 { "org.freedesktop.systemd1.Manager", "Progress",      bus_manager_append_progress,   "d",  NULL               },
300                 { "org.freedesktop.systemd1.Manager", "Environment",   bus_property_append_strv,      "as", m->environment     },
301                 { "org.freedesktop.systemd1.Manager", "ConfirmSpawn",  bus_property_append_bool,      "b",  &m->confirm_spawn  },
302                 { "org.freedesktop.systemd1.Manager", "ShowStatus",    bus_property_append_bool,      "b",  &m->show_status    },
303                 { "org.freedesktop.systemd1.Manager", "UnitPath",      bus_property_append_strv,      "as", m->lookup_paths.unit_path },
304                 { "org.freedesktop.systemd1.Manager", "NotifySocket",  bus_property_append_string,    "s",  m->notify_socket   },
305                 { "org.freedesktop.systemd1.Manager", "ControlGroupHierarchy", bus_property_append_string, "s", m->cgroup_hierarchy },
306                 { "org.freedesktop.systemd1.Manager", "MountAuto",     bus_property_append_bool,      "b",  &m->mount_auto     },
307                 { "org.freedesktop.systemd1.Manager", "SwapAuto",      bus_property_append_bool,      "b",  &m->swap_auto     },
308 #ifdef HAVE_SYSV_COMPAT
309                 { "org.freedesktop.systemd1.Manager", "SysVConsole",   bus_property_append_bool,      "b",  &m->sysv_console   },
310                 { "org.freedesktop.systemd1.Manager", "SysVInitPath",  bus_property_append_strv,      "as", m->lookup_paths.sysvinit_path },
311                 { "org.freedesktop.systemd1.Manager", "SysVRcndPath",  bus_property_append_strv,      "as", m->lookup_paths.sysvrcnd_path },
312 #endif
313                 { NULL, NULL, NULL, NULL, NULL }
314         };
315
316         int r;
317         DBusError error;
318         DBusMessage *reply = NULL;
319         char * path = NULL;
320         JobType job_type = _JOB_TYPE_INVALID;
321         bool reload_if_possible = false;
322
323         assert(connection);
324         assert(message);
325         assert(m);
326
327         dbus_error_init(&error);
328
329         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
330                 const char *name;
331                 Unit *u;
332
333                 if (!dbus_message_get_args(
334                                     message,
335                                     &error,
336                                     DBUS_TYPE_STRING, &name,
337                                     DBUS_TYPE_INVALID))
338                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
339
340                 if (!(u = manager_get_unit(m, name))) {
341                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
342                         return bus_send_error_reply(m, connection, message, &error, -ENOENT);
343                 }
344
345                 if (!(reply = dbus_message_new_method_return(message)))
346                         goto oom;
347
348                 if (!(path = unit_dbus_path(u)))
349                         goto oom;
350
351                 if (!dbus_message_append_args(
352                                     reply,
353                                     DBUS_TYPE_OBJECT_PATH, &path,
354                                     DBUS_TYPE_INVALID))
355                         goto oom;
356         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
357                 Unit *u;
358                 uint32_t pid;
359
360                 if (!dbus_message_get_args(
361                                     message,
362                                     &error,
363                                     DBUS_TYPE_UINT32, &pid,
364                                     DBUS_TYPE_INVALID))
365                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
366
367                 if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) {
368                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
369                         return bus_send_error_reply(m, connection, message, &error, -ENOENT);
370                 }
371
372                 if (!(reply = dbus_message_new_method_return(message)))
373                         goto oom;
374
375                 if (!(path = unit_dbus_path(u)))
376                         goto oom;
377
378                 if (!dbus_message_append_args(
379                                     reply,
380                                     DBUS_TYPE_OBJECT_PATH, &path,
381                                     DBUS_TYPE_INVALID))
382                         goto oom;
383         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
384                 const char *name;
385                 Unit *u;
386
387                 if (!dbus_message_get_args(
388                                     message,
389                                     &error,
390                                     DBUS_TYPE_STRING, &name,
391                                     DBUS_TYPE_INVALID))
392                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
393
394                 if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
395                         return bus_send_error_reply(m, connection, message, &error, r);
396
397                 if (!(reply = dbus_message_new_method_return(message)))
398                         goto oom;
399
400                 if (!(path = unit_dbus_path(u)))
401                         goto oom;
402
403                 if (!dbus_message_append_args(
404                                     reply,
405                                     DBUS_TYPE_OBJECT_PATH, &path,
406                                     DBUS_TYPE_INVALID))
407                         goto oom;
408
409         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
410                 job_type = JOB_START;
411         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
412                 job_type = JOB_STOP;
413         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
414                 job_type = JOB_RELOAD;
415         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
416                 job_type = JOB_RESTART;
417         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
418                 job_type = JOB_TRY_RESTART;
419         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
420                 reload_if_possible = true;
421                 job_type = JOB_RESTART;
422         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
423                 reload_if_possible = true;
424                 job_type = JOB_TRY_RESTART;
425         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
426                 uint32_t id;
427                 Job *j;
428
429                 if (!dbus_message_get_args(
430                                     message,
431                                     &error,
432                                     DBUS_TYPE_UINT32, &id,
433                                     DBUS_TYPE_INVALID))
434                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
435
436                 if (!(j = manager_get_job(m, id))) {
437                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
438                         return bus_send_error_reply(m, connection, message, &error, -ENOENT);
439                 }
440
441                 if (!(reply = dbus_message_new_method_return(message)))
442                         goto oom;
443
444                 if (!(path = job_dbus_path(j)))
445                         goto oom;
446
447                 if (!dbus_message_append_args(
448                                     reply,
449                                     DBUS_TYPE_OBJECT_PATH, &path,
450                                     DBUS_TYPE_INVALID))
451                         goto oom;
452
453         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
454
455                 manager_clear_jobs(m);
456
457                 if (!(reply = dbus_message_new_method_return(message)))
458                         goto oom;
459
460         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
461
462                 manager_reset_failed(m);
463
464                 if (!(reply = dbus_message_new_method_return(message)))
465                         goto oom;
466
467         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
468                 const char *name;
469                 Unit *u;
470
471                 if (!dbus_message_get_args(
472                                     message,
473                                     &error,
474                                     DBUS_TYPE_STRING, &name,
475                                     DBUS_TYPE_INVALID))
476                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
477
478                 if (!(u = manager_get_unit(m, name))) {
479                         dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
480                         return bus_send_error_reply(m, connection, message, &error, -ENOENT);
481                 }
482
483                 unit_reset_failed(u);
484
485                 if (!(reply = dbus_message_new_method_return(message)))
486                         goto oom;
487
488         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
489                 DBusMessageIter iter, sub;
490                 Iterator i;
491                 Unit *u;
492                 const char *k;
493
494                 if (!(reply = dbus_message_new_method_return(message)))
495                         goto oom;
496
497                 dbus_message_iter_init_append(reply, &iter);
498
499                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
500                         goto oom;
501
502                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
503                         char *u_path, *j_path;
504                         const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
505                         DBusMessageIter sub2;
506                         uint32_t job_id;
507                         Unit *f;
508
509                         if (k != u->meta.id)
510                                 continue;
511
512                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
513                                 goto oom;
514
515                         description = unit_description(u);
516                         load_state = unit_load_state_to_string(u->meta.load_state);
517                         active_state = unit_active_state_to_string(unit_active_state(u));
518                         sub_state = unit_sub_state_to_string(u);
519
520                         f = unit_following(u);
521                         following = f ? f->meta.id : "";
522
523                         if (!(u_path = unit_dbus_path(u)))
524                                 goto oom;
525
526                         if (u->meta.job) {
527                                 job_id = (uint32_t) u->meta.job->id;
528
529                                 if (!(j_path = job_dbus_path(u->meta.job))) {
530                                         free(u_path);
531                                         goto oom;
532                                 }
533
534                                 sjob_type = job_type_to_string(u->meta.job->type);
535                         } else {
536                                 job_id = 0;
537                                 j_path = u_path;
538                                 sjob_type = "";
539                         }
540
541                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->meta.id) ||
542                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
543                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
544                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
545                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
546                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
547                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
548                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
549                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
550                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
551                                 free(u_path);
552                                 if (u->meta.job)
553                                         free(j_path);
554                                 goto oom;
555                         }
556
557                         free(u_path);
558                         if (u->meta.job)
559                                 free(j_path);
560
561                         if (!dbus_message_iter_close_container(&sub, &sub2))
562                                 goto oom;
563                 }
564
565                 if (!dbus_message_iter_close_container(&iter, &sub))
566                         goto oom;
567
568         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
569                 DBusMessageIter iter, sub;
570                 Iterator i;
571                 Job *j;
572
573                 if (!(reply = dbus_message_new_method_return(message)))
574                         goto oom;
575
576                 dbus_message_iter_init_append(reply, &iter);
577
578                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
579                         goto oom;
580
581                 HASHMAP_FOREACH(j, m->jobs, i) {
582                         char *u_path, *j_path;
583                         const char *state, *type;
584                         uint32_t id;
585                         DBusMessageIter sub2;
586
587                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
588                                 goto oom;
589
590                         id = (uint32_t) j->id;
591                         state = job_state_to_string(j->state);
592                         type = job_type_to_string(j->type);
593
594                         if (!(j_path = job_dbus_path(j)))
595                                 goto oom;
596
597                         if (!(u_path = unit_dbus_path(j->unit))) {
598                                 free(j_path);
599                                 goto oom;
600                         }
601
602                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
603                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->meta.id) ||
604                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
605                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
606                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
607                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
608                                 free(j_path);
609                                 free(u_path);
610                                 goto oom;
611                         }
612
613                         free(j_path);
614                         free(u_path);
615
616                         if (!dbus_message_iter_close_container(&sub, &sub2))
617                                 goto oom;
618                 }
619
620                 if (!dbus_message_iter_close_container(&iter, &sub))
621                         goto oom;
622
623         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
624                 char *client;
625                 Set *s;
626
627                 if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
628                         if (!(s = set_new(string_hash_func, string_compare_func)))
629                                 goto oom;
630
631                         if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
632                                 set_free(s);
633                                 goto oom;
634                         }
635                 }
636
637                 if (!(client = strdup(message_get_sender_with_fallback(message))))
638                         goto oom;
639
640                 if ((r = set_put(s, client)) < 0) {
641                         free(client);
642                         return bus_send_error_reply(m, connection, message, NULL, r);
643                 }
644
645                 if (!(reply = dbus_message_new_method_return(message)))
646                         goto oom;
647
648         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
649                 char *client;
650
651                 if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) {
652                         dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
653                         return bus_send_error_reply(m, connection, message, &error, -ENOENT);
654                 }
655
656                 free(client);
657
658                 if (!(reply = dbus_message_new_method_return(message)))
659                         goto oom;
660
661         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
662                 FILE *f;
663                 char *dump = NULL;
664                 size_t size;
665
666                 if (!(reply = dbus_message_new_method_return(message)))
667                         goto oom;
668
669                 if (!(f = open_memstream(&dump, &size)))
670                         goto oom;
671
672                 manager_dump_units(m, f, NULL);
673                 manager_dump_jobs(m, f, NULL);
674
675                 if (ferror(f)) {
676                         fclose(f);
677                         free(dump);
678                         goto oom;
679                 }
680
681                 fclose(f);
682
683                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
684                         free(dump);
685                         goto oom;
686                 }
687
688                 free(dump);
689         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
690                 const char *name;
691                 dbus_bool_t cleanup;
692                 Snapshot *s;
693
694                 if (!dbus_message_get_args(
695                                     message,
696                                     &error,
697                                     DBUS_TYPE_STRING, &name,
698                                     DBUS_TYPE_BOOLEAN, &cleanup,
699                                     DBUS_TYPE_INVALID))
700                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
701
702                 if (name && name[0] == 0)
703                         name = NULL;
704
705                 if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0)
706                         return bus_send_error_reply(m, connection, message, &error, r);
707
708                 if (!(reply = dbus_message_new_method_return(message)))
709                         goto oom;
710
711                 if (!(path = unit_dbus_path(UNIT(s))))
712                         goto oom;
713
714                 if (!dbus_message_append_args(
715                                     reply,
716                                     DBUS_TYPE_OBJECT_PATH, &path,
717                                     DBUS_TYPE_INVALID))
718                         goto oom;
719
720         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
721                 char *introspection = NULL;
722                 FILE *f;
723                 Iterator i;
724                 Unit *u;
725                 Job *j;
726                 const char *k;
727                 size_t size;
728
729                 if (!(reply = dbus_message_new_method_return(message)))
730                         goto oom;
731
732                 /* We roll our own introspection code here, instead of
733                  * relying on bus_default_message_handler() because we
734                  * need to generate our introspection string
735                  * dynamically. */
736
737                 if (!(f = open_memstream(&introspection, &size)))
738                         goto oom;
739
740                 fputs(INTROSPECTION_BEGIN, f);
741
742                 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
743                         char *p;
744
745                         if (k != u->meta.id)
746                                 continue;
747
748                         if (!(p = bus_path_escape(k))) {
749                                 fclose(f);
750                                 free(introspection);
751                                 goto oom;
752                         }
753
754                         fprintf(f, "<node name=\"unit/%s\"/>", p);
755                         free(p);
756                 }
757
758                 HASHMAP_FOREACH(j, m->jobs, i)
759                         fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
760
761                 fputs(INTROSPECTION_END, f);
762
763                 if (ferror(f)) {
764                         fclose(f);
765                         free(introspection);
766                         goto oom;
767                 }
768
769                 fclose(f);
770
771                 if (!introspection)
772                         goto oom;
773
774                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
775                         free(introspection);
776                         goto oom;
777                 }
778
779                 free(introspection);
780
781         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
782
783                 assert(!m->queued_message);
784
785                 /* Instead of sending the reply back right away, we
786                  * just remember that we need to and then send it
787                  * after the reload is finished. That way the caller
788                  * knows when the reload finished. */
789
790                 if (!(m->queued_message = dbus_message_new_method_return(message)))
791                         goto oom;
792
793                 m->queued_message_connection = connection;
794                 m->exit_code = MANAGER_RELOAD;
795
796         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
797
798                 if (!(reply = dbus_message_new_method_return(message)))
799                         goto oom;
800
801                 m->exit_code = MANAGER_REEXECUTE;
802
803         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
804
805                 if (m->running_as == MANAGER_SYSTEM) {
806                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for session managers.");
807                         return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
808                 }
809
810                 if (!(reply = dbus_message_new_method_return(message)))
811                         goto oom;
812
813                 m->exit_code = MANAGER_EXIT;
814
815         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
816
817                 if (m->running_as != MANAGER_SYSTEM) {
818                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
819                         return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
820                 }
821
822                 if (!(reply = dbus_message_new_method_return(message)))
823                         goto oom;
824
825                 m->exit_code = MANAGER_REBOOT;
826
827         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
828
829                 if (m->running_as != MANAGER_SYSTEM) {
830                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
831                         return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
832                 }
833
834                 if (!(reply = dbus_message_new_method_return(message)))
835                         goto oom;
836
837                 m->exit_code = MANAGER_POWEROFF;
838
839         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
840
841                 if (m->running_as != MANAGER_SYSTEM) {
842                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
843                         return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
844                 }
845
846                 if (!(reply = dbus_message_new_method_return(message)))
847                         goto oom;
848
849                 m->exit_code = MANAGER_HALT;
850
851         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
852
853                 if (m->running_as != MANAGER_SYSTEM) {
854                         dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
855                         return bus_send_error_reply(m, connection, message, &error, -ENOTSUP);
856                 }
857
858                 if (!(reply = dbus_message_new_method_return(message)))
859                         goto oom;
860
861                 m->exit_code = MANAGER_KEXEC;
862
863         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
864                 char **l = NULL, **e = NULL;
865
866                 if ((r = bus_parse_strv(message, &l)) < 0) {
867                         if (r == -ENOMEM)
868                                 goto oom;
869
870                         return bus_send_error_reply(m, connection, message, NULL, r);
871                 }
872
873                 e = strv_env_merge(2, m->environment, l);
874                 strv_free(l);
875
876                 if (!e)
877                         goto oom;
878
879                 if (!(reply = dbus_message_new_method_return(message))) {
880                         strv_free(e);
881                         goto oom;
882                 }
883
884                 strv_free(m->environment);
885                 m->environment = e;
886
887         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
888                 char **l = NULL, **e = NULL;
889
890                 if ((r = bus_parse_strv(message, &l)) < 0) {
891                         if (r == -ENOMEM)
892                                 goto oom;
893
894                         return bus_send_error_reply(m, connection, message, NULL, r);
895                 }
896
897                 e = strv_env_delete(m->environment, 1, l);
898                 strv_free(l);
899
900                 if (!e)
901                         goto oom;
902
903                 if (!(reply = dbus_message_new_method_return(message)))
904                         goto oom;
905
906                 strv_free(m->environment);
907                 m->environment = e;
908
909         } else
910                 return bus_default_message_handler(m, connection, message, NULL, properties);
911
912         if (job_type != _JOB_TYPE_INVALID) {
913                 const char *name, *smode;
914                 JobMode mode;
915                 Job *j;
916                 Unit *u;
917
918                 if (!dbus_message_get_args(
919                                     message,
920                                     &error,
921                                     DBUS_TYPE_STRING, &name,
922                                     DBUS_TYPE_STRING, &smode,
923                                     DBUS_TYPE_INVALID))
924                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
925
926                 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
927                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
928                         return bus_send_error_reply(m, connection, message, &error, -EINVAL);
929                 }
930
931                 if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
932                         return bus_send_error_reply(m, connection, message, &error, r);
933
934                 if (reload_if_possible && unit_can_reload(u)) {
935                         if (job_type == JOB_RESTART)
936                                 job_type = JOB_RELOAD_OR_START;
937                         else if (job_type == JOB_TRY_RESTART)
938                                 job_type = JOB_RELOAD;
939                 }
940
941                 if ((job_type == JOB_START && u->meta.refuse_manual_start) ||
942                     (job_type == JOB_STOP && u->meta.refuse_manual_stop) ||
943                     ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
944                      (u->meta.refuse_manual_start || u->meta.refuse_manual_stop))) {
945                         dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
946                         return bus_send_error_reply(m, connection, message, &error, -EPERM);
947                 }
948
949                 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
950                         return bus_send_error_reply(m, connection, message, &error, r);
951
952                 if (!(j->bus_client = strdup(message_get_sender_with_fallback(message))))
953                         goto oom;
954
955                 j->bus = connection;
956
957                 if (!(reply = dbus_message_new_method_return(message)))
958                         goto oom;
959
960                 if (!(path = job_dbus_path(j)))
961                         goto oom;
962
963                 if (!dbus_message_append_args(
964                                     reply,
965                                     DBUS_TYPE_OBJECT_PATH, &path,
966                                     DBUS_TYPE_INVALID))
967                         goto oom;
968         }
969
970         free(path);
971
972         if (reply) {
973                 if (!dbus_connection_send(connection, reply, NULL))
974                         goto oom;
975
976                 dbus_message_unref(reply);
977         }
978
979         return DBUS_HANDLER_RESULT_HANDLED;
980
981 oom:
982         free(path);
983
984         if (reply)
985                 dbus_message_unref(reply);
986
987         dbus_error_free(&error);
988
989         return DBUS_HANDLER_RESULT_NEED_MEMORY;
990 }
991
992 const DBusObjectPathVTable bus_manager_vtable = {
993         .message_function = bus_manager_message_handler
994 };