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[] _introspect_("Unit") = 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_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
316 const char *name, *message;
323 if (u->load_error != 0) {
324 name = bus_errno_to_dbus(u->load_error);
325 message = strempty(strerror(-u->load_error));
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))
338 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
339 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
341 JobType job_type = _JOB_TYPE_INVALID;
342 bool reload_if_possible = false;
345 dbus_error_init(&error);
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"))
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")) {
368 if (!dbus_message_get_args(
371 DBUS_TYPE_STRING, &swho,
372 DBUS_TYPE_INT32, &signo,
374 return bus_send_error_reply(connection, message, &error, -EINVAL);
379 who = kill_who_from_string(swho);
381 return bus_send_error_reply(connection, message, &error, -EINVAL);
384 if (signo <= 0 || signo >= _NSIG)
385 return bus_send_error_reply(connection, message, &error, -EINVAL);
387 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
389 r = unit_kill(u, who, signo, &error);
391 return bus_send_error_reply(connection, message, &error, r);
393 reply = dbus_message_new_method_return(message);
397 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
399 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
401 unit_reset_failed(u);
403 reply = dbus_message_new_method_return(message);
406 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
407 DBusMessageIter iter;
410 if (!dbus_message_iter_init(message, &iter))
413 if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
414 return bus_send_error_reply(connection, message, NULL, -EINVAL);
416 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
418 r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
420 return bus_send_error_reply(connection, message, &error, r);
422 reply = dbus_message_new_method_return(message);
426 } else if (UNIT_VTABLE(u)->bus_message_handler)
427 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
429 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
431 if (job_type != _JOB_TYPE_INVALID) {
435 if (!dbus_message_get_args(
438 DBUS_TYPE_STRING, &smode,
440 return bus_send_error_reply(connection, message, &error, -EINVAL);
442 mode = job_mode_from_string(smode);
444 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
445 return bus_send_error_reply(connection, message, &error, -EINVAL);
448 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
452 if (!bus_maybe_send_reply(connection, message, reply))
455 return DBUS_HANDLER_RESULT_HANDLED;
458 dbus_error_free(&error);
459 return DBUS_HANDLER_RESULT_NEED_MEMORY;
462 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
466 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
473 dbus_error_init(&error);
475 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
476 /* Be nice to gdbus and return introspection data for our mid-level paths */
478 SELINUX_ACCESS_CHECK(connection, message, "status");
480 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
481 char *introspection = NULL;
487 reply = dbus_message_new_method_return(message);
491 /* We roll our own introspection code here, instead of
492 * relying on bus_default_message_handler() because we
493 * need to generate our introspection string
496 f = open_memstream(&introspection, &size);
500 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
503 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
504 fputs(BUS_PEER_INTERFACE, f);
506 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
512 p = bus_path_escape(k);
519 fprintf(f, "<node name=\"%s\"/>", p);
523 fputs("</node>\n", f);
536 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
543 if (!bus_maybe_send_reply(connection, message, reply))
546 return DBUS_HANDLER_RESULT_HANDLED;
549 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
552 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
556 return bus_send_error_reply(connection, message, &error, r);
558 return bus_unit_message_dispatch(u, connection, message);
561 dbus_error_free(&error);
563 return DBUS_HANDLER_RESULT_NEED_MEMORY;
566 const DBusObjectPathVTable bus_unit_vtable = {
567 .message_function = bus_unit_message_handler
570 void bus_unit_send_change_signal(Unit *u) {
571 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
572 _cleanup_free_ char *p = NULL;
577 if (u->in_dbus_queue) {
578 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
579 u->in_dbus_queue = false;
585 if (!bus_has_subscriber(u->manager)) {
586 u->sent_dbus_new_signal = true;
590 p = unit_dbus_path(u);
596 if (u->sent_dbus_new_signal) {
597 /* Send a properties changed signal. First for the
598 * specific type, then for the generic unit. The
599 * clients may rely on this order to get atomic
600 * behavior if needed. */
602 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
604 m = bus_properties_changed_new(p,
605 UNIT_VTABLE(u)->bus_interface,
606 UNIT_VTABLE(u)->bus_invalidating_properties);
612 r = bus_broadcast(u->manager, m);
614 log_error("Failed to broadcast change message: %s", strerror(-r));
618 dbus_message_unref(m);
621 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
622 INVALIDATING_PROPERTIES);
629 /* Send a new signal */
631 m = dbus_message_new_signal("/org/freedesktop/systemd1",
632 "org.freedesktop.systemd1.Manager",
639 if (!dbus_message_append_args(m,
640 DBUS_TYPE_STRING, &u->id,
641 DBUS_TYPE_OBJECT_PATH, &p,
642 DBUS_TYPE_INVALID)) {
648 r = bus_broadcast(u->manager, m);
650 log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
654 u->sent_dbus_new_signal = true;
657 void bus_unit_send_removed_signal(Unit *u) {
658 _cleanup_free_ char *p = NULL;
659 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
663 if (!bus_has_subscriber(u->manager))
666 if (!u->sent_dbus_new_signal)
667 bus_unit_send_change_signal(u);
672 p = unit_dbus_path(u);
676 m = dbus_message_new_signal("/org/freedesktop/systemd1",
677 "org.freedesktop.systemd1.Manager",
682 if (!dbus_message_append_args(m,
683 DBUS_TYPE_STRING, &u->id,
684 DBUS_TYPE_OBJECT_PATH, &p,
688 if (bus_broadcast(u->manager, m) < 0)
697 DBusHandlerResult bus_unit_queue_job(
698 DBusConnection *connection,
699 DBusMessage *message,
703 bool reload_if_possible) {
705 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
706 _cleanup_free_ char *path = NULL;
715 assert(type >= 0 && type < _JOB_TYPE_MAX);
716 assert(mode >= 0 && mode < _JOB_MODE_MAX);
718 dbus_error_init(&error);
720 if (reload_if_possible && unit_can_reload(u)) {
721 if (type == JOB_RESTART)
722 type = JOB_RELOAD_OR_START;
723 else if (type == JOB_TRY_RESTART)
727 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
728 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
729 type == JOB_STOP ? "stop" : "reload");
731 if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
732 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
733 return bus_send_error_reply(connection, message, &error, -EPERM);
736 if ((type == JOB_START && u->refuse_manual_start) ||
737 (type == JOB_STOP && u->refuse_manual_stop) ||
738 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
739 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
740 "Operation refused, unit %s may be requested by dependency only.", u->id);
741 return bus_send_error_reply(connection, message, &error, -EPERM);
744 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
746 return bus_send_error_reply(connection, message, &error, r);
748 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
752 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
754 reply = dbus_message_new_method_return(message);
758 path = job_dbus_path(j);
762 if (!dbus_message_append_args(
764 DBUS_TYPE_OBJECT_PATH, &path,
768 if (!bus_maybe_send_reply(connection, message, reply))
771 return DBUS_HANDLER_RESULT_HANDLED;
774 dbus_error_free(&error);
776 return DBUS_HANDLER_RESULT_NEED_MEMORY;
779 static int bus_unit_set_transient_property(
783 UnitSetPropertiesMode mode,
792 if (streq(name, "Description")) {
793 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
796 if (mode != UNIT_CHECK) {
797 const char *description;
799 dbus_message_iter_get_basic(i, &description);
801 r = unit_set_description(u, description);
805 unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", description);
810 } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
813 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
816 dbus_message_iter_get_basic(i, &s);
819 if (mode != UNIT_CHECK) {
820 unit_ref_unset(&u->slice);
821 unit_remove_drop_in(u, mode, name);
826 r = manager_load_unit(u->manager, s, NULL, error, &slice);
830 if (slice->type != UNIT_SLICE)
833 if (mode != UNIT_CHECK) {
834 unit_ref_set(&u->slice, slice);
835 unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
841 } else if (streq(name, "Requires") ||
842 streq(name, "RequiresOverridable") ||
843 streq(name, "Requisite") ||
844 streq(name, "RequisiteOverridable") ||
845 streq(name, "Wants") ||
846 streq(name, "BindsTo") ||
847 streq(name, "Conflicts") ||
848 streq(name, "Before") ||
849 streq(name, "After") ||
850 streq(name, "OnFailure") ||
851 streq(name, "PropagatesReloadTo") ||
852 streq(name, "ReloadPropagatedFrom") ||
853 streq(name, "PartOf")) {
858 d = unit_dependency_from_string(name);
862 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
863 dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRING)
866 dbus_message_iter_recurse(i, &sub);
867 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
870 dbus_message_iter_get_basic(&sub, &other);
872 if (!unit_name_is_valid(other, false))
875 if (mode != UNIT_CHECK) {
876 _cleanup_free_ char *label = NULL;
878 r = unit_add_dependency_by_name(u, d, other, NULL, true);
882 label = strjoin(name, "-", other, NULL);
886 unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
889 dbus_message_iter_next(&sub);
898 int bus_unit_set_properties(
900 DBusMessageIter *iter,
901 UnitSetPropertiesMode mode,
905 bool for_real = false;
914 mode &= UNIT_RUNTIME;
916 /* We iterate through the array twice. First run we just check
917 * if all passed data is valid, second run actually applies
918 * it. This is to implement transaction-like behaviour without
919 * actually providing full transactions. */
921 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
922 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
925 dbus_message_iter_recurse(iter, &sub);
927 DBusMessageIter sub2, sub3;
930 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
935 /* Reached EOF. Let's try again, and this time for realz... */
936 dbus_message_iter_recurse(iter, &sub);
941 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
944 dbus_message_iter_recurse(&sub, &sub2);
946 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
947 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
950 if (!UNIT_VTABLE(u)->bus_set_property) {
951 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
955 dbus_message_iter_recurse(&sub2, &sub3);
956 r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
957 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
958 r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
962 dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
966 dbus_message_iter_next(&sub);
971 if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
972 UNIT_VTABLE(u)->bus_commit_properties(u);
977 const BusProperty bus_unit_properties[] = {
978 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
979 { "Names", bus_unit_append_names, "as", 0 },
980 { "Following", bus_unit_append_following, "s", 0 },
981 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
982 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
983 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
984 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
985 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
986 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
987 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
988 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
989 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
990 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
991 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
992 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
993 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
994 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
995 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
996 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
997 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
998 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
999 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1000 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1001 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1002 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1003 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1004 { "Description", bus_unit_append_description, "s", 0 },
1005 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1006 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1007 { "SubState", bus_unit_append_sub_state, "s", 0 },
1008 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1009 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1010 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1011 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1012 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1013 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1014 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1015 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1016 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1017 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1018 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1019 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1020 { "CanStart", bus_unit_append_can_start, "b", 0 },
1021 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1022 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1023 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1024 { "Job", bus_unit_append_job, "(uo)", 0 },
1025 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1026 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1027 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1028 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1029 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1030 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1031 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1032 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1033 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1034 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1035 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1036 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1037 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1038 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1039 { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
1043 const BusProperty bus_unit_cgroup_properties[] = {
1044 { "Slice", bus_unit_append_slice, "s", 0 },
1045 { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },