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), "GetControlGroupAttributes")) {
506 DBusMessageIter iter;
507 _cleanup_strv_free_ char **list = NULL;
509 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
511 if (!dbus_message_iter_init(message, &iter))
514 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
516 return bus_send_error_reply(connection, message, NULL, r);
518 reply = dbus_message_new_method_return(message);
522 dbus_message_iter_init_append(reply, &iter);
523 if (bus_append_strv_iter(&iter, list) < 0)
526 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttributes")) {
527 DBusMessageIter iter;
529 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
531 if (!dbus_message_iter_init(message, &iter))
534 r = bus_unit_cgroup_attribute_set(u, &iter);
536 return bus_send_error_reply(connection, message, NULL, r);
538 reply = dbus_message_new_method_return(message);
542 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttributes")) {
543 DBusMessageIter iter;
545 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
547 if (!dbus_message_iter_init(message, &iter))
550 r = bus_unit_cgroup_attribute_unset(u, &iter);
552 return bus_send_error_reply(connection, message, NULL, r);
554 reply = dbus_message_new_method_return(message);
558 } else if (UNIT_VTABLE(u)->bus_message_handler)
559 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
561 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
563 if (job_type != _JOB_TYPE_INVALID) {
567 if (!dbus_message_get_args(
570 DBUS_TYPE_STRING, &smode,
572 return bus_send_error_reply(connection, message, &error, -EINVAL);
574 mode = job_mode_from_string(smode);
576 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
577 return bus_send_error_reply(connection, message, &error, -EINVAL);
580 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
584 if (!dbus_connection_send(connection, reply, NULL))
587 return DBUS_HANDLER_RESULT_HANDLED;
590 dbus_error_free(&error);
591 return DBUS_HANDLER_RESULT_NEED_MEMORY;
594 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
598 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
605 dbus_error_init(&error);
607 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
608 /* Be nice to gdbus and return introspection data for our mid-level paths */
610 SELINUX_ACCESS_CHECK(connection, message, "status");
612 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
613 char *introspection = NULL;
619 reply = dbus_message_new_method_return(message);
623 /* We roll our own introspection code here, instead of
624 * relying on bus_default_message_handler() because we
625 * need to generate our introspection string
628 f = open_memstream(&introspection, &size);
632 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
635 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
636 fputs(BUS_PEER_INTERFACE, f);
638 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
644 p = bus_path_escape(k);
651 fprintf(f, "<node name=\"%s\"/>", p);
655 fputs("</node>\n", f);
668 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
675 if (!dbus_connection_send(connection, reply, NULL))
678 return DBUS_HANDLER_RESULT_HANDLED;
681 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
684 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
688 return bus_send_error_reply(connection, message, &error, r);
690 return bus_unit_message_dispatch(u, connection, message);
693 dbus_error_free(&error);
695 return DBUS_HANDLER_RESULT_NEED_MEMORY;
698 const DBusObjectPathVTable bus_unit_vtable = {
699 .message_function = bus_unit_message_handler
702 void bus_unit_send_change_signal(Unit *u) {
703 _cleanup_free_ char *p = NULL;
704 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
708 if (u->in_dbus_queue) {
709 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
710 u->in_dbus_queue = false;
716 if (!bus_has_subscriber(u->manager)) {
717 u->sent_dbus_new_signal = true;
721 p = unit_dbus_path(u);
725 if (u->sent_dbus_new_signal) {
726 /* Send a properties changed signal. First for the
727 * specific type, then for the generic unit. The
728 * clients may rely on this order to get atomic
729 * behavior if needed. */
731 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
733 m = bus_properties_changed_new(p,
734 UNIT_VTABLE(u)->bus_interface,
735 UNIT_VTABLE(u)->bus_invalidating_properties);
739 if (bus_broadcast(u->manager, m) < 0)
742 dbus_message_unref(m);
745 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
746 INVALIDATING_PROPERTIES);
751 /* Send a new signal */
753 m = dbus_message_new_signal("/org/freedesktop/systemd1",
754 "org.freedesktop.systemd1.Manager",
759 if (!dbus_message_append_args(m,
760 DBUS_TYPE_STRING, &u->id,
761 DBUS_TYPE_OBJECT_PATH, &p,
766 if (bus_broadcast(u->manager, m) < 0)
769 u->sent_dbus_new_signal = true;
777 void bus_unit_send_removed_signal(Unit *u) {
778 _cleanup_free_ char *p = NULL;
779 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
783 if (!bus_has_subscriber(u->manager))
786 if (!u->sent_dbus_new_signal)
787 bus_unit_send_change_signal(u);
792 p = unit_dbus_path(u);
796 m = dbus_message_new_signal("/org/freedesktop/systemd1",
797 "org.freedesktop.systemd1.Manager",
802 if (!dbus_message_append_args(m,
803 DBUS_TYPE_STRING, &u->id,
804 DBUS_TYPE_OBJECT_PATH, &p,
808 if (bus_broadcast(u->manager, m) < 0)
817 DBusHandlerResult bus_unit_queue_job(
818 DBusConnection *connection,
819 DBusMessage *message,
823 bool reload_if_possible) {
825 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
826 _cleanup_free_ char *path = NULL;
835 assert(type >= 0 && type < _JOB_TYPE_MAX);
836 assert(mode >= 0 && mode < _JOB_MODE_MAX);
838 dbus_error_init(&error);
840 if (reload_if_possible && unit_can_reload(u)) {
841 if (type == JOB_RESTART)
842 type = JOB_RELOAD_OR_START;
843 else if (type == JOB_TRY_RESTART)
847 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
848 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
849 type == JOB_STOP ? "stop" : "reload");
851 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
852 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
853 return bus_send_error_reply(connection, message, &error, -EPERM);
856 if ((type == JOB_START && u->refuse_manual_start) ||
857 (type == JOB_STOP && u->refuse_manual_stop) ||
858 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
859 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
860 "Operation refused, unit %s may be requested by dependency only.", u->id);
861 return bus_send_error_reply(connection, message, &error, -EPERM);
864 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
866 return bus_send_error_reply(connection, message, &error, r);
868 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
872 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
874 reply = dbus_message_new_method_return(message);
878 path = job_dbus_path(j);
882 if (!dbus_message_append_args(
884 DBUS_TYPE_OBJECT_PATH, &path,
888 if (!dbus_connection_send(connection, reply, NULL))
891 return DBUS_HANDLER_RESULT_HANDLED;
894 dbus_error_free(&error);
896 return DBUS_HANDLER_RESULT_NEED_MEMORY;
899 static int next_and_parse_mode(DBusMessageIter *iter, bool *runtime) {
905 dbus_message_iter_next(iter);
906 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
909 dbus_message_iter_get_basic(iter, &mode);
910 if (streq(mode, "runtime"))
912 else if (streq(mode, "persistent"))
920 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
922 _cleanup_strv_free_ char **a = NULL;
929 if (!unit_get_exec_context(u))
932 r = bus_parse_strv_iter(iter, &a);
936 r = next_and_parse_mode(iter, &runtime);
940 STRV_FOREACH(name, a) {
941 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
944 r = cg_split_spec(*name, &controller, &new_path);
948 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
950 old_path = strdup(b->path);
955 r = unit_add_cgroup_from_text(u, *name, true, &b);
960 /* Try to move things to the new place, and clean up the old place */
961 cgroup_bonding_realize(b);
962 cgroup_bonding_migrate(b, u->cgroup_bondings);
965 cg_trim(controller, old_path, true);
968 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
969 "ControlGroup=", *name, "\n", NULL);
973 r = unit_write_drop_in(u, runtime, *name, contents);
981 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
982 _cleanup_strv_free_ char **a = NULL;
990 if (!unit_get_exec_context(u))
993 r = bus_parse_strv_iter(iter, &a);
997 r = next_and_parse_mode(iter, &runtime);
1001 STRV_FOREACH(name, a) {
1002 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
1005 r = cg_split_spec(*name, &controller, &path);
1009 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1012 unit_remove_drop_in(u, runtime, *name);
1014 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1018 if (path && !path_equal(path, b->path))
1024 /* Try to migrate the old group away */
1025 if (cg_get_by_pid(controller, 0, &target) >= 0)
1026 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1028 cgroup_bonding_free(b, true);
1034 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1035 _cleanup_strv_free_ char **l = NULL, **result = NULL;
1043 if (!unit_get_exec_context(u))
1046 r = bus_parse_strv_iter(iter, &l);
1050 STRV_FOREACH(name, l) {
1051 _cleanup_free_ char *controller = NULL;
1055 r = cg_controller_from_attr(*name, &controller);
1059 /* First attempt, read the value from the kernel */
1060 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1062 _cleanup_free_ char *p = NULL, *v = NULL;
1064 r = cg_get_path(b->controller, b->path, *name, &p);
1068 r = read_full_file(p, &v, NULL);
1070 r = strv_extend(&result, v);
1075 } else if (r != -ENOENT)
1079 /* If that didn't work, read our cached value */
1080 a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
1082 r = strv_extend(&result, a->value);
1098 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1099 _cleanup_strv_free_ char **l = NULL;
1101 bool runtime = false;
1102 char **name, **value;
1107 if (!unit_get_exec_context(u))
1110 r = bus_parse_strv_pairs_iter(iter, &l);
1114 r = next_and_parse_mode(iter, &runtime);
1118 STRV_FOREACH_PAIR(name, value, l) {
1119 _cleanup_free_ char *contents = NULL;
1122 r = unit_add_cgroup_attribute(u, NULL, *name, *value, NULL, &a);
1129 b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1131 /* Doesn't exist yet? Then let's add it */
1132 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1137 cgroup_bonding_realize(b);
1138 cgroup_bonding_migrate(b, u->cgroup_bondings);
1143 cgroup_attribute_apply(a, u->cgroup_bondings);
1146 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1147 "ControlGroupAttribute=", *name, " ", *value, "\n", NULL);
1151 r = unit_write_drop_in(u, runtime, *name, contents);
1159 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1160 _cleanup_strv_free_ char **l = NULL;
1168 if (!unit_get_exec_context(u))
1171 r = bus_parse_strv_iter(iter, &l);
1175 r = next_and_parse_mode(iter, &runtime);
1179 STRV_FOREACH(name, l) {
1182 a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
1184 cgroup_attribute_free(a);
1186 unit_remove_drop_in(u, runtime, *name);
1192 const BusProperty bus_unit_properties[] = {
1193 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1194 { "Names", bus_unit_append_names, "as", 0 },
1195 { "Following", bus_unit_append_following, "s", 0 },
1196 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1197 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1198 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1199 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1200 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1201 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1202 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1203 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1204 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1205 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1206 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1207 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1208 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1209 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1210 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1211 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1212 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1213 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1214 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1215 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1216 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1217 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1218 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1219 { "Description", bus_unit_append_description, "s", 0 },
1220 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1221 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1222 { "SubState", bus_unit_append_sub_state, "s", 0 },
1223 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1224 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1225 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1226 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1227 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1228 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1229 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1230 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1231 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1232 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1233 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1234 { "CanStart", bus_unit_append_can_start, "b", 0 },
1235 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1236 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1237 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1238 { "Job", bus_unit_append_job, "(uo)", 0 },
1239 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1240 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1241 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1242 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1243 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1244 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1245 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1246 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1247 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1248 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1249 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1250 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1251 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1252 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1256 const BusProperty bus_unit_cgroup_properties[] = {
1257 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
1258 { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
1259 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },