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