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) {
238 _cleanup_free_ char *p = NULL;
244 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
249 p = job_dbus_path(u->job);
253 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
254 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
259 /* No job, so let's fill in some placeholder
260 * data. Since we need to fill in a valid path we
261 * simple point to ourselves. */
263 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))
272 if (!dbus_message_iter_close_container(i, &sub))
278 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
288 cgb = unit_get_default_cgroup(u);
290 t = cgroup_bonding_to_string(cgb);
296 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
301 return success ? 0 : -ENOMEM;
304 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
309 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
312 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
313 char _cleanup_free_ *t = NULL;
316 t = cgroup_bonding_to_string(cgb);
320 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
325 if (!dbus_message_iter_close_container(i, &sub))
331 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
334 DBusMessageIter sub, sub2;
336 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
339 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
340 char _cleanup_free_ *v = NULL;
344 a->map_callback(a->controller, a->name, a->value, &v);
347 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
348 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
349 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
350 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
351 dbus_message_iter_close_container(&sub, &sub2);
356 if (!dbus_message_iter_close_container(i, &sub))
362 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
370 b = unit_need_daemon_reload(u);
372 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
378 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
380 const char *name, *message;
387 if (u->load_error != 0) {
388 name = bus_errno_to_dbus(u->load_error);
389 message = strempty(strerror(-u->load_error));
393 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
394 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
395 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
396 !dbus_message_iter_close_container(i, &sub))
402 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
403 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
405 JobType job_type = _JOB_TYPE_INVALID;
406 bool reload_if_possible = false;
409 dbus_error_init(&error);
411 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
412 job_type = JOB_START;
413 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
415 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
416 job_type = JOB_RELOAD;
417 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
418 job_type = JOB_RESTART;
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
420 job_type = JOB_TRY_RESTART;
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
422 reload_if_possible = true;
423 job_type = JOB_RESTART;
424 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
425 reload_if_possible = true;
426 job_type = JOB_TRY_RESTART;
427 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
432 if (!dbus_message_get_args(
435 DBUS_TYPE_STRING, &swho,
436 DBUS_TYPE_INT32, &signo,
438 return bus_send_error_reply(connection, message, &error, -EINVAL);
443 who = kill_who_from_string(swho);
445 return bus_send_error_reply(connection, message, &error, -EINVAL);
448 if (signo <= 0 || signo >= _NSIG)
449 return bus_send_error_reply(connection, message, &error, -EINVAL);
451 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
453 r = unit_kill(u, who, signo, &error);
455 return bus_send_error_reply(connection, message, &error, r);
457 reply = dbus_message_new_method_return(message);
461 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
463 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
465 unit_reset_failed(u);
467 reply = dbus_message_new_method_return(message);
471 } else if (UNIT_VTABLE(u)->bus_message_handler)
472 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
474 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
476 if (job_type != _JOB_TYPE_INVALID) {
480 if (!dbus_message_get_args(
483 DBUS_TYPE_STRING, &smode,
485 return bus_send_error_reply(connection, message, &error, -EINVAL);
487 mode = job_mode_from_string(smode);
489 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
490 return bus_send_error_reply(connection, message, &error, -EINVAL);
493 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
497 if (!dbus_connection_send(connection, reply, NULL))
500 return DBUS_HANDLER_RESULT_HANDLED;
503 dbus_error_free(&error);
504 return DBUS_HANDLER_RESULT_NEED_MEMORY;
507 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
511 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
518 dbus_error_init(&error);
520 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
521 /* Be nice to gdbus and return introspection data for our mid-level paths */
523 SELINUX_ACCESS_CHECK(connection, message, "status");
525 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
526 char *introspection = NULL;
532 reply = dbus_message_new_method_return(message);
536 /* We roll our own introspection code here, instead of
537 * relying on bus_default_message_handler() because we
538 * need to generate our introspection string
541 f = open_memstream(&introspection, &size);
545 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
548 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
549 fputs(BUS_PEER_INTERFACE, f);
551 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
557 p = bus_path_escape(k);
564 fprintf(f, "<node name=\"%s\"/>", p);
568 fputs("</node>\n", f);
581 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
588 if (!dbus_connection_send(connection, reply, NULL))
591 return DBUS_HANDLER_RESULT_HANDLED;
594 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
597 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
601 return bus_send_error_reply(connection, message, &error, r);
603 return bus_unit_message_dispatch(u, connection, message);
606 dbus_error_free(&error);
608 return DBUS_HANDLER_RESULT_NEED_MEMORY;
611 const DBusObjectPathVTable bus_unit_vtable = {
612 .message_function = bus_unit_message_handler
615 void bus_unit_send_change_signal(Unit *u) {
616 _cleanup_free_ char *p = NULL;
617 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
621 if (u->in_dbus_queue) {
622 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
623 u->in_dbus_queue = false;
629 if (!bus_has_subscriber(u->manager)) {
630 u->sent_dbus_new_signal = true;
634 p = unit_dbus_path(u);
638 if (u->sent_dbus_new_signal) {
639 /* Send a properties changed signal. First for the
640 * specific type, then for the generic unit. The
641 * clients may rely on this order to get atomic
642 * behavior if needed. */
644 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
646 m = bus_properties_changed_new(p,
647 UNIT_VTABLE(u)->bus_interface,
648 UNIT_VTABLE(u)->bus_invalidating_properties);
652 if (bus_broadcast(u->manager, m) < 0)
655 dbus_message_unref(m);
658 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
659 INVALIDATING_PROPERTIES);
664 /* Send a new signal */
666 m = dbus_message_new_signal("/org/freedesktop/systemd1",
667 "org.freedesktop.systemd1.Manager",
672 if (!dbus_message_append_args(m,
673 DBUS_TYPE_STRING, &u->id,
674 DBUS_TYPE_OBJECT_PATH, &p,
679 if (bus_broadcast(u->manager, m) < 0)
682 u->sent_dbus_new_signal = true;
690 void bus_unit_send_removed_signal(Unit *u) {
691 _cleanup_free_ char *p = NULL;
692 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
696 if (!bus_has_subscriber(u->manager))
699 if (!u->sent_dbus_new_signal)
700 bus_unit_send_change_signal(u);
705 p = unit_dbus_path(u);
709 m = dbus_message_new_signal("/org/freedesktop/systemd1",
710 "org.freedesktop.systemd1.Manager",
715 if (!dbus_message_append_args(m,
716 DBUS_TYPE_STRING, &u->id,
717 DBUS_TYPE_OBJECT_PATH, &p,
721 if (bus_broadcast(u->manager, m) < 0)
730 DBusHandlerResult bus_unit_queue_job(
731 DBusConnection *connection,
732 DBusMessage *message,
736 bool reload_if_possible) {
738 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
739 _cleanup_free_ char *path = NULL;
748 assert(type >= 0 && type < _JOB_TYPE_MAX);
749 assert(mode >= 0 && mode < _JOB_MODE_MAX);
751 dbus_error_init(&error);
753 if (reload_if_possible && unit_can_reload(u)) {
754 if (type == JOB_RESTART)
755 type = JOB_RELOAD_OR_START;
756 else if (type == JOB_TRY_RESTART)
760 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
761 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
762 type == JOB_STOP ? "stop" : "reload");
764 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
765 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
766 return bus_send_error_reply(connection, message, &error, -EPERM);
769 if ((type == JOB_START && u->refuse_manual_start) ||
770 (type == JOB_STOP && u->refuse_manual_stop) ||
771 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
772 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
773 "Operation refused, unit %s may be requested by dependency only.", u->id);
774 return bus_send_error_reply(connection, message, &error, -EPERM);
777 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
779 return bus_send_error_reply(connection, message, &error, r);
781 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
785 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
787 reply = dbus_message_new_method_return(message);
791 path = job_dbus_path(j);
795 if (!dbus_message_append_args(
797 DBUS_TYPE_OBJECT_PATH, &path,
801 if (!dbus_connection_send(connection, reply, NULL))
804 return DBUS_HANDLER_RESULT_HANDLED;
807 dbus_error_free(&error);
809 return DBUS_HANDLER_RESULT_NEED_MEMORY;
812 const BusProperty bus_unit_properties[] = {
813 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
814 { "Names", bus_unit_append_names, "as", 0 },
815 { "Following", bus_unit_append_following, "s", 0 },
816 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
817 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
818 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
819 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
820 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
821 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
822 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
823 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
824 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
825 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
826 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
827 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
828 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
829 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
830 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
831 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
832 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
833 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
834 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
835 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
836 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
837 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
838 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
839 { "Description", bus_unit_append_description, "s", 0 },
840 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
841 { "ActiveState", bus_unit_append_active_state, "s", 0 },
842 { "SubState", bus_unit_append_sub_state, "s", 0 },
843 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
844 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
845 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
846 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
847 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
848 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
849 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
850 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
851 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
852 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
853 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
854 { "CanStart", bus_unit_append_can_start, "b", 0 },
855 { "CanStop", bus_unit_append_can_stop, "b", 0 },
856 { "CanReload", bus_unit_append_can_reload, "b", 0 },
857 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
858 { "Job", bus_unit_append_job, "(uo)", 0 },
859 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
860 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
861 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
862 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
863 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
864 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
865 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
866 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
867 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
868 { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
869 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
870 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
871 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
872 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
873 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
874 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
875 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },