chiark / gitweb /
units: introduce new Documentation= field and make use of it everywhere
[elogind.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 = NULL;
562         DBusError error;
563
564         assert(connection);
565         assert(message);
566         assert(m);
567
568         dbus_error_init(&error);
569
570         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
571                 /* Be nice to gdbus and return introspection data for our mid-level paths */
572
573                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
574                         char *introspection = NULL;
575                         FILE *f;
576                         Iterator i;
577                         const char *k;
578                         size_t size;
579
580                         if (!(reply = dbus_message_new_method_return(message)))
581                                 goto oom;
582
583                         /* We roll our own introspection code here, instead of
584                          * relying on bus_default_message_handler() because we
585                          * need to generate our introspection string
586                          * dynamically. */
587
588                         if (!(f = open_memstream(&introspection, &size)))
589                                 goto oom;
590
591                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
592                               "<node>\n", f);
593
594                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
595                         fputs(BUS_PEER_INTERFACE, f);
596
597                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
598                                 char *p;
599
600                                 if (k != u->id)
601                                         continue;
602
603                                 if (!(p = bus_path_escape(k))) {
604                                         fclose(f);
605                                         free(introspection);
606                                         goto oom;
607                                 }
608
609                                 fprintf(f, "<node name=\"%s\"/>", p);
610                                 free(p);
611                         }
612
613                         fputs("</node>\n", f);
614
615                         if (ferror(f)) {
616                                 fclose(f);
617                                 free(introspection);
618                                 goto oom;
619                         }
620
621                         fclose(f);
622
623                         if (!introspection)
624                                 goto oom;
625
626                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
627                                 free(introspection);
628                                 goto oom;
629                         }
630
631                         free(introspection);
632
633                         if (!dbus_connection_send(connection, reply, NULL))
634                                 goto oom;
635
636                         dbus_message_unref(reply);
637
638                         return DBUS_HANDLER_RESULT_HANDLED;
639                 }
640
641                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
642         }
643
644         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
645         if (r < 0) {
646                 if (r == -ENOMEM)
647                         goto oom;
648
649                 return bus_send_error_reply(connection, message, &error, r);
650         }
651
652         return bus_unit_message_dispatch(u, connection, message);
653
654 oom:
655         if (reply)
656                 dbus_message_unref(reply);
657
658         dbus_error_free(&error);
659
660         return DBUS_HANDLER_RESULT_NEED_MEMORY;
661 }
662
663 const DBusObjectPathVTable bus_unit_vtable = {
664         .message_function = bus_unit_message_handler
665 };
666
667 void bus_unit_send_change_signal(Unit *u) {
668         char *p = NULL;
669         DBusMessage *m = NULL;
670
671         assert(u);
672
673         if (u->in_dbus_queue) {
674                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
675                 u->in_dbus_queue = false;
676         }
677
678         if (!u->id)
679                 return;
680
681         if (!bus_has_subscriber(u->manager)) {
682                 u->sent_dbus_new_signal = true;
683                 return;
684         }
685
686         if (!(p = unit_dbus_path(u)))
687                 goto oom;
688
689         if (u->sent_dbus_new_signal) {
690                 /* Send a properties changed signal. First for the
691                  * specific type, then for the generic unit. The
692                  * clients may rely on this order to get atomic
693                  * behaviour if needed. */
694
695                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
696
697                         if (!(m = bus_properties_changed_new(p,
698                                                              UNIT_VTABLE(u)->bus_interface,
699                                                              UNIT_VTABLE(u)->bus_invalidating_properties)))
700                                 goto oom;
701
702                         if (bus_broadcast(u->manager, m) < 0)
703                                 goto oom;
704
705                         dbus_message_unref(m);
706                 }
707
708                 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
709                         goto oom;
710
711         } else {
712                 /* Send a new signal */
713
714                 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
715                         goto oom;
716
717                 if (!dbus_message_append_args(m,
718                                               DBUS_TYPE_STRING, &u->id,
719                                               DBUS_TYPE_OBJECT_PATH, &p,
720                                               DBUS_TYPE_INVALID))
721                         goto oom;
722         }
723
724         if (bus_broadcast(u->manager, m) < 0)
725                 goto oom;
726
727         free(p);
728         dbus_message_unref(m);
729
730         u->sent_dbus_new_signal = true;
731
732         return;
733
734 oom:
735         free(p);
736
737         if (m)
738                 dbus_message_unref(m);
739
740         log_error("Failed to allocate unit change/new signal.");
741 }
742
743 void bus_unit_send_removed_signal(Unit *u) {
744         char *p = NULL;
745         DBusMessage *m = NULL;
746
747         assert(u);
748
749         if (!bus_has_subscriber(u->manager))
750                 return;
751
752         if (!u->sent_dbus_new_signal)
753                 bus_unit_send_change_signal(u);
754
755         if (!u->id)
756                 return;
757
758         if (!(p = unit_dbus_path(u)))
759                 goto oom;
760
761         if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
762                 goto oom;
763
764         if (!dbus_message_append_args(m,
765                                       DBUS_TYPE_STRING, &u->id,
766                                       DBUS_TYPE_OBJECT_PATH, &p,
767                                       DBUS_TYPE_INVALID))
768                 goto oom;
769
770         if (bus_broadcast(u->manager, m) < 0)
771                 goto oom;
772
773         free(p);
774         dbus_message_unref(m);
775
776         return;
777
778 oom:
779         free(p);
780
781         if (m)
782                 dbus_message_unref(m);
783
784         log_error("Failed to allocate unit remove signal.");
785 }
786
787 const BusProperty bus_unit_properties[] = {
788         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
789         { "Names",                bus_unit_append_names,             "as", 0 },
790         { "Following",            bus_unit_append_following,          "s", 0 },
791         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
792         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
793         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
794         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
795         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
796         { "BindTo",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BIND_TO]),                 true },
797         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
798         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
799         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
800         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
801         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
802         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
803         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
804         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
805         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
806         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
807         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
808         { "PropagateReloadTo",    bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]),     true },
809         { "PropagateReloadFrom",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]),   true },
810         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
811         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
812         { "Description",          bus_unit_append_description,        "s", 0 },
813         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
814         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
815         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
816         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
817         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
818         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
819         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
820         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
821         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
822         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
823         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
824         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
825         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
826         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
827         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
828         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
829         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
830         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
831         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
832         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
833         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
834         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
835         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
836         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
837         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
838         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
839         { "DefaultControlGroup",  bus_unit_append_default_cgroup,     "s", 0 },
840         { "ControlGroup",         bus_unit_append_cgroups,           "as", 0 },
841         { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
842         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
843         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
844         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
845         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
846         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
847         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
848         { NULL, }
849 };