]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/dbus-unit.c
build-sys: cleanup sysctl.d/coredump.conf
[thirdparty/systemd.git] / src / dbus-unit.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ea430986 2
a7334b09
LP
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
ea430986
LP
22#include <errno.h>
23
24#include "dbus.h"
25#include "log.h"
4139c1b2 26#include "dbus-unit.h"
398ef8ba 27#include "bus-errors.h"
bfebab7f 28#include "dbus-common.h"
ea430986 29
9a60da28 30const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
4288f619 31
d200735e
MS
32const 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
c4e2ceae
LP
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" \
34df5a34 103 "NeedDaemonReload\0"
c4e2ceae 104
bfebab7f 105int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
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
ac155bb8 114 SET_FOREACH(t, u->names, j)
4139c1b2
LP
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
bfebab7f 124int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
a7f241db 125 Unit *u = data, *f;
8fe914ec
LP
126 const char *d;
127
8fe914ec
LP
128 assert(i);
129 assert(property);
130 assert(u);
131
a7f241db 132 f = unit_following(u);
ac155bb8 133 d = f ? f->id : "";
8fe914ec
LP
134
135 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
136 return -ENOMEM;
137
138 return 0;
139}
140
bfebab7f 141int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
5301be81
LP
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)
ac155bb8 151 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
5301be81
LP
152 return -ENOMEM;
153
154 if (!dbus_message_iter_close_container(i, &sub))
155 return -ENOMEM;
156
157 return 0;
158}
159
bfebab7f 160int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
161 Unit *u = data;
162 const char *d;
163
ea430986
LP
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
6f4706b7 176DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
ea430986 177
bfebab7f 178int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
ea430986
LP
179 Unit *u = data;
180 const char *state;
181
ea430986
LP
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
bfebab7f 194int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
10a94420
LP
195 Unit *u = data;
196 const char *state;
197
10a94420
LP
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
a4375746
LP
210int 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
bfebab7f 226int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
227 Unit *u = data;
228 dbus_bool_t b;
229
38131695
LP
230 assert(i);
231 assert(property);
232 assert(u);
233
064f51fa 234 b = unit_can_start(u) &&
ac155bb8 235 !u->refuse_manual_start;
b5e9dba8
LP
236
237 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
238 return -ENOMEM;
239
240 return 0;
241}
242
bfebab7f 243int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
b5e9dba8
LP
244 Unit *u = data;
245 dbus_bool_t b;
246
b5e9dba8
LP
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) &&
ac155bb8 255 !u->refuse_manual_stop;
38131695
LP
256
257 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
258 return -ENOMEM;
259
260 return 0;
261}
262
bfebab7f 263int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
264 Unit *u = data;
265 dbus_bool_t b;
266
38131695
LP
267 assert(i);
268 assert(property);
269 assert(u);
270
4139c1b2 271 b = unit_can_reload(u);
38131695
LP
272
273 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
274 return -ENOMEM;
275
276 return 0;
277}
278
bfebab7f 279int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
2528a7a6
LP
280 Unit *u = data;
281 dbus_bool_t b;
282
2528a7a6
LP
283 assert(i);
284 assert(property);
285 assert(u);
286
287 b = unit_can_isolate(u) &&
ac155bb8 288 !u->refuse_manual_start;
2528a7a6
LP
289
290 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
291 return -ENOMEM;
292
293 return 0;
294}
295
bfebab7f 296int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
38131695
LP
297 Unit *u = data;
298 DBusMessageIter sub;
299 char *p;
300
38131695
LP
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
ac155bb8 308 if (u->job) {
38131695 309
ac155bb8 310 if (!(p = job_dbus_path(u->job)))
38131695
LP
311 return -ENOMEM;
312
ac155bb8 313 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
86ad3bc1 314 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
38131695
LP
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) ||
86ad3bc1 329 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
38131695
LP
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
bfebab7f 343int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
344 Unit *u = data;
345 char *t;
346 CGroupBonding *cgb;
347 bool success;
ea430986 348
4139c1b2
LP
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*) "";
ea430986 358
4139c1b2
LP
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
bfebab7f 367int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
4139c1b2
LP
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
ac155bb8 375 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
4139c1b2
LP
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
d8bbda91
LP
395int 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
ac155bb8 403 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
d8bbda91
LP
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
bfebab7f 429int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
45fb0699
LP
430 Unit *u = data;
431 dbus_bool_t b;
432
45fb0699
LP
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
9f39404c
LP
445int 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
ac155bb8
MS
454 if (u->load_error != 0) {
455 name = bus_errno_to_dbus(u->load_error);
456 message = strempty(strerror(-u->load_error));
9f39404c
LP
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
5e8d1c9a 469static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
b548631a 470 DBusMessage *reply = NULL;
ac155bb8 471 Manager *m = u->manager;
b548631a
LP
472 DBusError error;
473 JobType job_type = _JOB_TYPE_INVALID;
c87eba54 474 char *path = NULL;
6f28c033 475 bool reload_if_possible = false;
b548631a
LP
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;
9a1ac7b9
LP
487 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
488 job_type = JOB_TRY_RESTART;
6f28c033
LP
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;
8a0867d6
LP
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))
bfebab7f 509 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6 510
0a524ba7
LP
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)
bfebab7f 528 return bus_send_error_reply(connection, message, &error, -EINVAL);
8a0867d6
LP
529
530 if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
bfebab7f 531 return bus_send_error_reply(connection, message, &error, r);
8a0867d6
LP
532
533 if (!(reply = dbus_message_new_method_return(message)))
534 goto oom;
535
fdf20a31 536 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
5632e374 537
fdf20a31 538 unit_reset_failed(u);
5632e374
LP
539
540 if (!(reply = dbus_message_new_method_return(message)))
541 goto oom;
542
6f28c033 543 } else if (UNIT_VTABLE(u)->bus_message_handler)
5e8d1c9a 544 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
b548631a 545 else
4139c1b2 546 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
b548631a
LP
547
548 if (job_type != _JOB_TYPE_INVALID) {
549 const char *smode;
550 JobMode mode;
551 Job *j;
552 int r;
b548631a 553
ac155bb8
MS
554 if ((job_type == JOB_START && u->refuse_manual_start) ||
555 (job_type == JOB_STOP && u->refuse_manual_stop) ||
b5e9dba8 556 ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
ac155bb8 557 (u->refuse_manual_start || u->refuse_manual_stop))) {
b5e9dba8 558 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
bfebab7f 559 return bus_send_error_reply(connection, message, &error, -EPERM);
398ef8ba 560 }
bc0f8771 561
b548631a
LP
562 if (!dbus_message_get_args(
563 message,
564 &error,
565 DBUS_TYPE_STRING, &smode,
566 DBUS_TYPE_INVALID))
bfebab7f 567 return bus_send_error_reply(connection, message, &error, -EINVAL);
b548631a 568
6f28c033
LP
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
398ef8ba
LP
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);
bfebab7f 578 return bus_send_error_reply(connection, message, &error, -EINVAL);
398ef8ba 579 }
b548631a 580
398ef8ba 581 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
bfebab7f 582 return bus_send_error_reply(connection, message, &error, r);
b548631a
LP
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) {
5e8d1c9a 598 if (!dbus_connection_send(connection, reply, NULL))
b548631a
LP
599 goto oom;
600
601 dbus_message_unref(reply);
602 }
603
da19d5c1
LP
604 free(path);
605
b548631a
LP
606 return DBUS_HANDLER_RESULT_HANDLED;
607
608oom:
c87eba54
LP
609 free(path);
610
b548631a
LP
611 if (reply)
612 dbus_message_unref(reply);
613
614 dbus_error_free(&error);
615
616 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
617}
618
5e8d1c9a 619static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
ea430986
LP
620 Manager *m = data;
621 Unit *u;
622 int r;
2cccbca4 623 DBusMessage *reply;
ea430986
LP
624
625 assert(connection);
626 assert(message);
627 assert(m);
628
2cccbca4
LP
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
ac155bb8 659 if (k != u->id)
2cccbca4
LP
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
ea430986
LP
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
08672cb5
LP
708 if (r == -ENOENT) {
709 DBusError e;
b8a021c9
AB
710
711 dbus_error_init(&e);
08672cb5 712 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
bfebab7f 713 return bus_send_error_reply(connection, message, &e, r);
08672cb5 714 }
ea430986 715
bfebab7f 716 return bus_send_error_reply(connection, message, NULL, r);
ea430986
LP
717 }
718
5e8d1c9a 719 return bus_unit_message_dispatch(u, connection, message);
2cccbca4
LP
720
721oom:
722 if (reply)
723 dbus_message_unref(reply);
724
725 return DBUS_HANDLER_RESULT_NEED_MEMORY;
ea430986
LP
726}
727
728const DBusObjectPathVTable bus_unit_vtable = {
729 .message_function = bus_unit_message_handler
730};
c1e1601e
LP
731
732void bus_unit_send_change_signal(Unit *u) {
733 char *p = NULL;
734 DBusMessage *m = NULL;
735
736 assert(u);
c1e1601e 737
ac155bb8
MS
738 if (u->in_dbus_queue) {
739 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
740 u->in_dbus_queue = false;
c0bd0cf7 741 }
c1e1601e 742
ac155bb8 743 if (!u->id)
04ade7d2
LP
744 return;
745
ac155bb8
MS
746 if (!bus_has_subscriber(u->manager)) {
747 u->sent_dbus_new_signal = true;
c1e1601e 748 return;
94b6dfa2 749 }
c1e1601e
LP
750
751 if (!(p = unit_dbus_path(u)))
752 goto oom;
753
ac155bb8 754 if (u->sent_dbus_new_signal) {
c4e2ceae
LP
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
ac155bb8 767 if (bus_broadcast(u->manager, m) < 0)
c4e2ceae 768 goto oom;
c1e1601e 769
c4e2ceae
LP
770 dbus_message_unref(m);
771 }
772
773 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
c1e1601e 774 goto oom;
c4e2ceae 775
c1e1601e 776 } else {
c1e1601e
LP
777 /* Send a new signal */
778
701cc384 779 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
c1e1601e
LP
780 goto oom;
781
c1e1601e 782 if (!dbus_message_append_args(m,
ac155bb8 783 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
784 DBUS_TYPE_OBJECT_PATH, &p,
785 DBUS_TYPE_INVALID))
786 goto oom;
787 }
788
ac155bb8 789 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
790 goto oom;
791
792 free(p);
793 dbus_message_unref(m);
794
ac155bb8 795 u->sent_dbus_new_signal = true;
c1e1601e
LP
796
797 return;
798
799oom:
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
808void bus_unit_send_removed_signal(Unit *u) {
809 char *p = NULL;
810 DBusMessage *m = NULL;
c1e1601e
LP
811
812 assert(u);
813
ac155bb8 814 if (!bus_has_subscriber(u->manager))
c1e1601e
LP
815 return;
816
ac155bb8 817 if (!u->sent_dbus_new_signal)
7535cc78
LP
818 bus_unit_send_change_signal(u);
819
ac155bb8 820 if (!u->id)
04ade7d2
LP
821 return;
822
c1e1601e
LP
823 if (!(p = unit_dbus_path(u)))
824 goto oom;
825
701cc384 826 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
c1e1601e
LP
827 goto oom;
828
c1e1601e 829 if (!dbus_message_append_args(m,
ac155bb8 830 DBUS_TYPE_STRING, &u->id,
c1e1601e
LP
831 DBUS_TYPE_OBJECT_PATH, &p,
832 DBUS_TYPE_INVALID))
833 goto oom;
834
ac155bb8 835 if (bus_broadcast(u->manager, m) < 0)
c1e1601e
LP
836 goto oom;
837
838 free(p);
839 dbus_message_unref(m);
840
841 return;
842
843oom:
844 free(p);
845
846 if (m)
847 dbus_message_unref(m);
848
849 log_error("Failed to allocate unit remove signal.");
850}