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