chiark / gitweb /
core: drop KillMode parameter from KillUnit() bus call
[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;
435                 int32_t signo;
436                 KillWho who;
437                 int r;
438
439                 if (!dbus_message_get_args(
440                                     message,
441                                     &error,
442                                     DBUS_TYPE_STRING, &swho,
443                                     DBUS_TYPE_INT32, &signo,
444                                     DBUS_TYPE_INVALID))
445                         return bus_send_error_reply(connection, message, &error, -EINVAL);
446
447                 if (isempty(swho))
448                         who = KILL_ALL;
449                 else {
450                         who = kill_who_from_string(swho);
451                         if (who < 0)
452                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
453                 }
454
455                 if (signo <= 0 || signo >= _NSIG)
456                         return bus_send_error_reply(connection, message, &error, -EINVAL);
457
458                 r = unit_kill(u, who, signo, &error);
459                 if (r < 0)
460                         return bus_send_error_reply(connection, message, &error, r);
461
462                 reply = dbus_message_new_method_return(message);
463                 if (!reply)
464                         goto oom;
465
466         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
467
468                 unit_reset_failed(u);
469
470                 if (!(reply = dbus_message_new_method_return(message)))
471                         goto oom;
472
473         } else if (UNIT_VTABLE(u)->bus_message_handler)
474                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
475         else
476                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
477
478         if (job_type != _JOB_TYPE_INVALID) {
479                 const char *smode;
480                 JobMode mode;
481                 Job *j;
482                 int r;
483
484                 if ((job_type == JOB_START && u->refuse_manual_start) ||
485                     (job_type == JOB_STOP && u->refuse_manual_stop) ||
486                     ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
487                      (u->refuse_manual_start || u->refuse_manual_stop))) {
488                         dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
489                         return bus_send_error_reply(connection, message, &error, -EPERM);
490                 }
491
492                 if (!dbus_message_get_args(
493                                     message,
494                                     &error,
495                                     DBUS_TYPE_STRING, &smode,
496                                     DBUS_TYPE_INVALID))
497                         return bus_send_error_reply(connection, message, &error, -EINVAL);
498
499                 if (reload_if_possible && unit_can_reload(u)) {
500                         if (job_type == JOB_RESTART)
501                                 job_type = JOB_RELOAD_OR_START;
502                         else if (job_type == JOB_TRY_RESTART)
503                                 job_type = JOB_RELOAD;
504                 }
505
506                 if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
507                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
508                         return bus_send_error_reply(connection, message, &error, -EINVAL);
509                 }
510
511                 if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
512                         return bus_send_error_reply(connection, message, &error, r);
513
514                 if (!(reply = dbus_message_new_method_return(message)))
515                         goto oom;
516
517                 if (!(path = job_dbus_path(j)))
518                         goto oom;
519
520                 if (!dbus_message_append_args(
521                                     reply,
522                                     DBUS_TYPE_OBJECT_PATH, &path,
523                                     DBUS_TYPE_INVALID))
524                         goto oom;
525         }
526
527         if (reply) {
528                 if (!dbus_connection_send(connection, reply, NULL))
529                         goto oom;
530
531                 dbus_message_unref(reply);
532         }
533
534         free(path);
535
536         return DBUS_HANDLER_RESULT_HANDLED;
537
538 oom:
539         free(path);
540
541         if (reply)
542                 dbus_message_unref(reply);
543
544         dbus_error_free(&error);
545
546         return DBUS_HANDLER_RESULT_NEED_MEMORY;
547 }
548
549 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
550         Manager *m = data;
551         Unit *u;
552         int r;
553         DBusMessage *reply = NULL;
554         DBusError error;
555
556         assert(connection);
557         assert(message);
558         assert(m);
559
560         dbus_error_init(&error);
561
562         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
563                 /* Be nice to gdbus and return introspection data for our mid-level paths */
564
565                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
566                         char *introspection = NULL;
567                         FILE *f;
568                         Iterator i;
569                         const char *k;
570                         size_t size;
571
572                         if (!(reply = dbus_message_new_method_return(message)))
573                                 goto oom;
574
575                         /* We roll our own introspection code here, instead of
576                          * relying on bus_default_message_handler() because we
577                          * need to generate our introspection string
578                          * dynamically. */
579
580                         if (!(f = open_memstream(&introspection, &size)))
581                                 goto oom;
582
583                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
584                               "<node>\n", f);
585
586                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
587                         fputs(BUS_PEER_INTERFACE, f);
588
589                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
590                                 char *p;
591
592                                 if (k != u->id)
593                                         continue;
594
595                                 if (!(p = bus_path_escape(k))) {
596                                         fclose(f);
597                                         free(introspection);
598                                         goto oom;
599                                 }
600
601                                 fprintf(f, "<node name=\"%s\"/>", p);
602                                 free(p);
603                         }
604
605                         fputs("</node>\n", f);
606
607                         if (ferror(f)) {
608                                 fclose(f);
609                                 free(introspection);
610                                 goto oom;
611                         }
612
613                         fclose(f);
614
615                         if (!introspection)
616                                 goto oom;
617
618                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
619                                 free(introspection);
620                                 goto oom;
621                         }
622
623                         free(introspection);
624
625                         if (!dbus_connection_send(connection, reply, NULL))
626                                 goto oom;
627
628                         dbus_message_unref(reply);
629
630                         return DBUS_HANDLER_RESULT_HANDLED;
631                 }
632
633                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
634         }
635
636         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
637         if (r < 0) {
638                 if (r == -ENOMEM)
639                         goto oom;
640
641                 return bus_send_error_reply(connection, message, &error, r);
642         }
643
644         return bus_unit_message_dispatch(u, connection, message);
645
646 oom:
647         if (reply)
648                 dbus_message_unref(reply);
649
650         dbus_error_free(&error);
651
652         return DBUS_HANDLER_RESULT_NEED_MEMORY;
653 }
654
655 const DBusObjectPathVTable bus_unit_vtable = {
656         .message_function = bus_unit_message_handler
657 };
658
659 void bus_unit_send_change_signal(Unit *u) {
660         char *p = NULL;
661         DBusMessage *m = NULL;
662
663         assert(u);
664
665         if (u->in_dbus_queue) {
666                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
667                 u->in_dbus_queue = false;
668         }
669
670         if (!u->id)
671                 return;
672
673         if (!bus_has_subscriber(u->manager)) {
674                 u->sent_dbus_new_signal = true;
675                 return;
676         }
677
678         if (!(p = unit_dbus_path(u)))
679                 goto oom;
680
681         if (u->sent_dbus_new_signal) {
682                 /* Send a properties changed signal. First for the
683                  * specific type, then for the generic unit. The
684                  * clients may rely on this order to get atomic
685                  * behaviour if needed. */
686
687                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
688
689                         if (!(m = bus_properties_changed_new(p,
690                                                              UNIT_VTABLE(u)->bus_interface,
691                                                              UNIT_VTABLE(u)->bus_invalidating_properties)))
692                                 goto oom;
693
694                         if (bus_broadcast(u->manager, m) < 0)
695                                 goto oom;
696
697                         dbus_message_unref(m);
698                 }
699
700                 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
701                         goto oom;
702
703         } else {
704                 /* Send a new signal */
705
706                 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
707                         goto oom;
708
709                 if (!dbus_message_append_args(m,
710                                               DBUS_TYPE_STRING, &u->id,
711                                               DBUS_TYPE_OBJECT_PATH, &p,
712                                               DBUS_TYPE_INVALID))
713                         goto oom;
714         }
715
716         if (bus_broadcast(u->manager, m) < 0)
717                 goto oom;
718
719         free(p);
720         dbus_message_unref(m);
721
722         u->sent_dbus_new_signal = true;
723
724         return;
725
726 oom:
727         free(p);
728
729         if (m)
730                 dbus_message_unref(m);
731
732         log_error("Failed to allocate unit change/new signal.");
733 }
734
735 void bus_unit_send_removed_signal(Unit *u) {
736         char *p = NULL;
737         DBusMessage *m = NULL;
738
739         assert(u);
740
741         if (!bus_has_subscriber(u->manager))
742                 return;
743
744         if (!u->sent_dbus_new_signal)
745                 bus_unit_send_change_signal(u);
746
747         if (!u->id)
748                 return;
749
750         if (!(p = unit_dbus_path(u)))
751                 goto oom;
752
753         if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
754                 goto oom;
755
756         if (!dbus_message_append_args(m,
757                                       DBUS_TYPE_STRING, &u->id,
758                                       DBUS_TYPE_OBJECT_PATH, &p,
759                                       DBUS_TYPE_INVALID))
760                 goto oom;
761
762         if (bus_broadcast(u->manager, m) < 0)
763                 goto oom;
764
765         free(p);
766         dbus_message_unref(m);
767
768         return;
769
770 oom:
771         free(p);
772
773         if (m)
774                 dbus_message_unref(m);
775
776         log_error("Failed to allocate unit remove signal.");
777 }
778
779 const BusProperty bus_unit_properties[] = {
780         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
781         { "Names",                bus_unit_append_names,             "as", 0 },
782         { "Following",            bus_unit_append_following,          "s", 0 },
783         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
784         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
785         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
786         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
787         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
788         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
789         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
790         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
791         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
792         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
793         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
794         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
795         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
796         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
797         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
798         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
799         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
800         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
801         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
802         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
803         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
804         { "Description",          bus_unit_append_description,        "s", 0 },
805         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
806         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
807         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
808         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
809         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
810         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
811         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
812         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
813         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
814         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
815         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
816         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
817         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
818         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
819         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
820         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
821         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
822         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
823         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
824         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
825         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
826         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
827         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
828         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
829         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
830         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
831         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
832         { "DefaultControlGroup",  bus_unit_append_default_cgroup,     "s", 0 },
833         { "ControlGroup",         bus_unit_append_cgroups,           "as", 0 },
834         { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
835         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
836         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
837         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
838         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
839         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
840         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
841         { NULL, }
842 };