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 /src/core | |
parent | 748ebafa7a10d4e1f168dd8ae0193124cdf4226e (diff) |
core: add bus API and systemctl commands for altering cgroup parameters during runtime
Diffstat (limited to 'src/core')
-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 |
14 files changed, 581 insertions, 64 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); |