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