]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dbus-unit.c
core: general cgroup rework
[thirdparty/systemd.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_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
299 Unit *u = data;
300 dbus_bool_t b;
301
302 assert(i);
303 assert(property);
304 assert(u);
305
306 b = unit_need_daemon_reload(u);
307
308 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
309 return -ENOMEM;
310
311 return 0;
312 }
313
314 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
315 Unit *u = data;
316 const char *name, *message;
317 DBusMessageIter sub;
318
319 assert(i);
320 assert(property);
321 assert(u);
322
323 if (u->load_error != 0) {
324 name = bus_errno_to_dbus(u->load_error);
325 message = strempty(strerror(-u->load_error));
326 } else
327 name = message = "";
328
329 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
330 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
331 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
332 !dbus_message_iter_close_container(i, &sub))
333 return -ENOMEM;
334
335 return 0;
336 }
337
338 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
339 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
340 DBusError error;
341 JobType job_type = _JOB_TYPE_INVALID;
342 bool reload_if_possible = false;
343 int r;
344
345 dbus_error_init(&error);
346
347 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
348 job_type = JOB_START;
349 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
350 job_type = JOB_STOP;
351 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
352 job_type = JOB_RELOAD;
353 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
354 job_type = JOB_RESTART;
355 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
356 job_type = JOB_TRY_RESTART;
357 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
358 reload_if_possible = true;
359 job_type = JOB_RESTART;
360 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
361 reload_if_possible = true;
362 job_type = JOB_TRY_RESTART;
363 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
364 const char *swho;
365 int32_t signo;
366 KillWho who;
367
368 if (!dbus_message_get_args(
369 message,
370 &error,
371 DBUS_TYPE_STRING, &swho,
372 DBUS_TYPE_INT32, &signo,
373 DBUS_TYPE_INVALID))
374 return bus_send_error_reply(connection, message, &error, -EINVAL);
375
376 if (isempty(swho))
377 who = KILL_ALL;
378 else {
379 who = kill_who_from_string(swho);
380 if (who < 0)
381 return bus_send_error_reply(connection, message, &error, -EINVAL);
382 }
383
384 if (signo <= 0 || signo >= _NSIG)
385 return bus_send_error_reply(connection, message, &error, -EINVAL);
386
387 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
388
389 r = unit_kill(u, who, signo, &error);
390 if (r < 0)
391 return bus_send_error_reply(connection, message, &error, r);
392
393 reply = dbus_message_new_method_return(message);
394 if (!reply)
395 goto oom;
396
397 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
398
399 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
400
401 unit_reset_failed(u);
402
403 reply = dbus_message_new_method_return(message);
404 if (!reply)
405 goto oom;
406
407 } else if (UNIT_VTABLE(u)->bus_message_handler)
408 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
409 else
410 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
411
412 if (job_type != _JOB_TYPE_INVALID) {
413 const char *smode;
414 JobMode mode;
415
416 if (!dbus_message_get_args(
417 message,
418 &error,
419 DBUS_TYPE_STRING, &smode,
420 DBUS_TYPE_INVALID))
421 return bus_send_error_reply(connection, message, &error, -EINVAL);
422
423 mode = job_mode_from_string(smode);
424 if (mode < 0) {
425 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
426 return bus_send_error_reply(connection, message, &error, -EINVAL);
427 }
428
429 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
430 }
431
432 if (reply)
433 if (!bus_maybe_send_reply(connection, message, reply))
434 goto oom;
435
436 return DBUS_HANDLER_RESULT_HANDLED;
437
438 oom:
439 dbus_error_free(&error);
440 return DBUS_HANDLER_RESULT_NEED_MEMORY;
441 }
442
443 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
444 Manager *m = data;
445 Unit *u;
446 int r;
447 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
448 DBusError error;
449
450 assert(connection);
451 assert(message);
452 assert(m);
453
454 dbus_error_init(&error);
455
456 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
457 /* Be nice to gdbus and return introspection data for our mid-level paths */
458
459 SELINUX_ACCESS_CHECK(connection, message, "status");
460
461 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
462 char *introspection = NULL;
463 FILE *f;
464 Iterator i;
465 const char *k;
466 size_t size;
467
468 reply = dbus_message_new_method_return(message);
469 if (!reply)
470 goto oom;
471
472 /* We roll our own introspection code here, instead of
473 * relying on bus_default_message_handler() because we
474 * need to generate our introspection string
475 * dynamically. */
476
477 f = open_memstream(&introspection, &size);
478 if (!f)
479 goto oom;
480
481 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
482 "<node>\n", f);
483
484 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
485 fputs(BUS_PEER_INTERFACE, f);
486
487 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
488 char *p;
489
490 if (k != u->id)
491 continue;
492
493 p = bus_path_escape(k);
494 if (!p) {
495 fclose(f);
496 free(introspection);
497 goto oom;
498 }
499
500 fprintf(f, "<node name=\"%s\"/>", p);
501 free(p);
502 }
503
504 fputs("</node>\n", f);
505
506 if (ferror(f)) {
507 fclose(f);
508 free(introspection);
509 goto oom;
510 }
511
512 fclose(f);
513
514 if (!introspection)
515 goto oom;
516
517 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
518 free(introspection);
519 goto oom;
520 }
521
522 free(introspection);
523
524 if (!bus_maybe_send_reply(connection, message, reply))
525 goto oom;
526
527 return DBUS_HANDLER_RESULT_HANDLED;
528 }
529
530 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
531 }
532
533 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
534 if (r == -ENOMEM)
535 goto oom;
536 if (r < 0)
537 return bus_send_error_reply(connection, message, &error, r);
538
539 return bus_unit_message_dispatch(u, connection, message);
540
541 oom:
542 dbus_error_free(&error);
543
544 return DBUS_HANDLER_RESULT_NEED_MEMORY;
545 }
546
547 const DBusObjectPathVTable bus_unit_vtable = {
548 .message_function = bus_unit_message_handler
549 };
550
551 void bus_unit_send_change_signal(Unit *u) {
552 _cleanup_free_ char *p = NULL;
553 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
554
555 assert(u);
556
557 if (u->in_dbus_queue) {
558 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
559 u->in_dbus_queue = false;
560 }
561
562 if (!u->id)
563 return;
564
565 if (!bus_has_subscriber(u->manager)) {
566 u->sent_dbus_new_signal = true;
567 return;
568 }
569
570 p = unit_dbus_path(u);
571 if (!p)
572 goto oom;
573
574 if (u->sent_dbus_new_signal) {
575 /* Send a properties changed signal. First for the
576 * specific type, then for the generic unit. The
577 * clients may rely on this order to get atomic
578 * behavior if needed. */
579
580 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
581
582 m = bus_properties_changed_new(p,
583 UNIT_VTABLE(u)->bus_interface,
584 UNIT_VTABLE(u)->bus_invalidating_properties);
585 if (!m)
586 goto oom;
587
588 if (bus_broadcast(u->manager, m) < 0)
589 goto oom;
590
591 dbus_message_unref(m);
592 }
593
594 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
595 INVALIDATING_PROPERTIES);
596 if (!m)
597 goto oom;
598
599 } else {
600 /* Send a new signal */
601
602 m = dbus_message_new_signal("/org/freedesktop/systemd1",
603 "org.freedesktop.systemd1.Manager",
604 "UnitNew");
605 if (!m)
606 goto oom;
607
608 if (!dbus_message_append_args(m,
609 DBUS_TYPE_STRING, &u->id,
610 DBUS_TYPE_OBJECT_PATH, &p,
611 DBUS_TYPE_INVALID))
612 goto oom;
613 }
614
615 if (bus_broadcast(u->manager, m) < 0)
616 goto oom;
617
618 u->sent_dbus_new_signal = true;
619
620 return;
621
622 oom:
623 log_oom();
624 }
625
626 void bus_unit_send_removed_signal(Unit *u) {
627 _cleanup_free_ char *p = NULL;
628 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
629
630 assert(u);
631
632 if (!bus_has_subscriber(u->manager))
633 return;
634
635 if (!u->sent_dbus_new_signal)
636 bus_unit_send_change_signal(u);
637
638 if (!u->id)
639 return;
640
641 p = unit_dbus_path(u);
642 if (!p)
643 goto oom;
644
645 m = dbus_message_new_signal("/org/freedesktop/systemd1",
646 "org.freedesktop.systemd1.Manager",
647 "UnitRemoved");
648 if (!m)
649 goto oom;
650
651 if (!dbus_message_append_args(m,
652 DBUS_TYPE_STRING, &u->id,
653 DBUS_TYPE_OBJECT_PATH, &p,
654 DBUS_TYPE_INVALID))
655 goto oom;
656
657 if (bus_broadcast(u->manager, m) < 0)
658 goto oom;
659
660 return;
661
662 oom:
663 log_oom();
664 }
665
666 DBusHandlerResult bus_unit_queue_job(
667 DBusConnection *connection,
668 DBusMessage *message,
669 Unit *u,
670 JobType type,
671 JobMode mode,
672 bool reload_if_possible) {
673
674 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
675 _cleanup_free_ char *path = NULL;
676 Job *j;
677 JobBusClient *cl;
678 DBusError error;
679 int r;
680
681 assert(connection);
682 assert(message);
683 assert(u);
684 assert(type >= 0 && type < _JOB_TYPE_MAX);
685 assert(mode >= 0 && mode < _JOB_MODE_MAX);
686
687 dbus_error_init(&error);
688
689 if (reload_if_possible && unit_can_reload(u)) {
690 if (type == JOB_RESTART)
691 type = JOB_RELOAD_OR_START;
692 else if (type == JOB_TRY_RESTART)
693 type = JOB_RELOAD;
694 }
695
696 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
697 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
698 type == JOB_STOP ? "stop" : "reload");
699
700 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
701 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
702 return bus_send_error_reply(connection, message, &error, -EPERM);
703 }
704
705 if ((type == JOB_START && u->refuse_manual_start) ||
706 (type == JOB_STOP && u->refuse_manual_stop) ||
707 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
708 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
709 "Operation refused, unit %s may be requested by dependency only.", u->id);
710 return bus_send_error_reply(connection, message, &error, -EPERM);
711 }
712
713 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
714 if (r < 0)
715 return bus_send_error_reply(connection, message, &error, r);
716
717 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
718 if (!cl)
719 goto oom;
720
721 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
722
723 reply = dbus_message_new_method_return(message);
724 if (!reply)
725 goto oom;
726
727 path = job_dbus_path(j);
728 if (!path)
729 goto oom;
730
731 if (!dbus_message_append_args(
732 reply,
733 DBUS_TYPE_OBJECT_PATH, &path,
734 DBUS_TYPE_INVALID))
735 goto oom;
736
737 if (!bus_maybe_send_reply(connection, message, reply))
738 goto oom;
739
740 return DBUS_HANDLER_RESULT_HANDLED;
741
742 oom:
743 dbus_error_free(&error);
744
745 return DBUS_HANDLER_RESULT_NEED_MEMORY;
746 }
747
748 const BusProperty bus_unit_properties[] = {
749 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
750 { "Names", bus_unit_append_names, "as", 0 },
751 { "Following", bus_unit_append_following, "s", 0 },
752 { "Slice", bus_unit_append_slice, "s", 0 },
753 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
754 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
755 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
756 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
757 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
758 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
759 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
760 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
761 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
762 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
763 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
764 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
765 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
766 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
767 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
768 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
769 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
770 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
771 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
772 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
773 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
774 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
775 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
776 { "Description", bus_unit_append_description, "s", 0 },
777 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
778 { "ActiveState", bus_unit_append_active_state, "s", 0 },
779 { "SubState", bus_unit_append_sub_state, "s", 0 },
780 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
781 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
782 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
783 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
784 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
785 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
786 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
787 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
788 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
789 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
790 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
791 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
792 { "CanStart", bus_unit_append_can_start, "b", 0 },
793 { "CanStop", bus_unit_append_can_stop, "b", 0 },
794 { "CanReload", bus_unit_append_can_reload, "b", 0 },
795 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
796 { "Job", bus_unit_append_job, "(uo)", 0 },
797 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
798 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
799 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
800 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
801 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
802 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
803 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
804 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
805 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
806 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
807 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
808 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
809 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
810 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
811 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },
812 { NULL, }
813 };