chiark / gitweb /
dbus: more efficient implementation of properties
[elogind.git] / src / dbus-unit.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-unit.h"
27 #include "bus-errors.h"
28 #include "dbus-common.h"
29
30 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
31
32 const BusProperty bus_unit_properties[] = {
33         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
34         { "Names",                bus_unit_append_names,             "as", 0 },
35         { "Following",            bus_unit_append_following,          "s", 0 },
36         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
37         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
38         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
39         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
40         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
41         { "BindTo",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BIND_TO]),                 true },
42         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
43         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
44         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
45         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
46         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
47         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
48         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
49         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
50         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
51         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
52         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
53         { "PropagateReloadTo",    bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]),     true },
54         { "PropagateReloadFrom",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]),   true },
55         { "Description",          bus_unit_append_description,        "s", 0 },
56         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
57         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
58         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
59         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
60         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
61         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
62         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
63         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
64         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
65         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
66         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
67         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
68         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
69         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
70         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
71         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
72         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
73         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
74         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
75         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
76         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
77         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
78         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
79         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
80         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
81         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
82         { "DefaultControlGroup",  bus_unit_append_default_cgroup,     "s", 0 },
83         { "ControlGroup",         bus_unit_append_cgroups,           "as", 0 },
84         { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
85         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
86         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
87         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
88         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
89         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
90         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
91         { NULL, }
92 };
93
94 #define INVALIDATING_PROPERTIES                 \
95         "LoadState\0"                           \
96         "ActiveState\0"                         \
97         "SubState\0"                            \
98         "InactiveExitTimestamp\0"               \
99         "ActiveEnterTimestamp\0"                \
100         "ActiveExitTimestamp\0"                 \
101         "InactiveEnterTimestamp\0"              \
102         "Job\0"                                 \
103         "NeedDaemonReload\0"
104
105 int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
106         char *t;
107         Iterator j;
108         DBusMessageIter sub;
109         Unit *u = data;
110
111         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
112                 return -ENOMEM;
113
114         SET_FOREACH(t, u->names, j)
115                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
116                         return -ENOMEM;
117
118         if (!dbus_message_iter_close_container(i, &sub))
119                 return -ENOMEM;
120
121         return 0;
122 }
123
124 int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
125         Unit *u = data, *f;
126         const char *d;
127
128         assert(i);
129         assert(property);
130         assert(u);
131
132         f = unit_following(u);
133         d = f ? f->id : "";
134
135         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
136                 return -ENOMEM;
137
138         return 0;
139 }
140
141 int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
142         Unit *u;
143         Iterator j;
144         DBusMessageIter sub;
145         Set *s = data;
146
147         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
148                 return -ENOMEM;
149
150         SET_FOREACH(u, s, j)
151                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
152                         return -ENOMEM;
153
154         if (!dbus_message_iter_close_container(i, &sub))
155                 return -ENOMEM;
156
157         return 0;
158 }
159
160 int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
161         Unit *u = data;
162         const char *d;
163
164         assert(i);
165         assert(property);
166         assert(u);
167
168         d = unit_description(u);
169
170         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
171                 return -ENOMEM;
172
173         return 0;
174 }
175
176 DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
177
178 int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
179         Unit *u = data;
180         const char *state;
181
182         assert(i);
183         assert(property);
184         assert(u);
185
186         state = unit_active_state_to_string(unit_active_state(u));
187
188         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
189                 return -ENOMEM;
190
191         return 0;
192 }
193
194 int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
195         Unit *u = data;
196         const char *state;
197
198         assert(i);
199         assert(property);
200         assert(u);
201
202         state = unit_sub_state_to_string(u);
203
204         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
205                 return -ENOMEM;
206
207         return 0;
208 }
209
210 int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
211         Unit *u = data;
212         const char *state;
213
214         assert(i);
215         assert(property);
216         assert(u);
217
218         state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
219
220         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
221                 return -ENOMEM;
222
223         return 0;
224 }
225
226 int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
227         Unit *u = data;
228         dbus_bool_t b;
229
230         assert(i);
231         assert(property);
232         assert(u);
233
234         b = unit_can_start(u) &&
235                 !u->refuse_manual_start;
236
237         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
238                 return -ENOMEM;
239
240         return 0;
241 }
242
243 int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
244         Unit *u = data;
245         dbus_bool_t b;
246
247         assert(i);
248         assert(property);
249         assert(u);
250
251         /* On the lower levels we assume that every unit we can start
252          * we can also stop */
253
254         b = unit_can_start(u) &&
255                 !u->refuse_manual_stop;
256
257         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
258                 return -ENOMEM;
259
260         return 0;
261 }
262
263 int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
264         Unit *u = data;
265         dbus_bool_t b;
266
267         assert(i);
268         assert(property);
269         assert(u);
270
271         b = unit_can_reload(u);
272
273         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
274                 return -ENOMEM;
275
276         return 0;
277 }
278
279 int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
280         Unit *u = data;
281         dbus_bool_t b;
282
283         assert(i);
284         assert(property);
285         assert(u);
286
287         b = unit_can_isolate(u) &&
288                 !u->refuse_manual_start;
289
290         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
291                 return -ENOMEM;
292
293         return 0;
294 }
295
296 int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
297         Unit *u = data;
298         DBusMessageIter sub;
299         char *p;
300
301         assert(i);
302         assert(property);
303         assert(u);
304
305         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
306                 return -ENOMEM;
307
308         if (u->job) {
309
310                 if (!(p = job_dbus_path(u->job)))
311                         return -ENOMEM;
312
313                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
314                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
315                         free(p);
316                         return -ENOMEM;
317                 }
318         } else {
319                 uint32_t id = 0;
320
321                 /* No job, so let's fill in some placeholder
322                  * data. Since we need to fill in a valid path we
323                  * simple point to ourselves. */
324
325                 if (!(p = unit_dbus_path(u)))
326                         return -ENOMEM;
327
328                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
329                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
330                         free(p);
331                         return -ENOMEM;
332                 }
333         }
334
335         free(p);
336
337         if (!dbus_message_iter_close_container(i, &sub))
338                 return -ENOMEM;
339
340         return 0;
341 }
342
343 int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
344         Unit *u = data;
345         char *t;
346         CGroupBonding *cgb;
347         bool success;
348
349         assert(i);
350         assert(property);
351         assert(u);
352
353         if ((cgb = unit_get_default_cgroup(u))) {
354                 if (!(t = cgroup_bonding_to_string(cgb)))
355                         return -ENOMEM;
356         } else
357                 t = (char*) "";
358
359         success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
360
361         if (cgb)
362                 free(t);
363
364         return success ? 0 : -ENOMEM;
365 }
366
367 int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
368         Unit *u = data;
369         CGroupBonding *cgb;
370         DBusMessageIter sub;
371
372         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
373                 return -ENOMEM;
374
375         LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
376                 char *t;
377                 bool success;
378
379                 if (!(t = cgroup_bonding_to_string(cgb)))
380                         return -ENOMEM;
381
382                 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
383                 free(t);
384
385                 if (!success)
386                         return -ENOMEM;
387         }
388
389         if (!dbus_message_iter_close_container(i, &sub))
390                 return -ENOMEM;
391
392         return 0;
393 }
394
395 int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
396         Unit *u = data;
397         CGroupAttribute *a;
398         DBusMessageIter sub, sub2;
399
400         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
401                 return -ENOMEM;
402
403         LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
404                 char *v = NULL;
405                 bool success;
406
407                 if (a->map_callback)
408                         a->map_callback(a->controller, a->name, a->value, &v);
409
410                 success =
411                         dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
412                         dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
413                         dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
414                         dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
415                         dbus_message_iter_close_container(&sub, &sub2);
416
417                 free(v);
418
419                 if (!success)
420                         return -ENOMEM;
421         }
422
423         if (!dbus_message_iter_close_container(i, &sub))
424                 return -ENOMEM;
425
426         return 0;
427 }
428
429 int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
430         Unit *u = data;
431         dbus_bool_t b;
432
433         assert(i);
434         assert(property);
435         assert(u);
436
437         b = unit_need_daemon_reload(u);
438
439         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
440                 return -ENOMEM;
441
442         return 0;
443 }
444
445 int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
446         Unit *u = data;
447         const char *name, *message;
448         DBusMessageIter sub;
449
450         assert(i);
451         assert(property);
452         assert(u);
453
454         if (u->load_error != 0) {
455                 name = bus_errno_to_dbus(u->load_error);
456                 message = strempty(strerror(-u->load_error));
457         } else
458                 name = message = "";
459
460         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
461             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
462             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
463             !dbus_message_iter_close_container(i, &sub))
464                 return -ENOMEM;
465
466         return 0;
467 }
468
469 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
470         DBusMessage *reply = NULL;
471         Manager *m = u->manager;
472         DBusError error;
473         JobType job_type = _JOB_TYPE_INVALID;
474         char *path = NULL;
475         bool reload_if_possible = false;
476
477         dbus_error_init(&error);
478
479         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
480                 job_type = JOB_START;
481         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
482                 job_type = JOB_STOP;
483         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
484                 job_type = JOB_RELOAD;
485         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
486                 job_type = JOB_RESTART;
487         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
488                 job_type = JOB_TRY_RESTART;
489         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
490                 reload_if_possible = true;
491                 job_type = JOB_RESTART;
492         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
493                 reload_if_possible = true;
494                 job_type = JOB_TRY_RESTART;
495         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
496                 const char *swho, *smode;
497                 int32_t signo;
498                 KillMode mode;
499                 KillWho who;
500                 int r;
501
502                 if (!dbus_message_get_args(
503                                     message,
504                                     &error,
505                                     DBUS_TYPE_STRING, &swho,
506                                     DBUS_TYPE_STRING, &smode,
507                                     DBUS_TYPE_INT32, &signo,
508                                     DBUS_TYPE_INVALID))
509                         return bus_send_error_reply(connection, message, &error, -EINVAL);
510
511                 if (isempty(swho))
512                         who = KILL_ALL;
513                 else {
514                         who = kill_who_from_string(swho);
515                         if (who < 0)
516                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
517                 }
518
519                 if (isempty(smode))
520                         mode = KILL_CONTROL_GROUP;
521                 else {
522                         mode = kill_mode_from_string(smode);
523                         if (mode < 0)
524                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
525                 }
526
527                 if (signo <= 0 || signo >= _NSIG)
528                         return bus_send_error_reply(connection, message, &error, -EINVAL);
529
530                 if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
531                         return bus_send_error_reply(connection, message, &error, r);
532
533                 if (!(reply = dbus_message_new_method_return(message)))
534                         goto oom;
535
536         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
537
538                 unit_reset_failed(u);
539
540                 if (!(reply = dbus_message_new_method_return(message)))
541                         goto oom;
542
543         } else if (UNIT_VTABLE(u)->bus_message_handler)
544                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
545         else
546                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
547
548         if (job_type != _JOB_TYPE_INVALID) {
549                 const char *smode;
550                 JobMode mode;
551                 Job *j;
552                 int r;
553
554                 if ((job_type == JOB_START && u->refuse_manual_start) ||
555                     (job_type == JOB_STOP && u->refuse_manual_stop) ||
556                     ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
557                      (u->refuse_manual_start || u->refuse_manual_stop))) {
558                         dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
559                         return bus_send_error_reply(connection, message, &error, -EPERM);
560                 }
561
562                 if (!dbus_message_get_args(
563                                     message,
564                                     &error,
565                                     DBUS_TYPE_STRING, &smode,
566                                     DBUS_TYPE_INVALID))
567                         return bus_send_error_reply(connection, message, &error, -EINVAL);
568
569                 if (reload_if_possible && unit_can_reload(u)) {
570                         if (job_type == JOB_RESTART)
571                                 job_type = JOB_RELOAD_OR_START;
572                         else if (job_type == JOB_TRY_RESTART)
573                                 job_type = JOB_RELOAD;
574                 }
575
576                 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
577                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
578                         return bus_send_error_reply(connection, message, &error, -EINVAL);
579                 }
580
581                 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
582                         return bus_send_error_reply(connection, message, &error, r);
583
584                 if (!(reply = dbus_message_new_method_return(message)))
585                         goto oom;
586
587                 if (!(path = job_dbus_path(j)))
588                         goto oom;
589
590                 if (!dbus_message_append_args(
591                                     reply,
592                                     DBUS_TYPE_OBJECT_PATH, &path,
593                                     DBUS_TYPE_INVALID))
594                         goto oom;
595         }
596
597         if (reply) {
598                 if (!dbus_connection_send(connection, reply, NULL))
599                         goto oom;
600
601                 dbus_message_unref(reply);
602         }
603
604         free(path);
605
606         return DBUS_HANDLER_RESULT_HANDLED;
607
608 oom:
609         free(path);
610
611         if (reply)
612                 dbus_message_unref(reply);
613
614         dbus_error_free(&error);
615
616         return DBUS_HANDLER_RESULT_NEED_MEMORY;
617 }
618
619 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
620         Manager *m = data;
621         Unit *u;
622         int r;
623         DBusMessage *reply;
624
625         assert(connection);
626         assert(message);
627         assert(m);
628
629         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
630                 /* Be nice to gdbus and return introspection data for our mid-level paths */
631
632                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
633                         char *introspection = NULL;
634                         FILE *f;
635                         Iterator i;
636                         const char *k;
637                         size_t size;
638
639                         if (!(reply = dbus_message_new_method_return(message)))
640                                 goto oom;
641
642                         /* We roll our own introspection code here, instead of
643                          * relying on bus_default_message_handler() because we
644                          * need to generate our introspection string
645                          * dynamically. */
646
647                         if (!(f = open_memstream(&introspection, &size)))
648                                 goto oom;
649
650                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
651                               "<node>\n", f);
652
653                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
654                         fputs(BUS_PEER_INTERFACE, f);
655
656                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
657                                 char *p;
658
659                                 if (k != u->id)
660                                         continue;
661
662                                 if (!(p = bus_path_escape(k))) {
663                                         fclose(f);
664                                         free(introspection);
665                                         goto oom;
666                                 }
667
668                                 fprintf(f, "<node name=\"%s\"/>", p);
669                                 free(p);
670                         }
671
672                         fputs("</node>\n", f);
673
674                         if (ferror(f)) {
675                                 fclose(f);
676                                 free(introspection);
677                                 goto oom;
678                         }
679
680                         fclose(f);
681
682                         if (!introspection)
683                                 goto oom;
684
685                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
686                                 free(introspection);
687                                 goto oom;
688                         }
689
690                         free(introspection);
691
692                         if (!dbus_connection_send(connection, reply, NULL))
693                                 goto oom;
694
695                         dbus_message_unref(reply);
696
697                         return DBUS_HANDLER_RESULT_HANDLED;
698                 }
699
700                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
701         }
702
703         if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
704
705                 if (r == -ENOMEM)
706                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
707
708                 if (r == -ENOENT) {
709                         DBusError e;
710
711                         dbus_error_init(&e);
712                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
713                         return bus_send_error_reply(connection, message, &e, r);
714                 }
715
716                 return bus_send_error_reply(connection, message, NULL, r);
717         }
718
719         return bus_unit_message_dispatch(u, connection, message);
720
721 oom:
722         if (reply)
723                 dbus_message_unref(reply);
724
725         return DBUS_HANDLER_RESULT_NEED_MEMORY;
726 }
727
728 const DBusObjectPathVTable bus_unit_vtable = {
729         .message_function = bus_unit_message_handler
730 };
731
732 void bus_unit_send_change_signal(Unit *u) {
733         char *p = NULL;
734         DBusMessage *m = NULL;
735
736         assert(u);
737
738         if (u->in_dbus_queue) {
739                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
740                 u->in_dbus_queue = false;
741         }
742
743         if (!u->id)
744                 return;
745
746         if (!bus_has_subscriber(u->manager)) {
747                 u->sent_dbus_new_signal = true;
748                 return;
749         }
750
751         if (!(p = unit_dbus_path(u)))
752                 goto oom;
753
754         if (u->sent_dbus_new_signal) {
755                 /* Send a properties changed signal. First for the
756                  * specific type, then for the generic unit. The
757                  * clients may rely on this order to get atomic
758                  * behaviour if needed. */
759
760                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
761
762                         if (!(m = bus_properties_changed_new(p,
763                                                              UNIT_VTABLE(u)->bus_interface,
764                                                              UNIT_VTABLE(u)->bus_invalidating_properties)))
765                                 goto oom;
766
767                         if (bus_broadcast(u->manager, m) < 0)
768                                 goto oom;
769
770                         dbus_message_unref(m);
771                 }
772
773                 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
774                         goto oom;
775
776         } else {
777                 /* Send a new signal */
778
779                 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
780                         goto oom;
781
782                 if (!dbus_message_append_args(m,
783                                               DBUS_TYPE_STRING, &u->id,
784                                               DBUS_TYPE_OBJECT_PATH, &p,
785                                               DBUS_TYPE_INVALID))
786                         goto oom;
787         }
788
789         if (bus_broadcast(u->manager, m) < 0)
790                 goto oom;
791
792         free(p);
793         dbus_message_unref(m);
794
795         u->sent_dbus_new_signal = true;
796
797         return;
798
799 oom:
800         free(p);
801
802         if (m)
803                 dbus_message_unref(m);
804
805         log_error("Failed to allocate unit change/new signal.");
806 }
807
808 void bus_unit_send_removed_signal(Unit *u) {
809         char *p = NULL;
810         DBusMessage *m = NULL;
811
812         assert(u);
813
814         if (!bus_has_subscriber(u->manager))
815                 return;
816
817         if (!u->sent_dbus_new_signal)
818                 bus_unit_send_change_signal(u);
819
820         if (!u->id)
821                 return;
822
823         if (!(p = unit_dbus_path(u)))
824                 goto oom;
825
826         if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
827                 goto oom;
828
829         if (!dbus_message_append_args(m,
830                                       DBUS_TYPE_STRING, &u->id,
831                                       DBUS_TYPE_OBJECT_PATH, &p,
832                                       DBUS_TYPE_INVALID))
833                 goto oom;
834
835         if (bus_broadcast(u->manager, m) < 0)
836                 goto oom;
837
838         free(p);
839         dbus_message_unref(m);
840
841         return;
842
843 oom:
844         free(p);
845
846         if (m)
847                 dbus_message_unref(m);
848
849         log_error("Failed to allocate unit remove signal.");
850 }