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"
31 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
33 #define INVALIDATING_PROPERTIES \
37 "InactiveExitTimestamp\0" \
38 "ActiveEnterTimestamp\0" \
39 "ActiveExitTimestamp\0" \
40 "InactiveEnterTimestamp\0" \
44 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
50 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
53 SET_FOREACH(t, u->names, j)
54 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
57 if (!dbus_message_iter_close_container(i, &sub))
63 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
71 f = unit_following(u);
74 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
80 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
86 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
90 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
93 if (!dbus_message_iter_close_container(i, &sub))
99 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
107 d = unit_description(u);
109 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
115 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
117 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
125 state = unit_active_state_to_string(unit_active_state(u));
127 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
133 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
141 state = unit_sub_state_to_string(u);
143 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
149 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
157 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
159 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
165 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
173 b = unit_can_start(u) &&
174 !u->refuse_manual_start;
176 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
182 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
190 /* On the lower levels we assume that every unit we can start
191 * we can also stop */
193 b = unit_can_start(u) &&
194 !u->refuse_manual_stop;
196 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
202 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
210 b = unit_can_reload(u);
212 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
218 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
226 b = unit_can_isolate(u) &&
227 !u->refuse_manual_start;
229 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
235 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
244 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
249 if (!(p = job_dbus_path(u->job)))
252 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
253 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
260 /* No job, so let's fill in some placeholder
261 * data. Since we need to fill in a valid path we
262 * simple point to ourselves. */
264 if (!(p = unit_dbus_path(u)))
267 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
268 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
276 if (!dbus_message_iter_close_container(i, &sub))
282 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
292 if ((cgb = unit_get_default_cgroup(u))) {
293 if (!(t = cgroup_bonding_to_string(cgb)))
298 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
303 return success ? 0 : -ENOMEM;
306 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
311 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
314 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
318 if (!(t = cgroup_bonding_to_string(cgb)))
321 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
328 if (!dbus_message_iter_close_container(i, &sub))
334 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
337 DBusMessageIter sub, sub2;
339 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
342 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
347 a->map_callback(a->controller, a->name, a->value, &v);
350 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
351 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
352 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
353 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
354 dbus_message_iter_close_container(&sub, &sub2);
362 if (!dbus_message_iter_close_container(i, &sub))
368 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
376 b = unit_need_daemon_reload(u);
378 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
384 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
386 const char *name, *message;
393 if (u->load_error != 0) {
394 name = bus_errno_to_dbus(u->load_error);
395 message = strempty(strerror(-u->load_error));
399 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
400 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
401 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
402 !dbus_message_iter_close_container(i, &sub))
408 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
409 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
411 JobType job_type = _JOB_TYPE_INVALID;
412 bool reload_if_possible = false;
415 dbus_error_init(&error);
417 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
418 job_type = JOB_START;
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
422 job_type = JOB_RELOAD;
423 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
424 job_type = JOB_RESTART;
425 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
426 job_type = JOB_TRY_RESTART;
427 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
428 reload_if_possible = true;
429 job_type = JOB_RESTART;
430 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
431 reload_if_possible = true;
432 job_type = JOB_TRY_RESTART;
433 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
438 if (!dbus_message_get_args(
441 DBUS_TYPE_STRING, &swho,
442 DBUS_TYPE_INT32, &signo,
444 return bus_send_error_reply(connection, message, &error, -EINVAL);
449 who = kill_who_from_string(swho);
451 return bus_send_error_reply(connection, message, &error, -EINVAL);
454 if (signo <= 0 || signo >= _NSIG)
455 return bus_send_error_reply(connection, message, &error, -EINVAL);
457 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
459 r = unit_kill(u, who, signo, &error);
461 return bus_send_error_reply(connection, message, &error, r);
463 reply = dbus_message_new_method_return(message);
467 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
469 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
471 unit_reset_failed(u);
473 reply = dbus_message_new_method_return(message);
477 } else if (UNIT_VTABLE(u)->bus_message_handler)
478 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
480 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
482 if (job_type != _JOB_TYPE_INVALID) {
486 if (!dbus_message_get_args(
489 DBUS_TYPE_STRING, &smode,
491 return bus_send_error_reply(connection, message, &error, -EINVAL);
493 mode = job_mode_from_string(smode);
495 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
496 return bus_send_error_reply(connection, message, &error, -EINVAL);
499 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
503 if (!dbus_connection_send(connection, reply, NULL))
506 return DBUS_HANDLER_RESULT_HANDLED;
509 dbus_error_free(&error);
510 return DBUS_HANDLER_RESULT_NEED_MEMORY;
513 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
517 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
524 dbus_error_init(&error);
526 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
527 /* Be nice to gdbus and return introspection data for our mid-level paths */
529 SELINUX_ACCESS_CHECK(connection, message, "status");
531 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
532 char *introspection = NULL;
538 reply = dbus_message_new_method_return(message);
542 /* We roll our own introspection code here, instead of
543 * relying on bus_default_message_handler() because we
544 * need to generate our introspection string
547 f = open_memstream(&introspection, &size);
551 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
554 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
555 fputs(BUS_PEER_INTERFACE, f);
557 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
563 p = bus_path_escape(k);
570 fprintf(f, "<node name=\"%s\"/>", p);
574 fputs("</node>\n", f);
587 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
594 if (!dbus_connection_send(connection, reply, NULL))
597 return DBUS_HANDLER_RESULT_HANDLED;
600 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
603 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
607 return bus_send_error_reply(connection, message, &error, r);
609 return bus_unit_message_dispatch(u, connection, message);
612 dbus_error_free(&error);
614 return DBUS_HANDLER_RESULT_NEED_MEMORY;
617 const DBusObjectPathVTable bus_unit_vtable = {
618 .message_function = bus_unit_message_handler
621 void bus_unit_send_change_signal(Unit *u) {
623 DBusMessage *m = NULL;
627 if (u->in_dbus_queue) {
628 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
629 u->in_dbus_queue = false;
635 if (!bus_has_subscriber(u->manager)) {
636 u->sent_dbus_new_signal = true;
640 if (!(p = unit_dbus_path(u)))
643 if (u->sent_dbus_new_signal) {
644 /* Send a properties changed signal. First for the
645 * specific type, then for the generic unit. The
646 * clients may rely on this order to get atomic
647 * behavior if needed. */
649 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
651 if (!(m = bus_properties_changed_new(p,
652 UNIT_VTABLE(u)->bus_interface,
653 UNIT_VTABLE(u)->bus_invalidating_properties)))
656 if (bus_broadcast(u->manager, m) < 0)
659 dbus_message_unref(m);
662 if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
666 /* Send a new signal */
668 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
671 if (!dbus_message_append_args(m,
672 DBUS_TYPE_STRING, &u->id,
673 DBUS_TYPE_OBJECT_PATH, &p,
678 if (bus_broadcast(u->manager, m) < 0)
682 dbus_message_unref(m);
684 u->sent_dbus_new_signal = true;
692 dbus_message_unref(m);
694 log_error("Failed to allocate unit change/new signal.");
697 void bus_unit_send_removed_signal(Unit *u) {
699 DBusMessage *m = NULL;
703 if (!bus_has_subscriber(u->manager))
706 if (!u->sent_dbus_new_signal)
707 bus_unit_send_change_signal(u);
712 if (!(p = unit_dbus_path(u)))
715 if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
718 if (!dbus_message_append_args(m,
719 DBUS_TYPE_STRING, &u->id,
720 DBUS_TYPE_OBJECT_PATH, &p,
724 if (bus_broadcast(u->manager, m) < 0)
728 dbus_message_unref(m);
736 dbus_message_unref(m);
738 log_error("Failed to allocate unit remove signal.");
741 DBusHandlerResult bus_unit_queue_job(
742 DBusConnection *connection,
743 DBusMessage *message,
747 bool reload_if_possible) {
749 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
750 _cleanup_free_ char *path = NULL;
759 assert(type >= 0 && type < _JOB_TYPE_MAX);
760 assert(mode >= 0 && mode < _JOB_MODE_MAX);
762 dbus_error_init(&error);
764 if (reload_if_possible && unit_can_reload(u)) {
765 if (type == JOB_RESTART)
766 type = JOB_RELOAD_OR_START;
767 else if (type == JOB_TRY_RESTART)
771 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
772 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
773 type == JOB_STOP ? "stop" : "reload");
775 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
776 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
777 return bus_send_error_reply(connection, message, &error, -EPERM);
780 if ((type == JOB_START && u->refuse_manual_start) ||
781 (type == JOB_STOP && u->refuse_manual_stop) ||
782 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
783 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
784 return bus_send_error_reply(connection, message, &error, -EPERM);
787 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
789 return bus_send_error_reply(connection, message, &error, r);
791 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
795 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
797 reply = dbus_message_new_method_return(message);
801 path = job_dbus_path(j);
805 if (!dbus_message_append_args(
807 DBUS_TYPE_OBJECT_PATH, &path,
811 if (!dbus_connection_send(connection, reply, NULL))
814 return DBUS_HANDLER_RESULT_HANDLED;
817 dbus_error_free(&error);
819 return DBUS_HANDLER_RESULT_NEED_MEMORY;
822 const BusProperty bus_unit_properties[] = {
823 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
824 { "Names", bus_unit_append_names, "as", 0 },
825 { "Following", bus_unit_append_following, "s", 0 },
826 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
827 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
828 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
829 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
830 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
831 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
832 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
833 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
834 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
835 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
836 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
837 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
838 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
839 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
840 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
841 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
842 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
843 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
844 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
845 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
846 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
847 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
848 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
849 { "Description", bus_unit_append_description, "s", 0 },
850 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
851 { "ActiveState", bus_unit_append_active_state, "s", 0 },
852 { "SubState", bus_unit_append_sub_state, "s", 0 },
853 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
854 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
855 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
856 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
857 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
858 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
859 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
860 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
861 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
862 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
863 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
864 { "CanStart", bus_unit_append_can_start, "b", 0 },
865 { "CanStop", bus_unit_append_can_stop, "b", 0 },
866 { "CanReload", bus_unit_append_can_reload, "b", 0 },
867 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
868 { "Job", bus_unit_append_job, "(uo)", 0 },
869 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
870 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
871 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
872 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
873 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
874 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
875 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
876 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
877 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
878 { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
879 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
880 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
881 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
882 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
883 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
884 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
885 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },