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_slice(DBusMessageIter *i, const char *property, void *data) {
92 if (UNIT_DEREF(u->slice))
93 d = UNIT_DEREF(u->slice)->id;
97 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
103 static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
109 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
113 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
116 if (!dbus_message_iter_close_container(i, &sub))
122 static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
130 d = unit_description(u);
132 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
138 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
140 static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
148 state = unit_active_state_to_string(unit_active_state(u));
150 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
156 static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
164 state = unit_sub_state_to_string(u);
166 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
172 static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
180 state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
182 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
188 static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
196 b = unit_can_start(u) &&
197 !u->refuse_manual_start;
199 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
205 static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
213 /* On the lower levels we assume that every unit we can start
214 * we can also stop */
216 b = unit_can_start(u) &&
217 !u->refuse_manual_stop;
219 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
225 static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
233 b = unit_can_reload(u);
235 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
241 static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
249 b = unit_can_isolate(u) &&
250 !u->refuse_manual_start;
252 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
258 static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
261 _cleanup_free_ char *p = NULL;
267 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
272 p = job_dbus_path(u->job);
276 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
277 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
282 /* No job, so let's fill in some placeholder
283 * data. Since we need to fill in a valid path we
284 * simple point to ourselves. */
286 p = unit_dbus_path(u);
290 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
291 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
295 if (!dbus_message_iter_close_container(i, &sub))
301 static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
311 cgb = unit_get_default_cgroup(u);
313 t = cgroup_bonding_to_string(cgb);
319 success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
324 return success ? 0 : -ENOMEM;
327 static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
332 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
335 LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
336 _cleanup_free_ char *t = NULL;
339 t = cgroup_bonding_to_string(cgb);
343 success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
348 if (!dbus_message_iter_close_container(i, &sub))
354 static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
357 DBusMessageIter sub, sub2;
359 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
362 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
363 _cleanup_free_ char *v = NULL;
366 if (a->semantics && a->semantics->map_write)
367 a->semantics->map_write(a->semantics, a->value, &v);
370 dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
371 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
372 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
373 dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
374 dbus_message_iter_close_container(&sub, &sub2);
379 if (!dbus_message_iter_close_container(i, &sub))
385 static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
393 b = unit_need_daemon_reload(u);
395 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
401 static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
403 const char *name, *message;
410 if (u->load_error != 0) {
411 name = bus_errno_to_dbus(u->load_error);
412 message = strempty(strerror(-u->load_error));
416 if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
417 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
418 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
419 !dbus_message_iter_close_container(i, &sub))
425 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
426 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
428 JobType job_type = _JOB_TYPE_INVALID;
429 bool reload_if_possible = false;
432 dbus_error_init(&error);
434 if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
435 job_type = JOB_START;
436 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
438 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
439 job_type = JOB_RELOAD;
440 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
441 job_type = JOB_RESTART;
442 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
443 job_type = JOB_TRY_RESTART;
444 else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
445 reload_if_possible = true;
446 job_type = JOB_RESTART;
447 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
448 reload_if_possible = true;
449 job_type = JOB_TRY_RESTART;
450 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
455 if (!dbus_message_get_args(
458 DBUS_TYPE_STRING, &swho,
459 DBUS_TYPE_INT32, &signo,
461 return bus_send_error_reply(connection, message, &error, -EINVAL);
466 who = kill_who_from_string(swho);
468 return bus_send_error_reply(connection, message, &error, -EINVAL);
471 if (signo <= 0 || signo >= _NSIG)
472 return bus_send_error_reply(connection, message, &error, -EINVAL);
474 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
476 r = unit_kill(u, who, signo, &error);
478 return bus_send_error_reply(connection, message, &error, r);
480 reply = dbus_message_new_method_return(message);
484 } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
486 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
488 unit_reset_failed(u);
490 reply = dbus_message_new_method_return(message);
494 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
495 DBusMessageIter iter;
497 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
499 if (!dbus_message_iter_init(message, &iter))
502 r = bus_unit_cgroup_set(u, &iter);
504 return bus_send_error_reply(connection, message, NULL, r);
506 reply = dbus_message_new_method_return(message);
510 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
511 DBusMessageIter iter;
513 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
515 if (!dbus_message_iter_init(message, &iter))
518 r = bus_unit_cgroup_unset(u, &iter);
520 return bus_send_error_reply(connection, message, NULL, r);
522 reply = dbus_message_new_method_return(message);
525 } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
526 DBusMessageIter iter;
527 _cleanup_strv_free_ char **list = NULL;
529 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
531 if (!dbus_message_iter_init(message, &iter))
534 r = bus_unit_cgroup_attribute_get(u, &iter, &list);
536 return bus_send_error_reply(connection, message, NULL, r);
538 reply = dbus_message_new_method_return(message);
542 dbus_message_iter_init_append(reply, &iter);
543 if (bus_append_strv_iter(&iter, list) < 0)
546 } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
547 DBusMessageIter iter;
549 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
551 if (!dbus_message_iter_init(message, &iter))
554 r = bus_unit_cgroup_attribute_set(u, &iter);
556 return bus_send_error_reply(connection, message, NULL, r);
558 reply = dbus_message_new_method_return(message);
562 } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
563 DBusMessageIter iter;
565 SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
567 if (!dbus_message_iter_init(message, &iter))
570 r = bus_unit_cgroup_attribute_unset(u, &iter);
572 return bus_send_error_reply(connection, message, NULL, r);
574 reply = dbus_message_new_method_return(message);
578 } else if (UNIT_VTABLE(u)->bus_message_handler)
579 return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
581 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
583 if (job_type != _JOB_TYPE_INVALID) {
587 if (!dbus_message_get_args(
590 DBUS_TYPE_STRING, &smode,
592 return bus_send_error_reply(connection, message, &error, -EINVAL);
594 mode = job_mode_from_string(smode);
596 dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
597 return bus_send_error_reply(connection, message, &error, -EINVAL);
600 return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
604 if (!bus_maybe_send_reply(connection, message, reply))
607 return DBUS_HANDLER_RESULT_HANDLED;
610 dbus_error_free(&error);
611 return DBUS_HANDLER_RESULT_NEED_MEMORY;
614 static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
618 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
625 dbus_error_init(&error);
627 if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
628 /* Be nice to gdbus and return introspection data for our mid-level paths */
630 SELINUX_ACCESS_CHECK(connection, message, "status");
632 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
633 char *introspection = NULL;
639 reply = dbus_message_new_method_return(message);
643 /* We roll our own introspection code here, instead of
644 * relying on bus_default_message_handler() because we
645 * need to generate our introspection string
648 f = open_memstream(&introspection, &size);
652 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
655 fputs(BUS_INTROSPECTABLE_INTERFACE, f);
656 fputs(BUS_PEER_INTERFACE, f);
658 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
664 p = bus_path_escape(k);
671 fprintf(f, "<node name=\"%s\"/>", p);
675 fputs("</node>\n", f);
688 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
695 if (!bus_maybe_send_reply(connection, message, reply))
698 return DBUS_HANDLER_RESULT_HANDLED;
701 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
704 r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
708 return bus_send_error_reply(connection, message, &error, r);
710 return bus_unit_message_dispatch(u, connection, message);
713 dbus_error_free(&error);
715 return DBUS_HANDLER_RESULT_NEED_MEMORY;
718 const DBusObjectPathVTable bus_unit_vtable = {
719 .message_function = bus_unit_message_handler
722 void bus_unit_send_change_signal(Unit *u) {
723 _cleanup_free_ char *p = NULL;
724 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
728 if (u->in_dbus_queue) {
729 LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
730 u->in_dbus_queue = false;
736 if (!bus_has_subscriber(u->manager)) {
737 u->sent_dbus_new_signal = true;
741 p = unit_dbus_path(u);
745 if (u->sent_dbus_new_signal) {
746 /* Send a properties changed signal. First for the
747 * specific type, then for the generic unit. The
748 * clients may rely on this order to get atomic
749 * behavior if needed. */
751 if (UNIT_VTABLE(u)->bus_invalidating_properties) {
753 m = bus_properties_changed_new(p,
754 UNIT_VTABLE(u)->bus_interface,
755 UNIT_VTABLE(u)->bus_invalidating_properties);
759 if (bus_broadcast(u->manager, m) < 0)
762 dbus_message_unref(m);
765 m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
766 INVALIDATING_PROPERTIES);
771 /* Send a new signal */
773 m = dbus_message_new_signal("/org/freedesktop/systemd1",
774 "org.freedesktop.systemd1.Manager",
779 if (!dbus_message_append_args(m,
780 DBUS_TYPE_STRING, &u->id,
781 DBUS_TYPE_OBJECT_PATH, &p,
786 if (bus_broadcast(u->manager, m) < 0)
789 u->sent_dbus_new_signal = true;
797 void bus_unit_send_removed_signal(Unit *u) {
798 _cleanup_free_ char *p = NULL;
799 _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
803 if (!bus_has_subscriber(u->manager))
806 if (!u->sent_dbus_new_signal)
807 bus_unit_send_change_signal(u);
812 p = unit_dbus_path(u);
816 m = dbus_message_new_signal("/org/freedesktop/systemd1",
817 "org.freedesktop.systemd1.Manager",
822 if (!dbus_message_append_args(m,
823 DBUS_TYPE_STRING, &u->id,
824 DBUS_TYPE_OBJECT_PATH, &p,
828 if (bus_broadcast(u->manager, m) < 0)
837 DBusHandlerResult bus_unit_queue_job(
838 DBusConnection *connection,
839 DBusMessage *message,
843 bool reload_if_possible) {
845 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
846 _cleanup_free_ char *path = NULL;
855 assert(type >= 0 && type < _JOB_TYPE_MAX);
856 assert(mode >= 0 && mode < _JOB_MODE_MAX);
858 dbus_error_init(&error);
860 if (reload_if_possible && unit_can_reload(u)) {
861 if (type == JOB_RESTART)
862 type = JOB_RELOAD_OR_START;
863 else if (type == JOB_TRY_RESTART)
867 SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
868 (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
869 type == JOB_STOP ? "stop" : "reload");
871 if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
872 dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
873 return bus_send_error_reply(connection, message, &error, -EPERM);
876 if ((type == JOB_START && u->refuse_manual_start) ||
877 (type == JOB_STOP && u->refuse_manual_stop) ||
878 ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
879 dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
880 "Operation refused, unit %s may be requested by dependency only.", u->id);
881 return bus_send_error_reply(connection, message, &error, -EPERM);
884 r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
886 return bus_send_error_reply(connection, message, &error, r);
888 cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
892 LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
894 reply = dbus_message_new_method_return(message);
898 path = job_dbus_path(j);
902 if (!dbus_message_append_args(
904 DBUS_TYPE_OBJECT_PATH, &path,
908 if (!bus_maybe_send_reply(connection, message, reply))
911 return DBUS_HANDLER_RESULT_HANDLED;
914 dbus_error_free(&error);
916 return DBUS_HANDLER_RESULT_NEED_MEMORY;
919 static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
926 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
930 if (streq(mode, "runtime"))
932 else if (streq(mode, "persistent"))
940 int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
941 _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
950 if (!unit_get_exec_context(u))
953 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
957 r = parse_mode(iter, &runtime, false);
961 r = cg_split_spec(name, &controller, &new_path);
966 new_path = unit_default_cgroup_path(u);
971 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
974 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
976 if (streq(b->path, new_path))
982 old_path = strdup(b->path);
987 r = unit_add_cgroup_from_text(u, name, true, &b);
993 /* Try to move things to the new place, and clean up the old place */
994 cgroup_bonding_realize(b);
995 cgroup_bonding_migrate(b, u->cgroup_bondings);
998 cg_trim(controller, old_path, true);
1000 /* Apply the attributes to the new group */
1001 LIST_FOREACH(by_unit, a, u->cgroup_attributes)
1002 if (streq(a->controller, controller))
1003 cgroup_attribute_apply(a, b);
1006 contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1007 "ControlGroup=", name, "\n", NULL);
1011 return unit_write_drop_in(u, runtime, controller, contents);
1014 int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
1015 _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
1017 CGroupAttribute *a, *n;
1025 if (!unit_get_exec_context(u))
1028 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1032 r = parse_mode(iter, &runtime, false);
1036 r = cg_split_spec(name, &controller, &path);
1040 if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1043 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1047 if (path && !path_equal(path, b->path))
1053 unit_remove_drop_in(u, runtime, controller);
1055 /* Try to migrate the old group away */
1056 if (cg_pid_get_path(controller, 0, &target) >= 0)
1057 cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
1059 cgroup_bonding_free(b, true);
1061 /* Drop all attributes of this controller */
1062 LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
1063 if (!streq(a->controller, controller))
1066 unit_remove_drop_in(u, runtime, a->name);
1067 cgroup_attribute_free(a);
1073 int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
1074 _cleanup_free_ char *controller = NULL;
1085 if (!unit_get_exec_context(u))
1088 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
1092 r = cg_controller_from_attr(name, &controller);
1096 /* First attempt, read the value from the kernel */
1097 b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
1099 _cleanup_free_ char *p = NULL, *v = NULL;
1101 r = cg_get_path(b->controller, b->path, name, &p);
1105 r = read_full_file(p, &v, NULL);
1107 /* Split on new lines */
1108 l = strv_split_newlines(v);
1118 /* If that didn't work, read our cached value */
1119 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1121 if (!cgroup_attribute_matches(a, controller, name))
1124 r = strv_extend(&l, a->value);
1138 static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
1139 _cleanup_free_ char *buf = NULL;
1145 LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
1146 if (!cgroup_attribute_matches(a, NULL, name))
1150 buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
1151 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1159 "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
1170 return unit_write_drop_in(u, runtime, name, buf);
1172 return unit_remove_drop_in(u, runtime, name);
1175 int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
1176 _cleanup_strv_free_ char **l = NULL;
1178 bool runtime = false;
1185 if (!unit_get_exec_context(u))
1188 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1192 r = bus_parse_strv_iter(iter, &l);
1196 if (!dbus_message_iter_next(iter))
1199 r = parse_mode(iter, &runtime, false);
1203 STRV_FOREACH(value, l) {
1204 _cleanup_free_ char *v = NULL;
1206 const CGroupSemantics *s;
1208 r = cgroup_semantics_find(NULL, name, *value, &v, &s);
1212 if (s && !s->multiple && l[1])
1215 r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
1222 b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
1224 /* Doesn't exist yet? Then let's add it */
1225 r = unit_add_cgroup_from_text(u, a->controller, false, &b);
1230 cgroup_bonding_realize(b);
1231 cgroup_bonding_migrate(b, u->cgroup_bondings);
1236 cgroup_attribute_apply(a, u->cgroup_bondings);
1241 r = update_attribute_drop_in(u, runtime, name);
1248 int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
1256 if (!unit_get_exec_context(u))
1259 r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
1263 r = parse_mode(iter, &runtime, false);
1267 cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
1268 update_attribute_drop_in(u, runtime, name);
1273 const BusProperty bus_unit_properties[] = {
1274 { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
1275 { "Names", bus_unit_append_names, "as", 0 },
1276 { "Following", bus_unit_append_following, "s", 0 },
1277 { "Slice", bus_unit_append_slice, "s", 0 },
1278 { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
1279 { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
1280 { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
1281 { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
1282 { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
1283 { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
1284 { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
1285 { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
1286 { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
1287 { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
1288 { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
1289 { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
1290 { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
1291 { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
1292 { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
1293 { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
1294 { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
1295 { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
1296 { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
1297 { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
1298 { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
1299 { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
1300 { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
1301 { "Description", bus_unit_append_description, "s", 0 },
1302 { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
1303 { "ActiveState", bus_unit_append_active_state, "s", 0 },
1304 { "SubState", bus_unit_append_sub_state, "s", 0 },
1305 { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
1306 { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
1307 { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
1308 { "UnitFileState", bus_unit_append_file_state, "s", 0 },
1309 { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
1310 { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
1311 { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
1312 { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
1313 { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
1314 { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
1315 { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
1316 { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
1317 { "CanStart", bus_unit_append_can_start, "b", 0 },
1318 { "CanStop", bus_unit_append_can_stop, "b", 0 },
1319 { "CanReload", bus_unit_append_can_reload, "b", 0 },
1320 { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
1321 { "Job", bus_unit_append_job, "(uo)", 0 },
1322 { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
1323 { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
1324 { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
1325 { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
1326 { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
1327 { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
1328 { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
1329 { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
1330 { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
1331 { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
1332 { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
1333 { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
1334 { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
1335 { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
1339 const BusProperty bus_unit_cgroup_properties[] = {
1340 { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
1341 { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
1342 { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },