chiark / gitweb /
b7391b5506d2945eed08e3ae1315d63311e15271
[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 #include "cgroup-util.h"
31 #include "strv.h"
32 #include "path-util.h"
33 #include "fileio.h"
34
35 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
36
37 #define INVALIDATING_PROPERTIES                 \
38         "LoadState\0"                           \
39         "ActiveState\0"                         \
40         "SubState\0"                            \
41         "InactiveExitTimestamp\0"               \
42         "ActiveEnterTimestamp\0"                \
43         "ActiveExitTimestamp\0"                 \
44         "InactiveEnterTimestamp\0"              \
45         "Job\0"                                 \
46         "NeedDaemonReload\0"
47
48 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
49         char *t;
50         Iterator j;
51         DBusMessageIter sub;
52         Unit *u = data;
53
54         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
55                 return -ENOMEM;
56
57         SET_FOREACH(t, u->names, j)
58                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
59                         return -ENOMEM;
60
61         if (!dbus_message_iter_close_container(i, &sub))
62                 return -ENOMEM;
63
64         return 0;
65 }
66
67 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
68         Unit *u = data, *f;
69         const char *d;
70
71         assert(i);
72         assert(property);
73         assert(u);
74
75         f = unit_following(u);
76         d = f ? f->id : "";
77
78         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
79                 return -ENOMEM;
80
81         return 0;
82 }
83
84 static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void *data) {
85         Unit *u = data;
86         const char *d;
87
88         assert(i);
89         assert(property);
90         assert(u);
91
92         if (UNIT_DEREF(u->slice))
93                 d = UNIT_DEREF(u->slice)->id;
94         else
95                 d = "";
96
97         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
98                 return -ENOMEM;
99
100         return 0;
101 }
102
103 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
104         Unit *u;
105         Iterator j;
106         DBusMessageIter sub;
107         Set *s = data;
108
109         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
110                 return -ENOMEM;
111
112         SET_FOREACH(u, s, j)
113                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
114                         return -ENOMEM;
115
116         if (!dbus_message_iter_close_container(i, &sub))
117                 return -ENOMEM;
118
119         return 0;
120 }
121
122 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
123         Unit *u = data;
124         const char *d;
125
126         assert(i);
127         assert(property);
128         assert(u);
129
130         d = unit_description(u);
131
132         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
133                 return -ENOMEM;
134
135         return 0;
136 }
137
138 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
139
140 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
141         Unit *u = data;
142         const char *state;
143
144         assert(i);
145         assert(property);
146         assert(u);
147
148         state = unit_active_state_to_string(unit_active_state(u));
149
150         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
151                 return -ENOMEM;
152
153         return 0;
154 }
155
156 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
157         Unit *u = data;
158         const char *state;
159
160         assert(i);
161         assert(property);
162         assert(u);
163
164         state = unit_sub_state_to_string(u);
165
166         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
167                 return -ENOMEM;
168
169         return 0;
170 }
171
172 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
173         Unit *u = data;
174         const char *state;
175
176         assert(i);
177         assert(property);
178         assert(u);
179
180         state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
181
182         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
183                 return -ENOMEM;
184
185         return 0;
186 }
187
188 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
189         Unit *u = data;
190         dbus_bool_t b;
191
192         assert(i);
193         assert(property);
194         assert(u);
195
196         b = unit_can_start(u) &&
197                 !u->refuse_manual_start;
198
199         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
200                 return -ENOMEM;
201
202         return 0;
203 }
204
205 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
206         Unit *u = data;
207         dbus_bool_t b;
208
209         assert(i);
210         assert(property);
211         assert(u);
212
213         /* On the lower levels we assume that every unit we can start
214          * we can also stop */
215
216         b = unit_can_start(u) &&
217                 !u->refuse_manual_stop;
218
219         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
220                 return -ENOMEM;
221
222         return 0;
223 }
224
225 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
226         Unit *u = data;
227         dbus_bool_t b;
228
229         assert(i);
230         assert(property);
231         assert(u);
232
233         b = unit_can_reload(u);
234
235         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
236                 return -ENOMEM;
237
238         return 0;
239 }
240
241 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
242         Unit *u = data;
243         dbus_bool_t b;
244
245         assert(i);
246         assert(property);
247         assert(u);
248
249         b = unit_can_isolate(u) &&
250                 !u->refuse_manual_start;
251
252         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
253                 return -ENOMEM;
254
255         return 0;
256 }
257
258 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
259         Unit *u = data;
260         DBusMessageIter sub;
261         _cleanup_free_ char *p = NULL;
262
263         assert(i);
264         assert(property);
265         assert(u);
266
267         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
268                 return -ENOMEM;
269
270         if (u->job) {
271
272                 p = job_dbus_path(u->job);
273                 if (!p)
274                         return -ENOMEM;
275
276                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
277                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
278                         return -ENOMEM;
279         } else {
280                 uint32_t id = 0;
281
282                 /* No job, so let's fill in some placeholder
283                  * data. Since we need to fill in a valid path we
284                  * simple point to ourselves. */
285
286                 p = unit_dbus_path(u);
287                 if (!p)
288                         return -ENOMEM;
289
290                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
291                     !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
292                         return -ENOMEM;
293         }
294
295         if (!dbus_message_iter_close_container(i, &sub))
296                 return -ENOMEM;
297
298         return 0;
299 }
300
301 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
302         Unit *u = data;
303         char *t;
304         CGroupBonding *cgb;
305         bool success;
306
307         assert(i);
308         assert(property);
309         assert(u);
310
311         cgb = unit_get_default_cgroup(u);
312         if (cgb) {
313                 t = cgroup_bonding_to_string(cgb);
314                 if (!t)
315                         return -ENOMEM;
316         } else
317                 t = (char*) "";
318
319         success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
320
321         if (cgb)
322                 free(t);
323
324         return success ? 0 : -ENOMEM;
325 }
326
327 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
328         Unit *u = data;
329         CGroupBonding *cgb;
330         DBusMessageIter sub;
331
332         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
333                 return -ENOMEM;
334
335         LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
336                 _cleanup_free_ char *t = NULL;
337                 bool success;
338
339                 t = cgroup_bonding_to_string(cgb);
340                 if (!t)
341                         return -ENOMEM;
342
343                 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
344                 if (!success)
345                         return -ENOMEM;
346         }
347
348         if (!dbus_message_iter_close_container(i, &sub))
349                 return -ENOMEM;
350
351         return 0;
352 }
353
354 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
355         Unit *u = data;
356         CGroupAttribute *a;
357         DBusMessageIter sub, sub2;
358
359         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
360                 return -ENOMEM;
361
362         LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
363                 _cleanup_free_ char *v = NULL;
364                 bool success;
365
366                 if (a->semantics && a->semantics->map_write)
367                         a->semantics->map_write(a->semantics, a->value, &v);
368
369                 success =
370                         dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
371                         dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
372                         dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
373                         dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
374                         dbus_message_iter_close_container(&sub, &sub2);
375                 if (!success)
376                         return -ENOMEM;
377         }
378
379         if (!dbus_message_iter_close_container(i, &sub))
380                 return -ENOMEM;
381
382         return 0;
383 }
384
385 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
386         Unit *u = data;
387         dbus_bool_t b;
388
389         assert(i);
390         assert(property);
391         assert(u);
392
393         b = unit_need_daemon_reload(u);
394
395         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
396                 return -ENOMEM;
397
398         return 0;
399 }
400
401 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
402         Unit *u = data;
403         const char *name, *message;
404         DBusMessageIter sub;
405
406         assert(i);
407         assert(property);
408         assert(u);
409
410         if (u->load_error != 0) {
411                 name = bus_errno_to_dbus(u->load_error);
412                 message = strempty(strerror(-u->load_error));
413         } else
414                 name = message = "";
415
416         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
417             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
418             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
419             !dbus_message_iter_close_container(i, &sub))
420                 return -ENOMEM;
421
422         return 0;
423 }
424
425 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
426         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
427         DBusError error;
428         JobType job_type = _JOB_TYPE_INVALID;
429         bool reload_if_possible = false;
430         int r;
431
432         dbus_error_init(&error);
433
434         if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
435                 job_type = JOB_START;
436         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
437                 job_type = JOB_STOP;
438         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
439                 job_type = JOB_RELOAD;
440         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
441                 job_type = JOB_RESTART;
442         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
443                 job_type = JOB_TRY_RESTART;
444         else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
445                 reload_if_possible = true;
446                 job_type = JOB_RESTART;
447         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
448                 reload_if_possible = true;
449                 job_type = JOB_TRY_RESTART;
450         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
451                 const char *swho;
452                 int32_t signo;
453                 KillWho who;
454
455                 if (!dbus_message_get_args(
456                                     message,
457                                     &error,
458                                     DBUS_TYPE_STRING, &swho,
459                                     DBUS_TYPE_INT32, &signo,
460                                     DBUS_TYPE_INVALID))
461                         return bus_send_error_reply(connection, message, &error, -EINVAL);
462
463                 if (isempty(swho))
464                         who = KILL_ALL;
465                 else {
466                         who = kill_who_from_string(swho);
467                         if (who < 0)
468                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
469                 }
470
471                 if (signo <= 0 || signo >= _NSIG)
472                         return bus_send_error_reply(connection, message, &error, -EINVAL);
473
474                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
475
476                 r = unit_kill(u, who, signo, &error);
477                 if (r < 0)
478                         return bus_send_error_reply(connection, message, &error, r);
479
480                 reply = dbus_message_new_method_return(message);
481                 if (!reply)
482                         goto oom;
483
484         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
485
486                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
487
488                 unit_reset_failed(u);
489
490                 reply = dbus_message_new_method_return(message);
491                 if (!reply)
492                         goto oom;
493
494         } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
495                 DBusMessageIter iter;
496
497                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
498
499                 if (!dbus_message_iter_init(message, &iter))
500                         goto oom;
501
502                 r = bus_unit_cgroup_set(u, &iter);
503                 if (r < 0)
504                         return bus_send_error_reply(connection, message, NULL, r);
505
506                 reply = dbus_message_new_method_return(message);
507                 if (!reply)
508                         goto oom;
509
510         } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
511                 DBusMessageIter iter;
512
513                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
514
515                 if (!dbus_message_iter_init(message, &iter))
516                         goto oom;
517
518                 r = bus_unit_cgroup_unset(u, &iter);
519                 if (r < 0)
520                         return bus_send_error_reply(connection, message, NULL, r);
521
522                 reply = dbus_message_new_method_return(message);
523                 if (!reply)
524                         goto oom;
525         } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
526                 DBusMessageIter iter;
527                 _cleanup_strv_free_ char **list = NULL;
528
529                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
530
531                 if (!dbus_message_iter_init(message, &iter))
532                         goto oom;
533
534                 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
535                 if (r < 0)
536                         return bus_send_error_reply(connection, message, NULL, r);
537
538                 reply = dbus_message_new_method_return(message);
539                 if (!reply)
540                         goto oom;
541
542                 dbus_message_iter_init_append(reply, &iter);
543                 if (bus_append_strv_iter(&iter, list) < 0)
544                         goto oom;
545
546         } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
547                 DBusMessageIter iter;
548
549                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
550
551                 if (!dbus_message_iter_init(message, &iter))
552                         goto oom;
553
554                 r = bus_unit_cgroup_attribute_set(u, &iter);
555                 if (r < 0)
556                         return bus_send_error_reply(connection, message, NULL, r);
557
558                 reply = dbus_message_new_method_return(message);
559                 if (!reply)
560                         goto oom;
561
562         } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
563                 DBusMessageIter iter;
564
565                 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
566
567                 if (!dbus_message_iter_init(message, &iter))
568                         goto oom;
569
570                 r = bus_unit_cgroup_attribute_unset(u, &iter);
571                 if (r < 0)
572                         return bus_send_error_reply(connection, message, NULL, r);
573
574                 reply = dbus_message_new_method_return(message);
575                 if (!reply)
576                         goto oom;
577
578         } else if (UNIT_VTABLE(u)->bus_message_handler)
579                 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
580         else
581                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
582
583         if (job_type != _JOB_TYPE_INVALID) {
584                 const char *smode;
585                 JobMode mode;
586
587                 if (!dbus_message_get_args(
588                                     message,
589                                     &error,
590                                     DBUS_TYPE_STRING, &smode,
591                                     DBUS_TYPE_INVALID))
592                         return bus_send_error_reply(connection, message, &error, -EINVAL);
593
594                 mode = job_mode_from_string(smode);
595                 if (mode < 0) {
596                         dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
597                         return bus_send_error_reply(connection, message, &error, -EINVAL);
598                 }
599
600                 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
601         }
602
603         if (reply)
604                 if (!bus_maybe_send_reply(connection, message, reply))
605                         goto oom;
606
607         return DBUS_HANDLER_RESULT_HANDLED;
608
609 oom:
610         dbus_error_free(&error);
611         return DBUS_HANDLER_RESULT_NEED_MEMORY;
612 }
613
614 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage  *message, void *data) {
615         Manager *m = data;
616         Unit *u;
617         int r;
618         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
619         DBusError error;
620
621         assert(connection);
622         assert(message);
623         assert(m);
624
625         dbus_error_init(&error);
626
627         if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
628                 /* Be nice to gdbus and return introspection data for our mid-level paths */
629
630                 SELINUX_ACCESS_CHECK(connection, message, "status");
631
632                 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
633                         char *introspection = NULL;
634                         FILE *f;
635                         Iterator i;
636                         const char *k;
637                         size_t size;
638
639                         reply = dbus_message_new_method_return(message);
640                         if (!reply)
641                                 goto oom;
642
643                         /* We roll our own introspection code here, instead of
644                          * relying on bus_default_message_handler() because we
645                          * need to generate our introspection string
646                          * dynamically. */
647
648                         f = open_memstream(&introspection, &size);
649                         if (!f)
650                                 goto oom;
651
652                         fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
653                               "<node>\n", f);
654
655                         fputs(BUS_INTROSPECTABLE_INTERFACE, f);
656                         fputs(BUS_PEER_INTERFACE, f);
657
658                         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
659                                 char *p;
660
661                                 if (k != u->id)
662                                         continue;
663
664                                 p = bus_path_escape(k);
665                                 if (!p) {
666                                         fclose(f);
667                                         free(introspection);
668                                         goto oom;
669                                 }
670
671                                 fprintf(f, "<node name=\"%s\"/>", p);
672                                 free(p);
673                         }
674
675                         fputs("</node>\n", f);
676
677                         if (ferror(f)) {
678                                 fclose(f);
679                                 free(introspection);
680                                 goto oom;
681                         }
682
683                         fclose(f);
684
685                         if (!introspection)
686                                 goto oom;
687
688                         if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
689                                 free(introspection);
690                                 goto oom;
691                         }
692
693                         free(introspection);
694
695                         if (!bus_maybe_send_reply(connection, message, reply))
696                                 goto oom;
697
698                         return DBUS_HANDLER_RESULT_HANDLED;
699                 }
700
701                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
702         }
703
704         r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
705         if (r == -ENOMEM)
706                 goto oom;
707         if (r < 0)
708                 return bus_send_error_reply(connection, message, &error, r);
709
710         return bus_unit_message_dispatch(u, connection, message);
711
712 oom:
713         dbus_error_free(&error);
714
715         return DBUS_HANDLER_RESULT_NEED_MEMORY;
716 }
717
718 const DBusObjectPathVTable bus_unit_vtable = {
719         .message_function = bus_unit_message_handler
720 };
721
722 void bus_unit_send_change_signal(Unit *u) {
723         _cleanup_free_ char *p = NULL;
724         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
725
726         assert(u);
727
728         if (u->in_dbus_queue) {
729                 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
730                 u->in_dbus_queue = false;
731         }
732
733         if (!u->id)
734                 return;
735
736         if (!bus_has_subscriber(u->manager)) {
737                 u->sent_dbus_new_signal = true;
738                 return;
739         }
740
741         p = unit_dbus_path(u);
742         if (!p)
743                 goto oom;
744
745         if (u->sent_dbus_new_signal) {
746                 /* Send a properties changed signal. First for the
747                  * specific type, then for the generic unit. The
748                  * clients may rely on this order to get atomic
749                  * behavior if needed. */
750
751                 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
752
753                         m = bus_properties_changed_new(p,
754                                                        UNIT_VTABLE(u)->bus_interface,
755                                                        UNIT_VTABLE(u)->bus_invalidating_properties);
756                         if (!m)
757                                 goto oom;
758
759                         if (bus_broadcast(u->manager, m) < 0)
760                                 goto oom;
761
762                         dbus_message_unref(m);
763                 }
764
765                 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
766                                                INVALIDATING_PROPERTIES);
767                 if (!m)
768                         goto oom;
769
770         } else {
771                 /* Send a new signal */
772
773                 m = dbus_message_new_signal("/org/freedesktop/systemd1",
774                                             "org.freedesktop.systemd1.Manager",
775                                             "UnitNew");
776                 if (!m)
777                         goto oom;
778
779                 if (!dbus_message_append_args(m,
780                                               DBUS_TYPE_STRING, &u->id,
781                                               DBUS_TYPE_OBJECT_PATH, &p,
782                                               DBUS_TYPE_INVALID))
783                         goto oom;
784         }
785
786         if (bus_broadcast(u->manager, m) < 0)
787                 goto oom;
788
789         u->sent_dbus_new_signal = true;
790
791         return;
792
793 oom:
794         log_oom();
795 }
796
797 void bus_unit_send_removed_signal(Unit *u) {
798         _cleanup_free_ char *p = NULL;
799         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
800
801         assert(u);
802
803         if (!bus_has_subscriber(u->manager))
804                 return;
805
806         if (!u->sent_dbus_new_signal)
807                 bus_unit_send_change_signal(u);
808
809         if (!u->id)
810                 return;
811
812         p = unit_dbus_path(u);
813         if (!p)
814                 goto oom;
815
816         m = dbus_message_new_signal("/org/freedesktop/systemd1",
817                                     "org.freedesktop.systemd1.Manager",
818                                     "UnitRemoved");
819         if (!m)
820                 goto oom;
821
822         if (!dbus_message_append_args(m,
823                                       DBUS_TYPE_STRING, &u->id,
824                                       DBUS_TYPE_OBJECT_PATH, &p,
825                                       DBUS_TYPE_INVALID))
826                 goto oom;
827
828         if (bus_broadcast(u->manager, m) < 0)
829                 goto oom;
830
831         return;
832
833 oom:
834         log_oom();
835 }
836
837 DBusHandlerResult bus_unit_queue_job(
838                 DBusConnection *connection,
839                 DBusMessage *message,
840                 Unit *u,
841                 JobType type,
842                 JobMode mode,
843                 bool reload_if_possible) {
844
845         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
846         _cleanup_free_ char *path = NULL;
847         Job *j;
848         JobBusClient *cl;
849         DBusError error;
850         int r;
851
852         assert(connection);
853         assert(message);
854         assert(u);
855         assert(type >= 0 && type < _JOB_TYPE_MAX);
856         assert(mode >= 0 && mode < _JOB_MODE_MAX);
857
858         dbus_error_init(&error);
859
860         if (reload_if_possible && unit_can_reload(u)) {
861                 if (type == JOB_RESTART)
862                         type = JOB_RELOAD_OR_START;
863                 else if (type == JOB_TRY_RESTART)
864                         type = JOB_RELOAD;
865         }
866
867         SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
868                                   (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
869                                   type == JOB_STOP ? "stop" : "reload");
870
871         if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
872                 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
873                 return bus_send_error_reply(connection, message, &error, -EPERM);
874         }
875
876         if ((type == JOB_START && u->refuse_manual_start) ||
877             (type == JOB_STOP && u->refuse_manual_stop) ||
878             ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
879                 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
880                                "Operation refused, unit %s may be requested by dependency only.", u->id);
881                 return bus_send_error_reply(connection, message, &error, -EPERM);
882         }
883
884         r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
885         if (r < 0)
886                 return bus_send_error_reply(connection, message, &error, r);
887
888         cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
889         if (!cl)
890                 goto oom;
891
892         LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
893
894         reply = dbus_message_new_method_return(message);
895         if (!reply)
896                 goto oom;
897
898         path = job_dbus_path(j);
899         if (!path)
900                 goto oom;
901
902         if (!dbus_message_append_args(
903                             reply,
904                             DBUS_TYPE_OBJECT_PATH, &path,
905                             DBUS_TYPE_INVALID))
906                 goto oom;
907
908         if (!bus_maybe_send_reply(connection, message, reply))
909                 goto oom;
910
911         return DBUS_HANDLER_RESULT_HANDLED;
912
913 oom:
914         dbus_error_free(&error);
915
916         return DBUS_HANDLER_RESULT_NEED_MEMORY;
917 }
918
919 static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
920         const char *mode;
921         int r;
922
923         assert(iter);
924         assert(runtime);
925
926         r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
927         if (r < 0)
928                 return r;
929
930         if (streq(mode, "runtime"))
931                 *runtime = true;
932         else if (streq(mode, "persistent"))
933                 *runtime = false;
934         else
935                 return -EINVAL;
936
937         return 0;
938 }
939
940 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
941         _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
942         const char *name;
943         CGroupBonding *b;
944         bool runtime;
945         int r;
946
947         assert(u);
948         assert(iter);
949
950         if (!unit_get_exec_context(u))
951                 return -EINVAL;
952
953         r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
954         if (r < 0)
955                 return r;
956
957         r = parse_mode(iter, &runtime, false);
958         if (r < 0)
959                 return r;
960
961         r = cg_split_spec(name, &controller, &new_path);
962         if (r < 0)
963                 return r;
964
965         if (!new_path) {
966                 new_path = unit_default_cgroup_path(u);
967                 if (!new_path)
968                         return -ENOMEM;
969         }
970
971         if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
972                 return -EINVAL;
973
974         b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
975         if (b) {
976                 if (streq(b->path, new_path))
977                         return 0;
978
979                 if (b->essential)
980                         return -EINVAL;
981
982                 old_path = strdup(b->path);
983                 if (!old_path)
984                         return -ENOMEM;
985         }
986
987         r = unit_add_cgroup_from_text(u, name, true, &b);
988         if (r < 0)
989                 return r;
990         if (r > 0) {
991                 CGroupAttribute *a;
992
993                 /* Try to move things to the new place, and clean up the old place */
994                 cgroup_bonding_realize(b);
995                 cgroup_bonding_migrate(b, u->cgroup_bondings);
996
997                 if (old_path)
998                         cg_trim(controller, old_path, true);
999
1000                 /* Apply the attributes to the new group */
1001                 LIST_FOREACH(by_unit, a, u->cgroup_attributes)
1002                         if (streq(a->controller, controller))
1003                                 cgroup_attribute_apply(a, b);
1004         }
1005
1006         contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1007                            "ControlGroup=", name, "\n", NULL);
1008         if (!contents)
1009                 return -ENOMEM;
1010
1011         return unit_write_drop_in(u, runtime, controller, contents);
1012 }
1013
1014 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
1015         _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
1016         const char *name;
1017         CGroupAttribute *a, *n;
1018         CGroupBonding *b;
1019         bool runtime;
1020         int r;
1021
1022         assert(u);
1023         assert(iter);
1024
1025         if (!unit_get_exec_context(u))
1026                 return -EINVAL;
1027
1028         r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1029         if (r < 0)
1030                 return r;
1031
1032         r = parse_mode(iter, &runtime, false);
1033         if (r < 0)
1034                 return r;
1035
1036         r = cg_split_spec(name, &controller, &path);
1037         if (r < 0)
1038                 return r;
1039
1040         if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1041                 return -EINVAL;
1042
1043         b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1044         if (!b)
1045                 return -ENOENT;
1046
1047         if (path && !path_equal(path, b->path))
1048                 return -ENOENT;
1049
1050         if (b->essential)
1051                 return -EINVAL;
1052
1053         unit_remove_drop_in(u, runtime, controller);
1054
1055         /* Try to migrate the old group away */
1056         if (cg_pid_get_path(controller, 0, &target) >= 0)
1057                 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1058
1059         cgroup_bonding_free(b, true);
1060
1061         /* Drop all attributes of this controller */
1062         LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
1063                 if (!streq(a->controller, controller))
1064                         continue;
1065
1066                 unit_remove_drop_in(u, runtime, a->name);
1067                 cgroup_attribute_free(a);
1068         }
1069
1070         return 0;
1071 }
1072
1073 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1074         _cleanup_free_ char *controller = NULL;
1075         CGroupAttribute *a;
1076         CGroupBonding *b;
1077         const char *name;
1078         char **l = NULL;
1079         int r;
1080
1081         assert(u);
1082         assert(iter);
1083         assert(_result);
1084
1085         if (!unit_get_exec_context(u))
1086                 return -EINVAL;
1087
1088         r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
1089         if (r < 0)
1090                 return r;
1091
1092         r = cg_controller_from_attr(name, &controller);
1093         if (r < 0)
1094                 return r;
1095
1096         /* First attempt, read the value from the kernel */
1097         b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1098         if (b) {
1099                 _cleanup_free_ char *p = NULL, *v = NULL;
1100
1101                 r = cg_get_path(b->controller, b->path, name, &p);
1102                 if (r < 0)
1103                         return r;
1104
1105                 r = read_full_file(p, &v, NULL);
1106                 if (r >= 0) {
1107                         /* Split on new lines */
1108                         l = strv_split_newlines(v);
1109                         if (!l)
1110                                 return -ENOMEM;
1111
1112                         *_result = l;
1113                         return 0;
1114
1115                 }
1116         }
1117
1118         /* If that didn't work, read our cached value */
1119         LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1120
1121                 if (!cgroup_attribute_matches(a, controller, name))
1122                         continue;
1123
1124                 r = strv_extend(&l, a->value);
1125                 if (r < 0) {
1126                         strv_free(l);
1127                         return r;
1128                 }
1129         }
1130
1131         if (!l)
1132                 return -ENOENT;
1133
1134         *_result = l;
1135         return 0;
1136 }
1137
1138 static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
1139         _cleanup_free_ char *buf = NULL;
1140         CGroupAttribute *a;
1141
1142         assert(u);
1143         assert(name);
1144
1145         LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1146                 if (!cgroup_attribute_matches(a, NULL, name))
1147                         continue;
1148
1149                 if (!buf) {
1150                         buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1151                                       "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1152
1153                         if (!buf)
1154                                 return -ENOMEM;
1155                 } else {
1156                         char *b;
1157
1158                         b = strjoin(buf,
1159                                     "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1160
1161                         if (!b)
1162                                 return -ENOMEM;
1163
1164                         free(buf);
1165                         buf = b;
1166                 }
1167         }
1168
1169         if (buf)
1170                 return unit_write_drop_in(u, runtime, name, buf);
1171         else
1172                 return unit_remove_drop_in(u, runtime, name);
1173 }
1174
1175 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1176         _cleanup_strv_free_ char **l = NULL;
1177         int r;
1178         bool runtime = false;
1179         char **value;
1180         const char *name;
1181
1182         assert(u);
1183         assert(iter);
1184
1185         if (!unit_get_exec_context(u))
1186                 return -EINVAL;
1187
1188         r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1189         if (r < 0)
1190                 return r;
1191
1192         r = bus_parse_strv_iter(iter, &l);
1193         if (r < 0)
1194                 return r;
1195
1196         if (!dbus_message_iter_next(iter))
1197                 return -EINVAL;
1198
1199         r = parse_mode(iter, &runtime, false);
1200         if (r < 0)
1201                 return r;
1202
1203         STRV_FOREACH(value, l) {
1204                 _cleanup_free_ char *v = NULL;
1205                 CGroupAttribute *a;
1206                 const CGroupSemantics *s;
1207
1208                 r = cgroup_semantics_find(NULL, name, *value, &v, &s);
1209                 if (r < 0)
1210                         return r;
1211
1212                 if (s && !s->multiple && l[1])
1213                         return -EINVAL;
1214
1215                 r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
1216                 if (r < 0)
1217                         return r;
1218
1219                 if (r > 0) {
1220                         CGroupBonding *b;
1221
1222                         b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1223                         if (!b) {
1224                                 /* Doesn't exist yet? Then let's add it */
1225                                 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1226                                 if (r < 0)
1227                                         return r;
1228
1229                                 if (r > 0) {
1230                                         cgroup_bonding_realize(b);
1231                                         cgroup_bonding_migrate(b, u->cgroup_bondings);
1232                                 }
1233                         }
1234
1235                         /* Make it count */
1236                         cgroup_attribute_apply(a, u->cgroup_bondings);
1237                 }
1238
1239         }
1240
1241         r = update_attribute_drop_in(u, runtime, name);
1242         if (r < 0)
1243                 return r;
1244
1245         return 0;
1246 }
1247
1248 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1249         const char *name;
1250         bool runtime;
1251         int r;
1252
1253         assert(u);
1254         assert(iter);
1255
1256         if (!unit_get_exec_context(u))
1257                 return -EINVAL;
1258
1259         r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1260         if (r < 0)
1261                 return r;
1262
1263         r = parse_mode(iter, &runtime, false);
1264         if (r < 0)
1265                 return r;
1266
1267         cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
1268         update_attribute_drop_in(u, runtime, name);
1269
1270         return 0;
1271 }
1272
1273 const BusProperty bus_unit_properties[] = {
1274         { "Id",                   bus_property_append_string,         "s", offsetof(Unit, id),                                         true },
1275         { "Names",                bus_unit_append_names,             "as", 0 },
1276         { "Following",            bus_unit_append_following,          "s", 0 },
1277         { "Slice",                bus_unit_append_slice,              "s", 0 },
1278         { "Requires",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES]),                true },
1279         { "RequiresOverridable",  bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]),    true },
1280         { "Requisite",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE]),               true },
1281         { "RequisiteOverridable", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]),   true },
1282         { "Wants",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTS]),                   true },
1283         { "BindsTo",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]),                true },
1284         { "PartOf",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PART_OF]),                 true },
1285         { "RequiredBy",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]),             true },
1286         { "RequiredByOverridable",bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1287         { "WantedBy",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]),               true },
1288         { "BoundBy",              bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]),                true },
1289         { "ConsistsOf",           bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]),             true },
1290         { "Conflicts",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]),               true },
1291         { "ConflictedBy",         bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]),           true },
1292         { "Before",               bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_BEFORE]),                  true },
1293         { "After",                bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_AFTER]),                   true },
1294         { "OnFailure",            bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]),              true },
1295         { "Triggers",             bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]),                true },
1296         { "TriggeredBy",          bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]),            true },
1297         { "PropagatesReloadTo",   bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]),    true },
1298         { "ReloadPropagatedFrom", bus_unit_append_dependencies,      "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]),  true },
1299         { "RequiresMountsFor",    bus_property_append_strv,          "as", offsetof(Unit, requires_mounts_for),                        true },
1300         { "Documentation",        bus_property_append_strv,          "as", offsetof(Unit, documentation),                              true },
1301         { "Description",          bus_unit_append_description,        "s", 0 },
1302         { "LoadState",            bus_unit_append_load_state,         "s", offsetof(Unit, load_state)                         },
1303         { "ActiveState",          bus_unit_append_active_state,       "s", 0 },
1304         { "SubState",             bus_unit_append_sub_state,          "s", 0 },
1305         { "FragmentPath",         bus_property_append_string,         "s", offsetof(Unit, fragment_path),                              true },
1306         { "SourcePath",           bus_property_append_string,         "s", offsetof(Unit, source_path),                                true },
1307         { "DropInPaths",          bus_property_append_strv,          "as", offsetof(Unit, dropin_paths),                               true },
1308         { "UnitFileState",        bus_unit_append_file_state,         "s", 0 },
1309         { "InactiveExitTimestamp",bus_property_append_usec,           "t", offsetof(Unit, inactive_exit_timestamp.realtime)   },
1310         { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic)  },
1311         { "ActiveEnterTimestamp", bus_property_append_usec,           "t", offsetof(Unit, active_enter_timestamp.realtime)    },
1312         { "ActiveEnterTimestampMonotonic", bus_property_append_usec,  "t", offsetof(Unit, active_enter_timestamp.monotonic)   },
1313         { "ActiveExitTimestamp",  bus_property_append_usec,           "t", offsetof(Unit, active_exit_timestamp.realtime)     },
1314         { "ActiveExitTimestampMonotonic",  bus_property_append_usec,  "t", offsetof(Unit, active_exit_timestamp.monotonic)    },
1315         { "InactiveEnterTimestamp", bus_property_append_usec,         "t", offsetof(Unit, inactive_enter_timestamp.realtime)  },
1316         { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1317         { "CanStart",             bus_unit_append_can_start,          "b", 0 },
1318         { "CanStop",              bus_unit_append_can_stop,           "b", 0 },
1319         { "CanReload",            bus_unit_append_can_reload,         "b", 0 },
1320         { "CanIsolate",           bus_unit_append_can_isolate,        "b", 0 },
1321         { "Job",                  bus_unit_append_job,             "(uo)", 0 },
1322         { "StopWhenUnneeded",     bus_property_append_bool,           "b", offsetof(Unit, stop_when_unneeded)                 },
1323         { "RefuseManualStart",    bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_start)                },
1324         { "RefuseManualStop",     bus_property_append_bool,           "b", offsetof(Unit, refuse_manual_stop)                 },
1325         { "AllowIsolate",         bus_property_append_bool,           "b", offsetof(Unit, allow_isolate)                      },
1326         { "DefaultDependencies",  bus_property_append_bool,           "b", offsetof(Unit, default_dependencies)               },
1327         { "OnFailureIsolate",     bus_property_append_bool,           "b", offsetof(Unit, on_failure_isolate)                 },
1328         { "IgnoreOnIsolate",      bus_property_append_bool,           "b", offsetof(Unit, ignore_on_isolate)                  },
1329         { "IgnoreOnSnapshot",     bus_property_append_bool,           "b", offsetof(Unit, ignore_on_snapshot)                 },
1330         { "NeedDaemonReload",     bus_unit_append_need_daemon_reload, "b", 0 },
1331         { "JobTimeoutUSec",       bus_property_append_usec,           "t", offsetof(Unit, job_timeout)                        },
1332         { "ConditionTimestamp",   bus_property_append_usec,           "t", offsetof(Unit, condition_timestamp.realtime)       },
1333         { "ConditionTimestampMonotonic", bus_property_append_usec,    "t", offsetof(Unit, condition_timestamp.monotonic)      },
1334         { "ConditionResult",      bus_property_append_bool,           "b", offsetof(Unit, condition_result)                   },
1335         { "LoadError",            bus_unit_append_load_error,      "(ss)", 0 },
1336         { NULL, }
1337 };
1338
1339 const BusProperty bus_unit_cgroup_properties[] = {
1340         { "DefaultControlGroup",    bus_unit_append_default_cgroup,     "s", 0 },
1341         { "ControlGroups",          bus_unit_append_cgroups,           "as", 0 },
1342         { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,  "a(sss)", 0 },
1343         { NULL, }
1344 };