1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "dbus-unit.h"
27 #include "bus-errors.h"
28 #include "dbus-common.h"
29 #include "selinux-access.h"
30 #include "cgroup-util.h"
32 #include "path-util.h"
35 const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
37 #define INVALIDATING_PROPERTIES \
41 "InactiveExitTimestamp\0" \
42 "ActiveEnterTimestamp\0" \
43 "ActiveExitTimestamp\0" \
44 "InactiveEnterTimestamp\0" \
48 static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
54 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
57 SET_FOREACH(t, u->names, j)
58 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
61 if (!dbus_message_iter_close_container(i, &sub))
67 static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
75 f = unit_following(u);
78 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
84 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
90 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
94 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
97 if (!dbus_message_iter_close_container(i, &sub))
103 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
111 d = unit_description(u);
113 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
119 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
121 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
129 state = unit_active_state_to_string(unit_active_state(u));
131 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
137 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
145 state = unit_sub_state_to_string(u);
147 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
153 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
161 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
163 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
169 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
177 b = unit_can_start(u) &&
178 !u->refuse_manual_start;
180 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
186 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
194 /* On the lower levels we assume that every unit we can start
195 * we can also stop */
197 b = unit_can_start(u) &&
198 !u->refuse_manual_stop;
200 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
206 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
214 b = unit_can_reload(u);
216 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
222 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
230 b = unit_can_isolate(u) &&
231 !u->refuse_manual_start;
233 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
239 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
242 _cleanup_free_ char *p = NULL;
248 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
253 p = job_dbus_path(u->job);
257 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
258 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
263 /* No job, so let's fill in some placeholder
264 * data. Since we need to fill in a valid path we
265 * simple point to ourselves. */
267 p = unit_dbus_path(u);
271 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
272 !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 cgb = unit_get_default_cgroup(u);
294 t = cgroup_bonding_to_string(cgb);
300 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
305 return success ? 0 : -ENOMEM;
308 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
313 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
316 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
317 _cleanup_free_ char *t = NULL;
320 t = cgroup_bonding_to_string(cgb);
324 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
329 if (!dbus_message_iter_close_container(i, &sub))
335 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
338 DBusMessageIter sub, sub2;
340 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
343 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
344 _cleanup_free_ char *v = NULL;
347 if (a->semantics && a->semantics->map_write)
348 a->semantics->map_write(a->semantics, a->value, &v);
351 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
352 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
353 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
354 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
355 dbus_message_iter_close_container(&sub, &sub2);
360 if (!dbus_message_iter_close_container(i, &sub))
366 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
374 b = unit_need_daemon_reload(u);
376 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
382 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
384 const char *name, *message;
391 if (u->load_error != 0) {
392 name = bus_errno_to_dbus(u->load_error);
393 message = strempty(strerror(-u->load_error));
397 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
398 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
399 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
400 !dbus_message_iter_close_container(i, &sub))
406 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
407 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
409 JobType job_type = _JOB_TYPE_INVALID;
410 bool reload_if_possible = false;
413 dbus_error_init(&error);
415 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
416 job_type = JOB_START;
417 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
419 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
420 job_type = JOB_RELOAD;
421 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
422 job_type = JOB_RESTART;
423 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
424 job_type = JOB_TRY_RESTART;
425 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
426 reload_if_possible = true;
427 job_type = JOB_RESTART;
428 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
429 reload_if_possible = true;
430 job_type = JOB_TRY_RESTART;
431 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
436 if (!dbus_message_get_args(
439 DBUS_TYPE_STRING, &swho,
440 DBUS_TYPE_INT32, &signo,
442 return bus_send_error_reply(connection, message, &error, -EINVAL);
447 who = kill_who_from_string(swho);
449 return bus_send_error_reply(connection, message, &error, -EINVAL);
452 if (signo <= 0 || signo >= _NSIG)
453 return bus_send_error_reply(connection, message, &error, -EINVAL);
455 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
457 r = unit_kill(u, who, signo, &error);
459 return bus_send_error_reply(connection, message, &error, r);
461 reply = dbus_message_new_method_return(message);
465 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
467 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
469 unit_reset_failed(u);
471 reply = dbus_message_new_method_return(message);
475 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
476 DBusMessageIter iter;
478 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
480 if (!dbus_message_iter_init(message, &iter))
483 r = bus_unit_cgroup_set(u, &iter);
485 return bus_send_error_reply(connection, message, NULL, r);
487 reply = dbus_message_new_method_return(message);
491 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
492 DBusMessageIter iter;
494 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
496 if (!dbus_message_iter_init(message, &iter))
499 r = bus_unit_cgroup_unset(u, &iter);
501 return bus_send_error_reply(connection, message, NULL, r);
503 reply = dbus_message_new_method_return(message);
506 } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
507 DBusMessageIter iter;
508 _cleanup_strv_free_ char **list = NULL;
510 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
512 if (!dbus_message_iter_init(message, &iter))
515 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
517 return bus_send_error_reply(connection, message, NULL, r);
519 reply = dbus_message_new_method_return(message);
523 dbus_message_iter_init_append(reply, &iter);
524 if (bus_append_strv_iter(&iter, list) < 0)
527 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
528 DBusMessageIter iter;
530 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
532 if (!dbus_message_iter_init(message, &iter))
535 r = bus_unit_cgroup_attribute_set(u, &iter);
537 return bus_send_error_reply(connection, message, NULL, r);
539 reply = dbus_message_new_method_return(message);
543 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
544 DBusMessageIter iter;
546 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
548 if (!dbus_message_iter_init(message, &iter))
551 r = bus_unit_cgroup_attribute_unset(u, &iter);
553 return bus_send_error_reply(connection, message, NULL, r);
555 reply = dbus_message_new_method_return(message);
559 } else if (UNIT_VTABLE(u)->bus_message_handler)
560 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
562 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
564 if (job_type != _JOB_TYPE_INVALID) {
568 if (!dbus_message_get_args(
571 DBUS_TYPE_STRING, &smode,
573 return bus_send_error_reply(connection, message, &error, -EINVAL);
575 mode = job_mode_from_string(smode);
577 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
578 return bus_send_error_reply(connection, message, &error, -EINVAL);
581 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
585 if (!bus_maybe_send_reply(connection, message, reply))
588 return DBUS_HANDLER_RESULT_HANDLED;
591 dbus_error_free(&error);
592 return DBUS_HANDLER_RESULT_NEED_MEMORY;
595 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
599 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
606 dbus_error_init(&error);
608 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
609 /* Be nice to gdbus and return introspection data for our mid-level paths */
611 SELINUX_ACCESS_CHECK(connection, message, "status");
613 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
614 char *introspection = NULL;
620 reply = dbus_message_new_method_return(message);
624 /* We roll our own introspection code here, instead of
625 * relying on bus_default_message_handler() because we
626 * need to generate our introspection string
629 f = open_memstream(&introspection, &size);
633 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
636 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
637 fputs(BUS_PEER_INTERFACE, f);
639 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
645 p = bus_path_escape(k);
652 fprintf(f, "<node name=\"%s\"/>", p);
656 fputs("</node>\n", f);
669 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
676 if (!bus_maybe_send_reply(connection, message, reply))
679 return DBUS_HANDLER_RESULT_HANDLED;
682 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
685 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
689 return bus_send_error_reply(connection, message, &error, r);
691 return bus_unit_message_dispatch(u, connection, message);
694 dbus_error_free(&error);
696 return DBUS_HANDLER_RESULT_NEED_MEMORY;
699 const DBusObjectPathVTable bus_unit_vtable = {
700 .message_function = bus_unit_message_handler
703 void bus_unit_send_change_signal(Unit *u) {
704 _cleanup_free_ char *p = NULL;
705 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
709 if (u->in_dbus_queue) {
710 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
711 u->in_dbus_queue = false;
717 if (!bus_has_subscriber(u->manager)) {
718 u->sent_dbus_new_signal = true;
722 p = unit_dbus_path(u);
726 if (u->sent_dbus_new_signal) {
727 /* Send a properties changed signal. First for the
728 * specific type, then for the generic unit. The
729 * clients may rely on this order to get atomic
730 * behavior if needed. */
732 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
734 m = bus_properties_changed_new(p,
735 UNIT_VTABLE(u)->bus_interface,
736 UNIT_VTABLE(u)->bus_invalidating_properties);
740 if (bus_broadcast(u->manager, m) < 0)
743 dbus_message_unref(m);
746 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
747 INVALIDATING_PROPERTIES);
752 /* Send a new signal */
754 m = dbus_message_new_signal("/org/freedesktop/systemd1",
755 "org.freedesktop.systemd1.Manager",
760 if (!dbus_message_append_args(m,
761 DBUS_TYPE_STRING, &u->id,
762 DBUS_TYPE_OBJECT_PATH, &p,
767 if (bus_broadcast(u->manager, m) < 0)
770 u->sent_dbus_new_signal = true;
778 void bus_unit_send_removed_signal(Unit *u) {
779 _cleanup_free_ char *p = NULL;
780 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
784 if (!bus_has_subscriber(u->manager))
787 if (!u->sent_dbus_new_signal)
788 bus_unit_send_change_signal(u);
793 p = unit_dbus_path(u);
797 m = dbus_message_new_signal("/org/freedesktop/systemd1",
798 "org.freedesktop.systemd1.Manager",
803 if (!dbus_message_append_args(m,
804 DBUS_TYPE_STRING, &u->id,
805 DBUS_TYPE_OBJECT_PATH, &p,
809 if (bus_broadcast(u->manager, m) < 0)
818 DBusHandlerResult bus_unit_queue_job(
819 DBusConnection *connection,
820 DBusMessage *message,
824 bool reload_if_possible) {
826 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
827 _cleanup_free_ char *path = NULL;
836 assert(type >= 0 && type < _JOB_TYPE_MAX);
837 assert(mode >= 0 && mode < _JOB_MODE_MAX);
839 dbus_error_init(&error);
841 if (reload_if_possible && unit_can_reload(u)) {
842 if (type == JOB_RESTART)
843 type = JOB_RELOAD_OR_START;
844 else if (type == JOB_TRY_RESTART)
848 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
849 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
850 type == JOB_STOP ? "stop" : "reload");
852 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
853 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
854 return bus_send_error_reply(connection, message, &error, -EPERM);
857 if ((type == JOB_START && u->refuse_manual_start) ||
858 (type == JOB_STOP && u->refuse_manual_stop) ||
859 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
860 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
861 "Operation refused, unit %s may be requested by dependency only.", u->id);
862 return bus_send_error_reply(connection, message, &error, -EPERM);
865 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
867 return bus_send_error_reply(connection, message, &error, r);
869 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
873 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
875 reply = dbus_message_new_method_return(message);
879 path = job_dbus_path(j);
883 if (!dbus_message_append_args(
885 DBUS_TYPE_OBJECT_PATH, &path,
889 if (!bus_maybe_send_reply(connection, message, reply))
892 return DBUS_HANDLER_RESULT_HANDLED;
895 dbus_error_free(&error);
897 return DBUS_HANDLER_RESULT_NEED_MEMORY;
900 static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
907 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
911 if (streq(mode, "runtime"))
913 else if (streq(mode, "persistent"))
921 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
922 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
931 if (!unit_get_exec_context(u))
934 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
938 r = parse_mode(iter, &runtime, false);
942 r = cg_split_spec(name, &controller, &new_path);
947 new_path = unit_default_cgroup_path(u);
952 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
955 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
957 if (streq(b->path, new_path))
963 old_path = strdup(b->path);
968 r = unit_add_cgroup_from_text(u, name, true, &b);
974 /* Try to move things to the new place, and clean up the old place */
975 cgroup_bonding_realize(b);
976 cgroup_bonding_migrate(b, u->cgroup_bondings);
979 cg_trim(controller, old_path, true);
981 /* Apply the attributes to the new group */
982 LIST_FOREACH(by_unit, a, u->cgroup_attributes)
983 if (streq(a->controller, controller))
984 cgroup_attribute_apply(a, b);
987 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
988 "ControlGroup=", name, "\n", NULL);
992 return unit_write_drop_in(u, runtime, controller, contents);
995 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
996 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
998 CGroupAttribute *a, *n;
1006 if (!unit_get_exec_context(u))
1009 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1013 r = parse_mode(iter, &runtime, false);
1017 r = cg_split_spec(name, &controller, &path);
1021 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1024 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1028 if (path && !path_equal(path, b->path))
1034 unit_remove_drop_in(u, runtime, controller);
1036 /* Try to migrate the old group away */
1037 if (cg_pid_get_path(controller, 0, &target) >= 0)
1038 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1040 cgroup_bonding_free(b, true);
1042 /* Drop all attributes of this controller */
1043 LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
1044 if (!streq(a->controller, controller))
1047 unit_remove_drop_in(u, runtime, a->name);
1048 cgroup_attribute_free(a);
1054 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1055 _cleanup_free_ char *controller = NULL;
1066 if (!unit_get_exec_context(u))
1069 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
1073 r = cg_controller_from_attr(name, &controller);
1077 /* First attempt, read the value from the kernel */
1078 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1080 _cleanup_free_ char *p = NULL, *v = NULL;
1082 r = cg_get_path(b->controller, b->path, name, &p);
1086 r = read_full_file(p, &v, NULL);
1088 /* Split on new lines */
1089 l = strv_split_newlines(v);
1099 /* If that didn't work, read our cached value */
1100 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1102 if (!cgroup_attribute_matches(a, controller, name))
1105 r = strv_extend(&l, a->value);
1119 static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
1120 _cleanup_free_ char *buf = NULL;
1126 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1127 if (!cgroup_attribute_matches(a, NULL, name))
1131 buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1132 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1140 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1151 return unit_write_drop_in(u, runtime, name, buf);
1153 return unit_remove_drop_in(u, runtime, name);
1156 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1157 _cleanup_strv_free_ char **l = NULL;
1159 bool runtime = false;
1166 if (!unit_get_exec_context(u))
1169 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1173 r = bus_parse_strv_iter(iter, &l);
1177 if (!dbus_message_iter_next(iter))
1180 r = parse_mode(iter, &runtime, false);
1184 STRV_FOREACH(value, l) {
1185 _cleanup_free_ char *v = NULL;
1187 const CGroupSemantics *s;
1189 r = cgroup_semantics_find(NULL, name, *value, &v, &s);
1193 if (s && !s->multiple && l[1])
1196 r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
1203 b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1205 /* Doesn't exist yet? Then let's add it */
1206 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1211 cgroup_bonding_realize(b);
1212 cgroup_bonding_migrate(b, u->cgroup_bondings);
1217 cgroup_attribute_apply(a, u->cgroup_bondings);
1222 r = update_attribute_drop_in(u, runtime, name);
1229 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1237 if (!unit_get_exec_context(u))
1240 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1244 r = parse_mode(iter, &runtime, false);
1248 cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
1249 update_attribute_drop_in(u, runtime, name);
1254 const BusProperty bus_unit_properties[] = {
1255 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1256 { "Names", bus_unit_append_names, "as", 0 },
1257 { "Following", bus_unit_append_following, "s", 0 },
1258 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1259 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1260 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1261 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1262 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1263 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1264 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1265 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1266 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1267 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1268 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1269 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1270 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1271 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1272 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1273 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1274 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1275 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1276 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1277 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1278 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1279 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1280 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1281 { "Description", bus_unit_append_description, "s", 0 },
1282 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1283 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1284 { "SubState", bus_unit_append_sub_state, "s", 0 },
1285 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1286 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1287 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1288 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1289 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1290 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1291 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1292 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1293 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1294 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1295 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1296 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1297 { "CanStart", bus_unit_append_can_start, "b", 0 },
1298 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1299 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1300 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1301 { "Job", bus_unit_append_job, "(uo)", 0 },
1302 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1303 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1304 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1305 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1306 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1307 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1308 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1309 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1310 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1311 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1312 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1313 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1314 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1315 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1319 const BusProperty bus_unit_cgroup_properties[] = {
1320 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
1321 { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
1322 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },