diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-01-12 04:24:12 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-01-14 21:24:57 +0100 |
commit | 246aa6dd9dcea84bb945d16ec86e69f869dbb9b4 (patch) | |
tree | 375b4ff2acb7f18461a7f1c44167575fa58e8a97 | |
parent | systemctl: honour inhibitors only when running unprivileged (diff) | |
download | systemd-246aa6dd9dcea84bb945d16ec86e69f869dbb9b4.tar.xz systemd-246aa6dd9dcea84bb945d16ec86e69f869dbb9b4.zip |
core: add bus API and systemctl commands for altering cgroup parameters during runtime
-rw-r--r-- | src/core/cgroup-attr.c | 33 | ||||
-rw-r--r-- | src/core/cgroup-attr.h | 3 | ||||
-rw-r--r-- | src/core/cgroup.c | 32 | ||||
-rw-r--r-- | src/core/cgroup.h | 3 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 131 | ||||
-rw-r--r-- | src/core/dbus-mount.c | 2 | ||||
-rw-r--r-- | src/core/dbus-service.c | 2 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 2 | ||||
-rw-r--r-- | src/core/dbus-swap.c | 2 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 250 | ||||
-rw-r--r-- | src/core/dbus-unit.h | 31 | ||||
-rw-r--r-- | src/core/load-fragment.c | 16 | ||||
-rw-r--r-- | src/core/unit.c | 134 | ||||
-rw-r--r-- | src/core/unit.h | 4 | ||||
-rw-r--r-- | src/shared/cgroup-util.c | 116 | ||||
-rw-r--r-- | src/shared/cgroup-util.h | 4 | ||||
-rw-r--r-- | src/shared/strv.h | 4 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 114 | ||||
-rw-r--r-- | src/test/test-cgroup.c | 2 |
19 files changed, 757 insertions, 128 deletions
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c index 71af09cf87..cedf37de50 100644 --- a/src/core/cgroup-attr.c +++ b/src/core/cgroup-attr.c @@ -71,23 +71,42 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) { return r; } -CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) { +CGroupAttribute *cgroup_attribute_find_list( + CGroupAttribute *first, + const char *controller, + const char *name) { CGroupAttribute *a; - assert(controller); assert(name); - LIST_FOREACH(by_unit, a, first) - if (streq(a->controller, controller) && - streq(a->name, name)) - return a; + LIST_FOREACH(by_unit, a, first) { + + + if (controller) { + if (streq(a->controller, controller) && streq(a->name, name)) + return a; + + } else if (streq(a->name, name)) { + size_t x, y; + x = strlen(a->controller); + y = strlen(name); + + if (y > x && + memcmp(a->controller, name, x) == 0 && + name[x] == '.') + return a; + } + } return NULL; } -static void cgroup_attribute_free(CGroupAttribute *a) { +void cgroup_attribute_free(CGroupAttribute *a) { assert(a); + if (a->unit) + LIST_REMOVE(CGroupAttribute, by_unit, a->unit->cgroup_attributes, a); + free(a->controller); free(a->name); free(a->value); diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h index 2b754eac40..0f5b854898 100644 --- a/src/core/cgroup-attr.h +++ b/src/core/cgroup-attr.h @@ -33,6 +33,8 @@ struct CGroupAttribute { char *name; char *value; + Unit *unit; + CGroupAttributeMapCallback map_callback; LIST_FIELDS(CGroupAttribute, by_unit); @@ -43,4 +45,5 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b); CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name); +void cgroup_attribute_free(CGroupAttribute *a); void cgroup_attribute_free_list(CGroupAttribute *first); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 8fc1731485..4790a09ff2 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -110,7 +110,6 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) { cgroup_bonding_trim(b, delete_root); } - int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) { char *p = NULL; const char *path; @@ -151,6 +150,34 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgr return 0; } +int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) { + CGroupBonding *q; + int ret = 0; + + LIST_FOREACH(by_unit, q, list) { + int r; + + if (q == b) + continue; + + if (!q->ours) + continue; + + r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false); + if (r < 0 && ret == 0) + ret = r; + } + + return ret; +} + +int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) { + assert(b); + assert(target); + + return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem); +} + int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) { assert(b); @@ -520,7 +547,8 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) { CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) { CGroupBonding *b; - assert(controller); + if (!controller) + controller = SYSTEMD_CGROUP_CONTROLLER; LIST_FOREACH(by_unit, b, first) if (streq(b->controller, controller)) diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 229da52ba4..2ff39e5767 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -58,6 +58,9 @@ void cgroup_bonding_free_list(CGroupBonding *first, bool trim); int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *suffix); int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *suffix); +int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list); +int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem); + int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 2d9cea676f..1d785a2347 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -102,6 +102,26 @@ " <method name=\"ResetFailedUnit\">\n" \ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ " </method>\n" \ + " <method name=\"SetUnitControlGroups\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"UnsetUnitControlGroups\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \ + " </method>\n" \ + " <method name=\"SetUnitControlGroupAttributes\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"attributes\" type=\"a(sss)\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \ + " </method>\n" \ + " <method name=\"UnsetUnitControlGroupAttributes\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ " <method name=\"GetJob\">\n" \ " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \ @@ -848,6 +868,117 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, if (!reply) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroups")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + r = bus_unit_cgroup_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroups")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + r = bus_unit_cgroup_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttributes")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + r = bus_unit_cgroup_attribute_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttributes")) { + const char *name; + Unit *u; + DBusMessageIter iter; + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + u = manager_get_unit(m, name); + if (!u) { + dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name); + return bus_send_error_reply(connection, message, &error, -ENOENT); + } + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + r = bus_unit_cgroup_attribute_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) { DBusMessageIter iter, sub; Iterator i; diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index d81edeb807..0fcceb500d 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -40,6 +40,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ @@ -159,6 +160,7 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess { "org.freedesktop.systemd1.Mount", bus_mount_properties, m }, { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context }, { "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context }, + { "org.freedesktop.systemd1.Mount", bus_unit_cgroup_properties, u }, { NULL, } }; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index d99058dd46..e06a5dce97 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -50,6 +50,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \ @@ -152,6 +153,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio { "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context }, { "org.freedesktop.systemd1.Service", bus_kill_context_properties, &s->kill_context }, { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status }, + { "org.freedesktop.systemd1.Service", bus_unit_cgroup_properties, u }, { NULL, } }; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 095a031612..2092a63694 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -39,6 +39,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \ @@ -142,6 +143,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes { "org.freedesktop.systemd1.Socket", bus_socket_properties, s }, { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context }, { "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context }, + { "org.freedesktop.systemd1.Socket", bus_unit_properties, u }, { NULL, } }; diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 67ea0f24fe..2e99fba7db 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -38,6 +38,7 @@ BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \ BUS_EXEC_CONTEXT_INTERFACE \ BUS_KILL_CONTEXT_INTERFACE \ + BUS_UNIT_CGROUP_INTERFACE \ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \ " </interface>\n" @@ -106,6 +107,7 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa { "org.freedesktop.systemd1.Swap", bus_swap_properties, s }, { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context }, { "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context }, + { "org.freedesktop.systemd1.Swap", bus_unit_cgroup_properties, u }, { NULL, } }; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 8433a720b2..c7bf043764 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -27,6 +27,9 @@ #include "bus-errors.h" #include "dbus-common.h" #include "selinux-access.h" +#include "cgroup-util.h" +#include "strv.h" +#include "path-util.h" const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE; @@ -468,6 +471,69 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn if (!reply) goto oom; + } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroups")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroups")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttributes")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_set(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttributes")) { + DBusMessageIter iter; + + SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop"); + + if (!dbus_message_iter_init(message, &iter)) + goto oom; + + r = bus_unit_cgroup_attribute_unset(u, &iter); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + } else if (UNIT_VTABLE(u)->bus_message_handler) return UNIT_VTABLE(u)->bus_message_handler(u, connection, message); else @@ -809,6 +875,180 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } +int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) { + int r; + _cleanup_strv_free_ char **a = NULL; + char **name; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_parse_strv_iter(iter, &a); + if (r < 0) + return r; + + STRV_FOREACH(name, a) { + _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL; + CGroupBonding *b; + + r = cg_split_spec(*name, &controller, &new_path); + if (r < 0) + return r; + + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + old_path = strdup(b->path); + if (!old_path) + return -ENOMEM; + } + + r = unit_add_cgroup_from_text(u, *name, true, &b); + if (r < 0) + return r; + + if (r > 0) { + /* Try to move things to the new place, and clean up the old place */ + cgroup_bonding_realize(b); + cgroup_bonding_migrate(b, u->cgroup_bondings); + + if (old_path) + cg_trim(controller, old_path, true); + } + } + + return 0; +} + +int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) { + _cleanup_strv_free_ char **a = NULL; + char **name; + int r; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_parse_strv_iter(iter, &a); + if (r < 0) + return r; + + STRV_FOREACH(name, a) { + _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL; + CGroupBonding *b; + + r = cg_split_spec(*name, &controller, &path); + if (r < 0) + return r; + + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (!b) + continue; + + if (path && !path_equal(path, b->path)) + continue; + + if (b->essential) + return -EINVAL; + + /* Try to migrate the old group away */ + if (cg_get_by_pid(controller, 0, &target) >= 0) + cgroup_bonding_migrate_to(u->cgroup_bondings, target, false); + + cgroup_bonding_free(b, true); + } + + return 0; +} + +int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) { + DBusMessageIter sub, sub2; + int r; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT) + return -EINVAL; + + dbus_message_iter_recurse(iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name, *value; + CGroupAttribute *a; + + assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT); + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) < 0) + return -EINVAL; + + dbus_message_iter_next(&sub); + + r = unit_add_cgroup_attribute(u, NULL, name, value, NULL, &a); + if (r < 0) + return r; + + if (r > 0) { + CGroupBonding *b; + + b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller); + if (!b) { + /* Doesn't exist yet? Then let's add it */ + r = unit_add_cgroup_from_text(u, a->controller, false, &b); + if (r < 0) + return r; + + if (r > 0) { + cgroup_bonding_realize(b); + cgroup_bonding_migrate(b, u->cgroup_bondings); + } + } + + /* Make it count */ + cgroup_attribute_apply(a, u->cgroup_bondings); + } + } + + return 0; +} + +int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) { + _cleanup_strv_free_ char **l = NULL; + char **name; + int r; + + assert(u); + assert(iter); + + if (!unit_get_exec_context(u)) + return -EINVAL; + + r = bus_parse_strv_iter(iter, &l); + if (r < 0) + return r; + + STRV_FOREACH(name, l) { + CGroupAttribute *a; + + a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name); + if (a) + cgroup_attribute_free(a); + } + + return 0; +} + const BusProperty bus_unit_properties[] = { { "Id", bus_property_append_string, "s", offsetof(Unit, id), true }, { "Names", bus_unit_append_names, "as", 0 }, @@ -864,9 +1104,6 @@ const BusProperty bus_unit_properties[] = { { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) }, { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) }, { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) }, - { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, - { "ControlGroup", bus_unit_append_cgroups, "as", 0 }, - { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 }, { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 }, { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) }, { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) }, @@ -875,3 +1112,10 @@ const BusProperty bus_unit_properties[] = { { "LoadError", bus_unit_append_load_error, "(ss)", 0 }, { NULL, } }; + +const BusProperty bus_unit_cgroup_properties[] = { + { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 }, + { "ControlGroups", bus_unit_append_cgroups, "as", 0 }, + { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 }, + { NULL, } +}; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index ac6785a949..7b8c5a9442 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -113,9 +113,6 @@ " <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \ - " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ - " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \ " <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \ @@ -124,16 +121,37 @@ " <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \ " </interface>\n" +#define BUS_UNIT_CGROUP_INTERFACE \ + " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \ + " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \ + " <method name=\"SetControlGroups\">\n" \ + " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"UnsetControlGroups\">\n" \ + " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"SetControlGroupAttributes\">\n" \ + " <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"UnsetControlGroupAttributes\">\n" \ + " <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \ + " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" + #define BUS_UNIT_INTERFACES_LIST \ BUS_GENERIC_INTERFACES_LIST \ "org.freedesktop.systemd1.Unit\0" extern const BusProperty bus_unit_properties[]; +extern const BusProperty bus_unit_cgroup_properties[]; void bus_unit_send_change_signal(Unit *u); void bus_unit_send_removed_signal(Unit *u); - DBusHandlerResult bus_unit_queue_job( DBusConnection *connection, DBusMessage *message, @@ -142,6 +160,11 @@ DBusHandlerResult bus_unit_queue_job( JobMode mode, bool reload_if_possible); +int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter); +int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter); +int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter); +int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter); + extern const DBusObjectPathVTable bus_unit_vtable; extern const char bus_unit_interface[]; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e35fdbc5ec..4d1154e408 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -984,7 +984,7 @@ int config_parse_unit_cgroup( if (!ku) return -ENOMEM; - r = unit_add_cgroup_from_text(u, ku); + r = unit_add_cgroup_from_text(u, ku, true, NULL); if (r < 0) { log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s", filename, line, k, rvalue); @@ -1659,7 +1659,7 @@ int config_parse_unit_cgroup_attr( return 0; } - r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL); + r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL, NULL); strv_free(l); if (r < 0) { @@ -1689,7 +1689,7 @@ int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char if (asprintf(&t, "%lu", ul) < 0) return -ENOMEM; - r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL); + r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL, NULL); free(t); if (r < 0) { @@ -1722,7 +1722,7 @@ int config_parse_unit_memory_limit(const char *filename, unsigned line, const ch r = unit_add_cgroup_attribute(u, "memory", streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes", - t, NULL); + t, NULL, NULL); free(t); if (r < 0) { @@ -1821,7 +1821,7 @@ int config_parse_unit_device_allow(const char *filename, unsigned line, const ch r = unit_add_cgroup_attribute(u, "devices", streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny", - rvalue, device_map); + rvalue, device_map, NULL); if (r < 0) { log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue); @@ -1931,9 +1931,9 @@ int config_parse_unit_blkio_weight(const char *filename, unsigned line, const ch return -ENOMEM; if (device) - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map); + r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map, NULL); else - r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL); + r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL, NULL); free(t); if (r < 0) { @@ -1987,7 +1987,7 @@ int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const r = unit_add_cgroup_attribute(u, "blkio", streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device", - t, blkio_map); + t, blkio_map, NULL); free(t); if (r < 0) { diff --git a/src/core/unit.c b/src/core/unit.c index f00cfedb89..1194c524bf 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1941,8 +1941,9 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { assert(b->path); if (!b->controller) { - if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER))) - return -ENOMEM; + b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER); + if (!b->controller) + return log_oom(); b->ours = true; } @@ -1956,7 +1957,8 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { l = hashmap_get(u->manager->cgroup_bondings, b->path); LIST_PREPEND(CGroupBonding, by_path, l, b); - if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) { + r = hashmap_replace(u->manager->cgroup_bondings, b->path, l); + if (r < 0) { LIST_REMOVE(CGroupBonding, by_path, l, b); return r; } @@ -1969,26 +1971,21 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) { } char *unit_default_cgroup_path(Unit *u) { - char *p; - assert(u); if (u->instance) { - char *t; + _cleanup_free_ char *t = NULL; t = unit_name_template(u->id); if (!t) return NULL; - p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL); - free(t); + return strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL); } else - p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL); - - return p; + return strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL); } -int unit_add_cgroup_from_text(Unit *u, const char *name) { +int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) { char *controller = NULL, *path = NULL; CGroupBonding *b = NULL; bool ours = false; @@ -1997,7 +1994,8 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { assert(u); assert(name); - if ((r = cg_split_spec(name, &controller, &path)) < 0) + r = cg_split_spec(name, &controller, &path); + if (r < 0) return r; if (!path) { @@ -2013,16 +2011,42 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { if (!path || !controller) { free(path); free(controller); - - return -ENOMEM; + return log_oom(); } - if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) { + b = cgroup_bonding_find_list(u->cgroup_bondings, controller); + if (b) { + if (streq(path, b->path)) { + free(path); + free(controller); + + if (ret) + *ret = b; + return 0; + } + + if (overwrite && !b->essential) { + free(controller); + + free(b->path); + b->path = path; + + b->ours = ours; + b->realized = false; + + if (ret) + *ret = b; + + return 1; + } + r = -EEXIST; + b = NULL; goto fail; } - if (!(b = new0(CGroupBonding, 1))) { + b = new0(CGroupBonding, 1); + if (!b) { r = -ENOMEM; goto fail; } @@ -2032,10 +2056,14 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) { b->ours = ours; b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - if ((r = unit_add_cgroup(u, b)) < 0) + r = unit_add_cgroup(u, b); + if (r < 0) goto fail; - return 0; + if (ret) + *ret = b; + + return 1; fail: free(path); @@ -2057,10 +2085,12 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) return 0; - if (!(b = new0(CGroupBonding, 1))) + b = new0(CGroupBonding, 1); + if (!b) return -ENOMEM; - if (!(b->controller = strdup(controller))) + b->controller = strdup(controller); + if (!b) goto fail; b->path = unit_default_cgroup_path(u); @@ -2070,7 +2100,8 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) { b->ours = true; b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER); - if ((r = unit_add_cgroup(u, b)) < 0) + r = unit_add_cgroup(u, b); + if (r < 0) goto fail; return 0; @@ -2096,7 +2127,8 @@ int unit_add_default_cgroups(Unit *u) { if (!u->manager->cgroup_hierarchy) return 0; - if ((r = unit_add_one_default_cgroup(u, NULL)) < 0) + r = unit_add_one_default_cgroup(u, NULL); + if (r < 0) return r; STRV_FOREACH(c, u->manager->default_controllers) @@ -2111,12 +2143,18 @@ int unit_add_default_cgroups(Unit *u) { CGroupBonding* unit_get_default_cgroup(Unit *u) { assert(u); - return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER); + return cgroup_bonding_find_list(u->cgroup_bondings, NULL); } -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) { - int r; - char *c = NULL; +int unit_add_cgroup_attribute( + Unit *u, + const char *controller, + const char *name, + const char *value, + CGroupAttributeMapCallback map_callback, + CGroupAttribute **ret) { + + _cleanup_free_ char *c = NULL; CGroupAttribute *a; assert(u); @@ -2137,16 +2175,36 @@ int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, controller = c; } - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - r = -EINVAL; - goto finish; + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + return -EINVAL; + + a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name); + if (a) { + char *v; + + if (streq(value, a->value)) { + if (ret) + *ret = a; + + return 0; + } + + v = strdup(value); + if (!v) + return -ENOMEM; + + free(a->value); + a->value = v; + + if (ret) + *ret = a; + + return 1; } a = new0(CGroupAttribute, 1); - if (!a) { - r = -ENOMEM; - goto finish; - } + if (!a) + return -ENOMEM; if (c) { a->controller = c; @@ -2167,14 +2225,14 @@ int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, } a->map_callback = map_callback; + a->unit = u; LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a); - r = 0; + if (ret) + *ret = a; -finish: - free(c); - return r; + return 1; } int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { diff --git a/src/core/unit.h b/src/core/unit.h index 702bfeece6..d1ecae74ac 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -438,10 +438,10 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep int unit_add_exec_dependencies(Unit *u, ExecContext *c); int unit_add_cgroup(Unit *u, CGroupBonding *b); -int unit_add_cgroup_from_text(Unit *u, const char *name); +int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret); int unit_add_default_cgroups(Unit *u); CGroupBonding* unit_get_default_cgroup(Unit *u); -int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback); +int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback, CGroupAttribute **ret); int unit_choose_id(Unit *u, const char *name); int unit_set_description(Unit *u, const char *description); diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index 18cbf0412a..9dfab2eaac 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -374,18 +374,20 @@ int cg_kill_recursive_and_wait(const char *controller, const char *path, bool re return 0; } -int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) { +int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) { bool done = false; - Set *s; + _cleanup_set_free_ Set *s = NULL; int r, ret = 0; pid_t my_pid; - FILE *f = NULL; + _cleanup_fclose_ FILE *f = NULL; - assert(controller); - assert(from); - assert(to); + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); - if (!(s = set_new(trivial_hash_func, trivial_compare_func))) + s = set_new(trivial_hash_func, trivial_compare_func); + if (!s) return -ENOMEM; my_pid = getpid(); @@ -394,11 +396,12 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig pid_t pid = 0; done = true; - if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) { + r = cg_enumerate_tasks(cfrom, pfrom, &f); + if (r < 0) { if (ret >= 0 && r != -ENOENT) ret = r; - goto finish; + return ret; } while ((r = cg_read_pid(f, &pid)) > 0) { @@ -412,7 +415,8 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) continue; - if ((r = cg_attach(controller, to, pid)) < 0) { + r = cg_attach(cto, pto, pid); + if (r < 0) { if (ret >= 0 && r != -ESRCH) ret = r; } else if (ret == 0) @@ -420,11 +424,12 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig done = false; - if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) { + r = set_put(s, LONG_TO_PTR(pid)); + if (r < 0) { if (ret >= 0) ret = r; - goto finish; + return ret; } } @@ -432,56 +437,48 @@ int cg_migrate(const char *controller, const char *from, const char *to, bool ig if (ret >= 0) ret = r; - goto finish; + return ret; } fclose(f); f = NULL; - } while (!done); -finish: - set_free(s); - - if (f) - fclose(f); - return ret; } -int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool rem) { +int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool rem) { int r, ret = 0; - DIR *d = NULL; + _cleanup_closedir_ DIR *d = NULL; char *fn; - assert(controller); - assert(from); - assert(to); + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); - ret = cg_migrate(controller, from, to, ignore_self); + ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self); - if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) { + r = cg_enumerate_subgroups(cfrom, pfrom, &d); + if (r < 0) { if (ret >= 0 && r != -ENOENT) ret = r; - goto finish; + return ret; } while ((r = cg_read_subgroup(d, &fn)) > 0) { - char *p = NULL; + _cleanup_free_ char *p = NULL; - r = asprintf(&p, "%s/%s", from, fn); + p = strjoin(pfrom, "/", fn, NULL); free(fn); - - if (r < 0) { + if (!p) { if (ret >= 0) ret = -ENOMEM; - goto finish; + return ret; } - r = cg_migrate_recursive(controller, p, to, ignore_self, rem); - free(p); - + r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem); if (r != 0 && ret >= 0) ret = r; } @@ -489,17 +486,11 @@ int cg_migrate_recursive(const char *controller, const char *from, const char *t if (r < 0 && ret >= 0) ret = r; - if (rem) - if ((r = cg_rmdir(controller, from, true)) < 0) { - if (ret >= 0 && - r != -ENOENT && - r != -EBUSY) - ret = r; - } - -finish: - if (d) - closedir(d); + if (rem) { + r = cg_rmdir(cfrom, pfrom, true); + if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) + return r; + } return ret; } @@ -677,7 +668,7 @@ int cg_delete(const char *controller, const char *path) { if ((r = path_get_parent(path, &parent)) < 0) return r; - r = cg_migrate_recursive(controller, path, parent, false, true); + r = cg_migrate_recursive(controller, path, controller, parent, false, true); free(parent); return r == -ENOENT ? 0 : r; @@ -947,7 +938,6 @@ int cg_is_empty_by_spec(const char *spec, bool ignore_self) { return cg_is_empty(controller, path, ignore_self); } - int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) { int r; DIR *d = NULL; @@ -997,12 +987,12 @@ int cg_split_spec(const char *spec, char **controller, char **path) { char *t = NULL, *u = NULL; assert(spec); - assert(controller || path); if (*spec == '/') { if (path) { - if (!(t = strdup(spec))) + t = strdup(spec); + if (!t) return -ENOMEM; *path = t; @@ -1014,13 +1004,14 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return 0; } - if (!(e = strchr(spec, ':'))) { - + e = strchr(spec, ':'); + if (!e) { if (strchr(spec, '/') || spec[0] == 0) return -EINVAL; if (controller) { - if (!(t = strdup(spec))) + t = strdup(spec); + if (!t) return -ENOMEM; *controller = t; @@ -1032,20 +1023,23 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return 0; } - if (e[1] != '/' || - e == spec || - memchr(spec, '/', e-spec)) + if (e[1] != '/' || e == spec || memchr(spec, '/', e-spec)) return -EINVAL; - if (controller) - if (!(t = strndup(spec, e-spec))) + if (controller) { + t = strndup(spec, e-spec); + if (!t) return -ENOMEM; - if (path) - if (!(u = strdup(e+1))) { + } + + if (path) { + u = strdup(e+1); + if (!u) { free(t); return -ENOMEM; } + } if (controller) *controller = t; diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h index af2efc39b4..f663fba687 100644 --- a/src/shared/cgroup-util.h +++ b/src/shared/cgroup-util.h @@ -39,8 +39,8 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool remove, Set *s); int cg_kill_recursive_and_wait(const char *controller, const char *path, bool remove); -int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self); -int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool remove); +int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self); +int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self, bool remove); int cg_split_spec(const char *spec, char **controller, char **path); int cg_join_spec(const char *controller, const char *path, char **spec); diff --git a/src/shared/strv.h b/src/shared/strv.h index 44ba3d1530..fd728eff81 100644 --- a/src/shared/strv.h +++ b/src/shared/strv.h @@ -82,4 +82,8 @@ bool strv_overlap(char **a, char **b); #define STRV_FOREACH_BACKWARDS(s, l) \ for (; (l) && ((s) >= (l)); (s)--) +#define STRV_FOREACH_PAIR(x, y, l) \ + for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2) + + char **strv_sort(char **l); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 91467cc085..075ee4b752 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2025,6 +2025,110 @@ static int kill_unit(DBusConnection *bus, char **args) { return 0; } +static int set_cgroup(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + DBusError error; + const char *method; + DBusMessageIter iter; + int r; + _cleanup_free_ char *n = NULL; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + method = + streq(args[0], "set-cgroup") ? "SetUnitControlGroups" : + streq(args[0], "unset-group") ? "UnsetUnitControlGroups" + : "UnsetUnitControlGroupAttributes"; + + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n)) + return log_oom(); + + r = bus_append_strv_iter(&iter, args + 2); + if (r < 0) + return log_oom(); + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } + + return 0; +} + +static int set_cgroup_attr(DBusConnection *bus, char **args) { + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + DBusError error; + DBusMessageIter iter, sub, sub2; + int r; + char **x, **y; + _cleanup_free_ char *n = NULL; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + if (strv_length(args) % 2 != 0) { + log_error("Expecting an uneven number of arguments!"); + return -EINVAL; + } + + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetUnitControlGroupAttributes"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) + return log_oom(); + + STRV_FOREACH_PAIR(x, y, args + 2) { + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, x) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, y) || + !dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + return -ENOMEM; + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + dbus_error_free(&error); + return -EIO; + } + + return 0; +} + typedef struct ExecStatusInfo { char *name; @@ -4076,6 +4180,12 @@ static int systemctl_help(void) { " help [NAME...|PID...] Show manual for one or more units\n" " reset-failed [NAME...] Reset failed state for all, one, or more\n" " units\n" + " set-cgroup [NAME] [CGROUP...] Add unit to a control group\n" + " unset-cgroup [NAME] [CGROUP...] Remove unit from a control group\n" + " set-cgroup-attr [NAME] [ATTR] [VALUE] ...\n" + " Set control group attribute\n" + " unset-cgroup-attr [NAME] [ATTR...]\n" + " Unset control group attribute\n" " load [NAME...] Load one or more units\n\n" "Unit File Commands:\n" " list-unit-files List installed unit files\n" @@ -5051,6 +5161,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */ { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */ { "isolate", EQUAL, 2, start_unit }, + { "set-cgroup", MORE, 2, set_cgroup }, + { "unset-cgroup", MORE, 2, set_cgroup }, + { "set-cgroup-attr", MORE, 2, set_cgroup_attr }, + { "unset-cgroup-attr", MORE, 2, set_cgroup }, { "kill", MORE, 2, kill_unit }, { "is-active", MORE, 2, check_unit_active }, { "check", MORE, 2, check_unit_active }, diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index 6d64a4e47f..96aca1f7de 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -65,7 +65,7 @@ int main(int argc, char*argv[]) { assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) > 0); - assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", "/test-a", false, false) > 0); + assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", false, false) > 0); assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0); assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); |