1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
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"
32 #include "path-util.h"
35 const char bus_unit_interface[] = BUS_UNIT_INTERFACE;
37 #define INVALIDATING_PROPERTIES \
41 "InactiveExitTimestamp\0" \
42 "ActiveEnterTimestamp\0" \
43 "ActiveExitTimestamp\0" \
44 "InactiveEnterTimestamp\0" \
48 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
54 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
57 SET_FOREACH(t, u->names, j)
58 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
61 if (!dbus_message_iter_close_container(i, &sub))
67 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
75 f = unit_following(u);
78 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
84 static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void *data) {
92 d = strempty(unit_slice_name(u));
94 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
100 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
106 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
110 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
113 if (!dbus_message_iter_close_container(i, &sub))
119 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
127 d = unit_description(u);
129 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
135 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
137 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
145 state = unit_active_state_to_string(unit_active_state(u));
147 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
153 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
161 state = unit_sub_state_to_string(u);
163 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
169 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
177 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
179 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
185 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
193 b = unit_can_start(u) &&
194 !u->refuse_manual_start;
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
202 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
210 /* On the lower levels we assume that every unit we can start
211 * we can also stop */
213 b = unit_can_start(u) &&
214 !u->refuse_manual_stop;
216 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
222 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
230 b = unit_can_reload(u);
232 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
238 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
246 b = unit_can_isolate(u) &&
247 !u->refuse_manual_start;
249 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
255 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
258 _cleanup_free_ char *p = NULL;
264 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
269 p = job_dbus_path(u->job);
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))
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. */
283 p = unit_dbus_path(u);
287 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
288 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
292 if (!dbus_message_iter_close_container(i, &sub))
298 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
306 b = unit_need_daemon_reload(u);
308 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
314 static int bus_property_append_condition(DBusMessageIter *i, const char *property, void *data) {
315 Condition **cp = data;
317 const char *name, *param;
318 dbus_bool_t trigger, negate;
329 name = condition_type_to_string(c->type);
330 param = c->parameter;
331 trigger = c->trigger;
335 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
336 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
337 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &trigger) ||
338 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &negate) ||
339 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, ¶m) ||
340 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &state) ||
341 !dbus_message_iter_close_container(i, &sub))
347 static int bus_property_append_condition_list(DBusMessageIter *i, const char *property, void *data) {
348 Condition **first = data, *c;
354 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sbbsi)", &sub))
357 LIST_FOREACH(conditions, c, *first)
358 bus_property_append_condition(&sub, property, &c);
360 if (!dbus_message_iter_close_container(i, &sub))
366 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
368 const char *name, *message;
375 if (u->load_error != 0) {
376 name = bus_errno_to_dbus(u->load_error);
377 message = strempty(strerror(-u->load_error));
381 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
382 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
383 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
384 !dbus_message_iter_close_container(i, &sub))
390 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
391 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
393 JobType job_type = _JOB_TYPE_INVALID;
394 bool reload_if_possible = false;
397 dbus_error_init(&error);
399 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
400 job_type = JOB_START;
401 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
403 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
404 job_type = JOB_RELOAD;
405 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
406 job_type = JOB_RESTART;
407 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
408 job_type = JOB_TRY_RESTART;
409 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
410 reload_if_possible = true;
411 job_type = JOB_RESTART;
412 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
413 reload_if_possible = true;
414 job_type = JOB_TRY_RESTART;
415 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
420 if (!dbus_message_get_args(
423 DBUS_TYPE_STRING, &swho,
424 DBUS_TYPE_INT32, &signo,
426 return bus_send_error_reply(connection, message, &error, -EINVAL);
431 who = kill_who_from_string(swho);
433 return bus_send_error_reply(connection, message, &error, -EINVAL);
436 if (signo <= 0 || signo >= _NSIG)
437 return bus_send_error_reply(connection, message, &error, -EINVAL);
439 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
441 r = unit_kill(u, who, signo, &error);
443 return bus_send_error_reply(connection, message, &error, r);
445 reply = dbus_message_new_method_return(message);
449 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
451 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
453 unit_reset_failed(u);
455 reply = dbus_message_new_method_return(message);
458 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
459 DBusMessageIter iter;
462 if (!dbus_message_iter_init(message, &iter))
465 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
466 return bus_send_error_reply(connection, message, NULL, -EINVAL);
468 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
470 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
472 return bus_send_error_reply(connection, message, &error, r);
474 reply = dbus_message_new_method_return(message);
478 } else if (UNIT_VTABLE(u)->bus_message_handler)
479 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
481 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
483 if (job_type != _JOB_TYPE_INVALID) {
487 if (!dbus_message_get_args(
490 DBUS_TYPE_STRING, &smode,
492 return bus_send_error_reply(connection, message, &error, -EINVAL);
494 mode = job_mode_from_string(smode);
496 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
497 return bus_send_error_reply(connection, message, &error, -EINVAL);
500 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
504 if (!bus_maybe_send_reply(connection, message, reply))
507 return DBUS_HANDLER_RESULT_HANDLED;
510 dbus_error_free(&error);
511 return DBUS_HANDLER_RESULT_NEED_MEMORY;
514 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
518 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
525 dbus_error_init(&error);
527 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
528 /* Be nice to gdbus and return introspection data for our mid-level paths */
530 SELINUX_ACCESS_CHECK(connection, message, "status");
532 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
533 char *introspection = NULL;
539 reply = dbus_message_new_method_return(message);
543 /* We roll our own introspection code here, instead of
544 * relying on bus_default_message_handler() because we
545 * need to generate our introspection string
548 f = open_memstream(&introspection, &size);
552 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
555 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
556 fputs(BUS_PEER_INTERFACE, f);
558 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
564 p = bus_path_escape(k);
571 fprintf(f, "<node name=\"%s\"/>", p);
575 fputs("</node>\n", f);
588 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
595 if (!bus_maybe_send_reply(connection, message, reply))
598 return DBUS_HANDLER_RESULT_HANDLED;
601 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
604 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
608 return bus_send_error_reply(connection, message, &error, r);
610 return bus_unit_message_dispatch(u, connection, message);
613 dbus_error_free(&error);
615 return DBUS_HANDLER_RESULT_NEED_MEMORY;
618 const DBusObjectPathVTable bus_unit_vtable = {
619 .message_function = bus_unit_message_handler
622 void bus_unit_send_change_signal(Unit *u) {
623 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
624 _cleanup_free_ char *p = NULL;
629 if (u->in_dbus_queue) {
630 LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
631 u->in_dbus_queue = false;
637 if (!bus_has_subscriber(u->manager)) {
638 u->sent_dbus_new_signal = true;
642 p = unit_dbus_path(u);
648 if (u->sent_dbus_new_signal) {
649 /* Send a properties changed signal. First for the
650 * specific type, then for the generic unit. The
651 * clients may rely on this order to get atomic
652 * behavior if needed. */
654 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
656 m = bus_properties_changed_new(p,
657 UNIT_VTABLE(u)->bus_interface,
658 UNIT_VTABLE(u)->bus_invalidating_properties);
664 r = bus_broadcast(u->manager, m);
666 log_error("Failed to broadcast change message: %s", strerror(-r));
670 dbus_message_unref(m);
673 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
674 INVALIDATING_PROPERTIES);
681 /* Send a new signal */
683 m = dbus_message_new_signal("/org/freedesktop/systemd1",
684 "org.freedesktop.systemd1.Manager",
691 if (!dbus_message_append_args(m,
692 DBUS_TYPE_STRING, &u->id,
693 DBUS_TYPE_OBJECT_PATH, &p,
694 DBUS_TYPE_INVALID)) {
700 r = bus_broadcast(u->manager, m);
702 log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
706 u->sent_dbus_new_signal = true;
709 void bus_unit_send_removed_signal(Unit *u) {
710 _cleanup_free_ char *p = NULL;
711 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
715 if (!bus_has_subscriber(u->manager))
718 if (!u->sent_dbus_new_signal)
719 bus_unit_send_change_signal(u);
724 p = unit_dbus_path(u);
728 m = dbus_message_new_signal("/org/freedesktop/systemd1",
729 "org.freedesktop.systemd1.Manager",
734 if (!dbus_message_append_args(m,
735 DBUS_TYPE_STRING, &u->id,
736 DBUS_TYPE_OBJECT_PATH, &p,
740 if (bus_broadcast(u->manager, m) < 0)
749 DBusHandlerResult bus_unit_queue_job(
750 DBusConnection *connection,
751 DBusMessage *message,
755 bool reload_if_possible) {
757 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
758 _cleanup_free_ char *path = NULL;
767 assert(type >= 0 && type < _JOB_TYPE_MAX);
768 assert(mode >= 0 && mode < _JOB_MODE_MAX);
770 dbus_error_init(&error);
772 if (reload_if_possible && unit_can_reload(u)) {
773 if (type == JOB_RESTART)
774 type = JOB_RELOAD_OR_START;
775 else if (type == JOB_TRY_RESTART)
779 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
780 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
781 type == JOB_STOP ? "stop" : "reload");
783 if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
784 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
785 return bus_send_error_reply(connection, message, &error, -EPERM);
788 if ((type == JOB_START && u->refuse_manual_start) ||
789 (type == JOB_STOP && u->refuse_manual_stop) ||
790 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
791 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
792 "Operation refused, unit %s may be requested by dependency only.", u->id);
793 return bus_send_error_reply(connection, message, &error, -EPERM);
796 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
798 return bus_send_error_reply(connection, message, &error, r);
800 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
804 LIST_PREPEND(client, j->bus_client_list, cl);
806 reply = dbus_message_new_method_return(message);
810 path = job_dbus_path(j);
814 if (!dbus_message_append_args(
816 DBUS_TYPE_OBJECT_PATH, &path,
820 if (!bus_maybe_send_reply(connection, message, reply))
823 return DBUS_HANDLER_RESULT_HANDLED;
826 dbus_error_free(&error);
828 return DBUS_HANDLER_RESULT_NEED_MEMORY;
831 static int bus_unit_set_transient_property(
835 UnitSetPropertiesMode mode,
844 if (streq(name, "Description")) {
845 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
848 if (mode != UNIT_CHECK) {
849 const char *description;
851 dbus_message_iter_get_basic(i, &description);
853 r = unit_set_description(u, description);
857 unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", description);
862 } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
865 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
868 dbus_message_iter_get_basic(i, &s);
871 if (mode != UNIT_CHECK) {
872 unit_ref_unset(&u->slice);
873 unit_remove_drop_in(u, mode, name);
878 r = manager_load_unit(u->manager, s, NULL, error, &slice);
882 if (slice->type != UNIT_SLICE)
885 if (mode != UNIT_CHECK) {
886 unit_ref_set(&u->slice, slice);
887 unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
893 } else if (streq(name, "Requires") ||
894 streq(name, "RequiresOverridable") ||
895 streq(name, "Requisite") ||
896 streq(name, "RequisiteOverridable") ||
897 streq(name, "Wants") ||
898 streq(name, "BindsTo") ||
899 streq(name, "Conflicts") ||
900 streq(name, "Before") ||
901 streq(name, "After") ||
902 streq(name, "OnFailure") ||
903 streq(name, "PropagatesReloadTo") ||
904 streq(name, "ReloadPropagatedFrom") ||
905 streq(name, "PartOf")) {
910 d = unit_dependency_from_string(name);
914 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
915 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRING)
918 dbus_message_iter_recurse(i, &sub);
919 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
922 dbus_message_iter_get_basic(&sub, &other);
924 if (!unit_name_is_valid(other, false))
927 if (mode != UNIT_CHECK) {
928 _cleanup_free_ char *label = NULL;
930 r = unit_add_dependency_by_name(u, d, other, NULL, true);
934 label = strjoin(name, "-", other, NULL);
938 unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
941 dbus_message_iter_next(&sub);
950 int bus_unit_set_properties(
952 DBusMessageIter *iter,
953 UnitSetPropertiesMode mode,
957 bool for_real = false;
966 mode &= UNIT_RUNTIME;
968 /* We iterate through the array twice. First run we just check
969 * if all passed data is valid, second run actually applies
970 * it. This is to implement transaction-like behaviour without
971 * actually providing full transactions. */
973 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
974 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
977 dbus_message_iter_recurse(iter, &sub);
979 DBusMessageIter sub2, sub3;
982 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
984 if (for_real || mode == UNIT_CHECK)
987 /* Reached EOF. Let's try again, and this time for realz... */
988 dbus_message_iter_recurse(iter, &sub);
993 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
996 dbus_message_iter_recurse(&sub, &sub2);
998 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
999 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
1002 if (!UNIT_VTABLE(u)->bus_set_property) {
1003 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
1007 dbus_message_iter_recurse(&sub2, &sub3);
1008 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
1009 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
1010 r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
1014 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
1018 dbus_message_iter_next(&sub);
1023 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
1024 UNIT_VTABLE(u)->bus_commit_properties(u);
1029 const BusProperty bus_unit_properties[] = {
1030 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1031 { "Names", bus_unit_append_names, "as", 0 },
1032 { "Following", bus_unit_append_following, "s", 0 },
1033 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1034 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1035 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1036 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1037 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1038 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1039 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1040 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1041 { "RequiredByOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1042 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1043 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1044 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1045 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1046 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1047 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1048 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1049 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1050 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1051 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1052 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1053 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1054 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1055 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1056 { "Description", bus_unit_append_description, "s", 0 },
1057 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1058 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1059 { "SubState", bus_unit_append_sub_state, "s", 0 },
1060 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1061 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1062 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1063 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1064 { "InactiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1065 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1066 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1067 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1068 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1069 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1070 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1071 { "InactiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1072 { "CanStart", bus_unit_append_can_start, "b", 0 },
1073 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1074 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1075 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1076 { "Job", bus_unit_append_job, "(uo)", 0 },
1077 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1078 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1079 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1080 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1081 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1082 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1083 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1084 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1085 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1086 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1087 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1088 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1089 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1090 { "Conditions", bus_property_append_condition_list, "a(sbbsi)", offsetof(Unit, conditions) },
1091 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1092 { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
1096 const BusProperty bus_unit_cgroup_properties[] = {
1097 { "Slice", bus_unit_append_slice, "s", 0 },
1098 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },