]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-unit.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / core / 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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser 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 #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" \
41 "NeedDaemonReload\0"
42
43 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
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
52 SET_FOREACH(t, u->names, j)
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
62 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
63 Unit *u = data, *f;
64 const char *d;
65
66 assert(i);
67 assert(property);
68 assert(u);
69
70 f = unit_following(u);
71 d = f ? f->id : "";
72
73 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
74 return -ENOMEM;
75
76 return 0;
77 }
78
79 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
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)
89 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
90 return -ENOMEM;
91
92 if (!dbus_message_iter_close_container(i, &sub))
93 return -ENOMEM;
94
95 return 0;
96 }
97
98 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
99 Unit *u = data;
100 const char *d;
101
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
114 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
115
116 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
117 Unit *u = data;
118 const char *state;
119
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
132 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
133 Unit *u = data;
134 const char *state;
135
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
148 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
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
164 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
165 Unit *u = data;
166 dbus_bool_t b;
167
168 assert(i);
169 assert(property);
170 assert(u);
171
172 b = unit_can_start(u) &&
173 !u->refuse_manual_start;
174
175 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
176 return -ENOMEM;
177
178 return 0;
179 }
180
181 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
182 Unit *u = data;
183 dbus_bool_t b;
184
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) &&
193 !u->refuse_manual_stop;
194
195 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
196 return -ENOMEM;
197
198 return 0;
199 }
200
201 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
202 Unit *u = data;
203 dbus_bool_t b;
204
205 assert(i);
206 assert(property);
207 assert(u);
208
209 b = unit_can_reload(u);
210
211 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
212 return -ENOMEM;
213
214 return 0;
215 }
216
217 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
218 Unit *u = data;
219 dbus_bool_t b;
220
221 assert(i);
222 assert(property);
223 assert(u);
224
225 b = unit_can_isolate(u) &&
226 !u->refuse_manual_start;
227
228 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
229 return -ENOMEM;
230
231 return 0;
232 }
233
234 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
235 Unit *u = data;
236 DBusMessageIter sub;
237 char *p;
238
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
246 if (u->job) {
247
248 if (!(p = job_dbus_path(u->job)))
249 return -ENOMEM;
250
251 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
252 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
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) ||
267 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
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
281 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
282 Unit *u = data;
283 char *t;
284 CGroupBonding *cgb;
285 bool success;
286
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*) "";
296
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
305 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
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
313 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
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
333 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
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
341 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
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
367 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
368 Unit *u = data;
369 dbus_bool_t b;
370
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
383 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
384 Unit *u = data;
385 const char *name, *message;
386 DBusMessageIter sub;
387
388 assert(i);
389 assert(property);
390 assert(u);
391
392 if (u->load_error != 0) {
393 name = bus_errno_to_dbus(u->load_error);
394 message = strempty(strerror(-u->load_error));
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
407 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
408 DBusMessage *reply = NULL;
409 Manager *m = u->manager;
410 DBusError error;
411 JobType job_type = _JOB_TYPE_INVALID;
412 char *path = NULL;
413 bool reload_if_possible = false;
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;
425 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
426 job_type = JOB_TRY_RESTART;
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;
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))
447 return bus_send_error_reply(connection, message, &error, -EINVAL);
448
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)
466 return bus_send_error_reply(connection, message, &error, -EINVAL);
467
468 if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
469 return bus_send_error_reply(connection, message, &error, r);
470
471 if (!(reply = dbus_message_new_method_return(message)))
472 goto oom;
473
474 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
475
476 unit_reset_failed(u);
477
478 if (!(reply = dbus_message_new_method_return(message)))
479 goto oom;
480
481 } else if (UNIT_VTABLE(u)->bus_message_handler)
482 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
483 else
484 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
485
486 if (job_type != _JOB_TYPE_INVALID) {
487 const char *smode;
488 JobMode mode;
489 Job *j;
490 int r;
491
492 if ((job_type == JOB_START && u->refuse_manual_start) ||
493 (job_type == JOB_STOP && u->refuse_manual_stop) ||
494 ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
495 (u->refuse_manual_start || u->refuse_manual_stop))) {
496 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
497 return bus_send_error_reply(connection, message, &error, -EPERM);
498 }
499
500 if (!dbus_message_get_args(
501 message,
502 &error,
503 DBUS_TYPE_STRING, &smode,
504 DBUS_TYPE_INVALID))
505 return bus_send_error_reply(connection, message, &error, -EINVAL);
506
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
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);
516 return bus_send_error_reply(connection, message, &error, -EINVAL);
517 }
518
519 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
520 return bus_send_error_reply(connection, message, &error, r);
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) {
536 if (!dbus_connection_send(connection, reply, NULL))
537 goto oom;
538
539 dbus_message_unref(reply);
540 }
541
542 free(path);
543
544 return DBUS_HANDLER_RESULT_HANDLED;
545
546 oom:
547 free(path);
548
549 if (reply)
550 dbus_message_unref(reply);
551
552 dbus_error_free(&error);
553
554 return DBUS_HANDLER_RESULT_NEED_MEMORY;
555 }
556
557 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
558 Manager *m = data;
559 Unit *u;
560 int r;
561 DBusMessage *reply;
562
563 assert(connection);
564 assert(message);
565 assert(m);
566
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
597 if (k != u->id)
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
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
646 if (r == -ENOENT) {
647 DBusError e;
648
649 dbus_error_init(&e);
650 dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
651 return bus_send_error_reply(connection, message, &e, r);
652 }
653
654 return bus_send_error_reply(connection, message, NULL, r);
655 }
656
657 return bus_unit_message_dispatch(u, connection, message);
658
659 oom:
660 if (reply)
661 dbus_message_unref(reply);
662
663 return DBUS_HANDLER_RESULT_NEED_MEMORY;
664 }
665
666 const DBusObjectPathVTable bus_unit_vtable = {
667 .message_function = bus_unit_message_handler
668 };
669
670 void bus_unit_send_change_signal(Unit *u) {
671 char *p = NULL;
672 DBusMessage *m = NULL;
673
674 assert(u);
675
676 if (u->in_dbus_queue) {
677 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
678 u->in_dbus_queue = false;
679 }
680
681 if (!u->id)
682 return;
683
684 if (!bus_has_subscriber(u->manager)) {
685 u->sent_dbus_new_signal = true;
686 return;
687 }
688
689 if (!(p = unit_dbus_path(u)))
690 goto oom;
691
692 if (u->sent_dbus_new_signal) {
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
705 if (bus_broadcast(u->manager, m) < 0)
706 goto oom;
707
708 dbus_message_unref(m);
709 }
710
711 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
712 goto oom;
713
714 } else {
715 /* Send a new signal */
716
717 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
718 goto oom;
719
720 if (!dbus_message_append_args(m,
721 DBUS_TYPE_STRING, &u->id,
722 DBUS_TYPE_OBJECT_PATH, &p,
723 DBUS_TYPE_INVALID))
724 goto oom;
725 }
726
727 if (bus_broadcast(u->manager, m) < 0)
728 goto oom;
729
730 free(p);
731 dbus_message_unref(m);
732
733 u->sent_dbus_new_signal = true;
734
735 return;
736
737 oom:
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
746 void bus_unit_send_removed_signal(Unit *u) {
747 char *p = NULL;
748 DBusMessage *m = NULL;
749
750 assert(u);
751
752 if (!bus_has_subscriber(u->manager))
753 return;
754
755 if (!u->sent_dbus_new_signal)
756 bus_unit_send_change_signal(u);
757
758 if (!u->id)
759 return;
760
761 if (!(p = unit_dbus_path(u)))
762 goto oom;
763
764 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
765 goto oom;
766
767 if (!dbus_message_append_args(m,
768 DBUS_TYPE_STRING, &u->id,
769 DBUS_TYPE_OBJECT_PATH, &p,
770 DBUS_TYPE_INVALID))
771 goto oom;
772
773 if (bus_broadcast(u->manager, m) < 0)
774 goto oom;
775
776 free(p);
777 dbus_message_unref(m);
778
779 return;
780
781 oom:
782 free(p);
783
784 if (m)
785 dbus_message_unref(m);
786
787 log_error("Failed to allocate unit remove signal.");
788 }
789
790 const 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 };