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