chiark / gitweb /
core: add bus API and systemctl commands for altering cgroup parameters during runtime
authorLennart Poettering <lennart@poettering.net>
Sat, 12 Jan 2013 03:24:12 +0000 (04:24 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 14 Jan 2013 20:24:57 +0000 (21:24 +0100)
19 files changed:
src/core/cgroup-attr.c
src/core/cgroup-attr.h
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-manager.c
src/core/dbus-mount.c
src/core/dbus-service.c
src/core/dbus-socket.c
src/core/dbus-swap.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/load-fragment.c
src/core/unit.c
src/core/unit.h
src/shared/cgroup-util.c
src/shared/cgroup-util.h
src/shared/strv.h
src/systemctl/systemctl.c
src/test/test-cgroup.c

index 71af09c..cedf37d 100644 (file)
@@ -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);
index 2b754ea..0f5b854 100644 (file)
@@ -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);
index 8fc1731..4790a09 100644 (file)
@@ -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))
index 229da52..2ff39e5 100644 (file)
@@ -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);
 
index 2d9cea6..1d785a2 100644 (file)
         "  <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;
index d81edeb..0fcceb5 100644 (file)
@@ -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, }
         };
 
index d99058d..e06a5dc 100644 (file)
@@ -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, }
         };
 
index 095a031..2092a63 100644 (file)
@@ -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, }
         };
 
index 67ea0f2..2e99fba 100644 (file)
@@ -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, }
         };
 
index 8433a72..c7bf043 100644 (file)
@@ -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, }
+};
index ac6785a..7b8c5a9 100644 (file)
         "  <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" \
         "  <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[];
index e35fdbc..4d1154e 100644 (file)
@@ -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) {
index f00cfed..1194c52 100644 (file)
@@ -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) {
index 702bfee..d1ecae7 100644 (file)
@@ -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);
index 18cbf04..9dfab2e 100644 (file)
@@ -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;
index af2efc3..f663fba 100644 (file)
@@ -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);
index 44ba3d1..fd728ef 100644 (file)
@@ -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);
index 91467cc..075ee4b 100644 (file)
@@ -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 },
index 6d64a4e..96aca1f 100644 (file)
@@ -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);