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