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"
34 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
36 #define INVALIDATING_PROPERTIES \
40 "InactiveExitTimestamp\0" \
41 "ActiveEnterTimestamp\0" \
42 "ActiveExitTimestamp\0" \
43 "InactiveEnterTimestamp\0" \
47 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
53 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
56 SET_FOREACH(t, u->names, j)
57 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
60 if (!dbus_message_iter_close_container(i, &sub))
66 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
74 f = unit_following(u);
77 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
83 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
89 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
93 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
96 if (!dbus_message_iter_close_container(i, &sub))
102 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
110 d = unit_description(u);
112 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
118 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
120 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
128 state = unit_active_state_to_string(unit_active_state(u));
130 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
136 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
144 state = unit_sub_state_to_string(u);
146 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
152 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
160 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
162 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
168 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
176 b = unit_can_start(u) &&
177 !u->refuse_manual_start;
179 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
185 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
193 /* On the lower levels we assume that every unit we can start
194 * we can also stop */
196 b = unit_can_start(u) &&
197 !u->refuse_manual_stop;
199 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
205 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
213 b = unit_can_reload(u);
215 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
221 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
229 b = unit_can_isolate(u) &&
230 !u->refuse_manual_start;
232 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
238 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
241 _cleanup_free_ char *p = NULL;
247 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
252 p = job_dbus_path(u->job);
256 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
257 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
262 /* No job, so let's fill in some placeholder
263 * data. Since we need to fill in a valid path we
264 * simple point to ourselves. */
266 p = unit_dbus_path(u);
270 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
271 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
275 if (!dbus_message_iter_close_container(i, &sub))
281 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
291 cgb = unit_get_default_cgroup(u);
293 t = cgroup_bonding_to_string(cgb);
299 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
304 return success ? 0 : -ENOMEM;
307 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
312 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
315 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
316 char _cleanup_free_ *t = NULL;
319 t = cgroup_bonding_to_string(cgb);
323 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) {
343 char _cleanup_free_ *v = NULL;
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);
359 if (!dbus_message_iter_close_container(i, &sub))
365 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
373 b = unit_need_daemon_reload(u);
375 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
381 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
383 const char *name, *message;
390 if (u->load_error != 0) {
391 name = bus_errno_to_dbus(u->load_error);
392 message = strempty(strerror(-u->load_error));
396 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
397 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
398 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
399 !dbus_message_iter_close_container(i, &sub))
405 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
406 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
408 JobType job_type = _JOB_TYPE_INVALID;
409 bool reload_if_possible = false;
412 dbus_error_init(&error);
414 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
415 job_type = JOB_START;
416 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
418 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
419 job_type = JOB_RELOAD;
420 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
421 job_type = JOB_RESTART;
422 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
423 job_type = JOB_TRY_RESTART;
424 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
425 reload_if_possible = true;
426 job_type = JOB_RESTART;
427 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
428 reload_if_possible = true;
429 job_type = JOB_TRY_RESTART;
430 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
435 if (!dbus_message_get_args(
438 DBUS_TYPE_STRING, &swho,
439 DBUS_TYPE_INT32, &signo,
441 return bus_send_error_reply(connection, message, &error, -EINVAL);
446 who = kill_who_from_string(swho);
448 return bus_send_error_reply(connection, message, &error, -EINVAL);
451 if (signo <= 0 || signo >= _NSIG)
452 return bus_send_error_reply(connection, message, &error, -EINVAL);
454 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
456 r = unit_kill(u, who, signo, &error);
458 return bus_send_error_reply(connection, message, &error, r);
460 reply = dbus_message_new_method_return(message);
464 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
466 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
468 unit_reset_failed(u);
470 reply = dbus_message_new_method_return(message);
474 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroups")) {
475 DBusMessageIter iter;
477 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
479 if (!dbus_message_iter_init(message, &iter))
482 r = bus_unit_cgroup_set(u, &iter);
484 return bus_send_error_reply(connection, message, NULL, r);
486 reply = dbus_message_new_method_return(message);
490 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroups")) {
491 DBusMessageIter iter;
493 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
495 if (!dbus_message_iter_init(message, &iter))
498 r = bus_unit_cgroup_set(u, &iter);
500 return bus_send_error_reply(connection, message, NULL, r);
502 reply = dbus_message_new_method_return(message);
505 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttributes")) {
506 DBusMessageIter iter;
508 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
510 if (!dbus_message_iter_init(message, &iter))
513 r = bus_unit_cgroup_attribute_set(u, &iter);
515 return bus_send_error_reply(connection, message, NULL, r);
517 reply = dbus_message_new_method_return(message);
521 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttributes")) {
522 DBusMessageIter iter;
524 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
526 if (!dbus_message_iter_init(message, &iter))
529 r = bus_unit_cgroup_attribute_unset(u, &iter);
531 return bus_send_error_reply(connection, message, NULL, r);
533 reply = dbus_message_new_method_return(message);
537 } else if (UNIT_VTABLE(u)->bus_message_handler)
538 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
540 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
542 if (job_type != _JOB_TYPE_INVALID) {
546 if (!dbus_message_get_args(
549 DBUS_TYPE_STRING, &smode,
551 return bus_send_error_reply(connection, message, &error, -EINVAL);
553 mode = job_mode_from_string(smode);
555 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
556 return bus_send_error_reply(connection, message, &error, -EINVAL);
559 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
563 if (!dbus_connection_send(connection, reply, NULL))
566 return DBUS_HANDLER_RESULT_HANDLED;
569 dbus_error_free(&error);
570 return DBUS_HANDLER_RESULT_NEED_MEMORY;
573 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
577 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
584 dbus_error_init(&error);
586 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
587 /* Be nice to gdbus and return introspection data for our mid-level paths */
589 SELINUX_ACCESS_CHECK(connection, message, "status");
591 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
592 char *introspection = NULL;
598 reply = dbus_message_new_method_return(message);
602 /* We roll our own introspection code here, instead of
603 * relying on bus_default_message_handler() because we
604 * need to generate our introspection string
607 f = open_memstream(&introspection, &size);
611 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
614 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
615 fputs(BUS_PEER_INTERFACE, f);
617 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
623 p = bus_path_escape(k);
630 fprintf(f, "<node name=\"%s\"/>", p);
634 fputs("</node>\n", f);
647 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
654 if (!dbus_connection_send(connection, reply, NULL))
657 return DBUS_HANDLER_RESULT_HANDLED;
660 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
663 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
667 return bus_send_error_reply(connection, message, &error, r);
669 return bus_unit_message_dispatch(u, connection, message);
672 dbus_error_free(&error);
674 return DBUS_HANDLER_RESULT_NEED_MEMORY;
677 const DBusObjectPathVTable bus_unit_vtable = {
678 .message_function = bus_unit_message_handler
681 void bus_unit_send_change_signal(Unit *u) {
682 _cleanup_free_ char *p = NULL;
683 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
687 if (u->in_dbus_queue) {
688 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
689 u->in_dbus_queue = false;
695 if (!bus_has_subscriber(u->manager)) {
696 u->sent_dbus_new_signal = true;
700 p = unit_dbus_path(u);
704 if (u->sent_dbus_new_signal) {
705 /* Send a properties changed signal. First for the
706 * specific type, then for the generic unit. The
707 * clients may rely on this order to get atomic
708 * behavior if needed. */
710 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
712 m = bus_properties_changed_new(p,
713 UNIT_VTABLE(u)->bus_interface,
714 UNIT_VTABLE(u)->bus_invalidating_properties);
718 if (bus_broadcast(u->manager, m) < 0)
721 dbus_message_unref(m);
724 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
725 INVALIDATING_PROPERTIES);
730 /* Send a new signal */
732 m = dbus_message_new_signal("/org/freedesktop/systemd1",
733 "org.freedesktop.systemd1.Manager",
738 if (!dbus_message_append_args(m,
739 DBUS_TYPE_STRING, &u->id,
740 DBUS_TYPE_OBJECT_PATH, &p,
745 if (bus_broadcast(u->manager, m) < 0)
748 u->sent_dbus_new_signal = true;
756 void bus_unit_send_removed_signal(Unit *u) {
757 _cleanup_free_ char *p = NULL;
758 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
762 if (!bus_has_subscriber(u->manager))
765 if (!u->sent_dbus_new_signal)
766 bus_unit_send_change_signal(u);
771 p = unit_dbus_path(u);
775 m = dbus_message_new_signal("/org/freedesktop/systemd1",
776 "org.freedesktop.systemd1.Manager",
781 if (!dbus_message_append_args(m,
782 DBUS_TYPE_STRING, &u->id,
783 DBUS_TYPE_OBJECT_PATH, &p,
787 if (bus_broadcast(u->manager, m) < 0)
796 DBusHandlerResult bus_unit_queue_job(
797 DBusConnection *connection,
798 DBusMessage *message,
802 bool reload_if_possible) {
804 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
805 _cleanup_free_ char *path = NULL;
814 assert(type >= 0 && type < _JOB_TYPE_MAX);
815 assert(mode >= 0 && mode < _JOB_MODE_MAX);
817 dbus_error_init(&error);
819 if (reload_if_possible && unit_can_reload(u)) {
820 if (type == JOB_RESTART)
821 type = JOB_RELOAD_OR_START;
822 else if (type == JOB_TRY_RESTART)
826 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
827 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
828 type == JOB_STOP ? "stop" : "reload");
830 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
831 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
832 return bus_send_error_reply(connection, message, &error, -EPERM);
835 if ((type == JOB_START && u->refuse_manual_start) ||
836 (type == JOB_STOP && u->refuse_manual_stop) ||
837 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
838 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
839 "Operation refused, unit %s may be requested by dependency only.", u->id);
840 return bus_send_error_reply(connection, message, &error, -EPERM);
843 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
845 return bus_send_error_reply(connection, message, &error, r);
847 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
851 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
853 reply = dbus_message_new_method_return(message);
857 path = job_dbus_path(j);
861 if (!dbus_message_append_args(
863 DBUS_TYPE_OBJECT_PATH, &path,
867 if (!dbus_connection_send(connection, reply, NULL))
870 return DBUS_HANDLER_RESULT_HANDLED;
873 dbus_error_free(&error);
875 return DBUS_HANDLER_RESULT_NEED_MEMORY;
878 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
880 _cleanup_strv_free_ char **a = NULL;
886 if (!unit_get_exec_context(u))
889 r = bus_parse_strv_iter(iter, &a);
893 STRV_FOREACH(name, a) {
894 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL;
897 r = cg_split_spec(*name, &controller, &new_path);
901 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
903 old_path = strdup(b->path);
908 r = unit_add_cgroup_from_text(u, *name, true, &b);
913 /* Try to move things to the new place, and clean up the old place */
914 cgroup_bonding_realize(b);
915 cgroup_bonding_migrate(b, u->cgroup_bondings);
918 cg_trim(controller, old_path, true);
925 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
926 _cleanup_strv_free_ char **a = NULL;
933 if (!unit_get_exec_context(u))
936 r = bus_parse_strv_iter(iter, &a);
940 STRV_FOREACH(name, a) {
941 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
944 r = cg_split_spec(*name, &controller, &path);
948 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
952 if (path && !path_equal(path, b->path))
958 /* Try to migrate the old group away */
959 if (cg_get_by_pid(controller, 0, &target) >= 0)
960 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
962 cgroup_bonding_free(b, true);
968 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
969 DBusMessageIter sub, sub2;
975 if (!unit_get_exec_context(u))
978 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
979 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
982 dbus_message_iter_recurse(iter, &sub);
984 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
985 const char *name, *value;
988 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT);
990 dbus_message_iter_recurse(&sub, &sub2);
992 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
993 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) < 0)
996 dbus_message_iter_next(&sub);
998 r = unit_add_cgroup_attribute(u, NULL, name, value, NULL, &a);
1005 b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1007 /* Doesn't exist yet? Then let's add it */
1008 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1013 cgroup_bonding_realize(b);
1014 cgroup_bonding_migrate(b, u->cgroup_bondings);
1019 cgroup_attribute_apply(a, u->cgroup_bondings);
1026 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1027 _cleanup_strv_free_ char **l = NULL;
1034 if (!unit_get_exec_context(u))
1037 r = bus_parse_strv_iter(iter, &l);
1041 STRV_FOREACH(name, l) {
1044 a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
1046 cgroup_attribute_free(a);
1052 const BusProperty bus_unit_properties[] = {
1053 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1054 { "Names", bus_unit_append_names, "as", 0 },
1055 { "Following", bus_unit_append_following, "s", 0 },
1056 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1057 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1058 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1059 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1060 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1061 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1062 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1063 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1064 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1065 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1066 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1067 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1068 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1069 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1070 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1071 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1072 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1073 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1074 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1075 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1076 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1077 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1078 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1079 { "Description", bus_unit_append_description, "s", 0 },
1080 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1081 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1082 { "SubState", bus_unit_append_sub_state, "s", 0 },
1083 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1084 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1085 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1086 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1087 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1088 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1089 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1090 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1091 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1092 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1093 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1094 { "CanStart", bus_unit_append_can_start, "b", 0 },
1095 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1096 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1097 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1098 { "Job", bus_unit_append_job, "(uo)", 0 },
1099 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1100 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1101 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1102 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1103 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1104 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1105 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1106 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1107 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1108 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1109 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1110 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1111 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1112 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1116 const BusProperty bus_unit_cgroup_properties[] = {
1117 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
1118 { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
1119 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },