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