]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: rework the Delegate= unit file setting to take a list of controller names
authorLennart Poettering <lennart@poettering.net>
Thu, 9 Nov 2017 14:29:34 +0000 (15:29 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 13 Nov 2017 09:49:15 +0000 (10:49 +0100)
Previously it was not possible to select which controllers to enable for
a unit where Delegate=yes was set, as all controllers were enabled. With
this change, this is made configurable, and thus delegation units can
pick specifically what they want to manage themselves, and what they
don't care about.

src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-cgroup.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/unit.c
src/shared/bus-unit-util.c

index d81d10a2df9ac3817d8203ddd8d24d5dffb01bb5..6872da1f895a9a831f28e3424aecbcf5118c0840 100644 (file)
@@ -209,6 +209,16 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
                 prefix, cgroup_device_policy_to_string(c->device_policy),
                 prefix, yes_no(c->delegate));
 
+        if (c->delegate) {
+                _cleanup_free_ char *t = NULL;
+
+                (void) cg_mask_to_string(c->delegate_controllers, &t);
+
+                fprintf(f, "%sDelegateController=%s\n",
+                        prefix,
+                        strempty(t));
+        }
+
         LIST_FOREACH(device_allow, a, c->device_allow)
                 fprintf(f,
                         "%sDeviceAllow=%s %s%s%s\n",
@@ -1062,37 +1072,47 @@ CGroupMask unit_get_own_mask(Unit *u) {
         if (!c)
                 return 0;
 
-        /* If delegation is turned on, then turn on all cgroups,
-         * unless we are on the legacy hierarchy and the process we
-         * fork into it is known to drop privileges, and hence
-         * shouldn't get access to the controllers.
+        return cgroup_context_get_mask(c);
+}
+
+CGroupMask unit_get_delegate_mask(Unit *u) {
+        CGroupContext *c;
+
+        /* If delegation is turned on, then turn on selected controllers, unless we are on the legacy hierarchy and the
+         * process we fork into is known to drop privileges, and hence shouldn't get access to the controllers.
          *
-         * Note that on the unified hierarchy it is safe to delegate
-         * controllers to unprivileged services. */
+         * Note that on the unified hierarchy it is safe to delegate controllers to unprivileged services. */
 
-        if (c->delegate) {
+        if (u->type == UNIT_SLICE)
+                return 0;
+
+        c = unit_get_cgroup_context(u);
+        if (!c)
+                return 0;
+
+        if (!c->delegate)
+                return 0;
+
+        if (cg_all_unified() <= 0) {
                 ExecContext *e;
 
                 e = unit_get_exec_context(u);
-                if (!e ||
-                    exec_context_maintains_privileges(e) ||
-                    cg_all_unified() > 0)
-                        return _CGROUP_MASK_ALL;
+                if (e && !exec_context_maintains_privileges(e))
+                        return 0;
         }
 
-        return cgroup_context_get_mask(c);
+        return c->delegate_controllers;
 }
 
 CGroupMask unit_get_members_mask(Unit *u) {
         assert(u);
 
-        /* Returns the mask of controllers all of the unit's children
-         * require, merged */
+        /* Returns the mask of controllers all of the unit's children require, merged */
 
         if (u->cgroup_members_mask_valid)
                 return u->cgroup_members_mask;
 
-        u->cgroup_members_mask = 0;
+        u->cgroup_members_mask = unit_get_delegate_mask(u);
 
         if (u->type == UNIT_SLICE) {
                 void *v;
index 65245fbc43d46c47e59f9cfe755f11446223a900..a75be380440cb29dce3056e7f8662e1407ae4534 100644 (file)
@@ -126,6 +126,7 @@ struct CGroupContext {
         uint64_t tasks_max;
 
         bool delegate;
+        CGroupMask delegate_controllers;
 };
 
 /* Used when querying IP accounting data */
@@ -153,8 +154,9 @@ void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODe
 void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
 
 CGroupMask unit_get_own_mask(Unit *u);
-CGroupMask unit_get_siblings_mask(Unit *u);
+CGroupMask unit_get_delegate_mask(Unit *u);
 CGroupMask unit_get_members_mask(Unit *u);
+CGroupMask unit_get_siblings_mask(Unit *u);
 CGroupMask unit_get_subtree_mask(Unit *u);
 
 CGroupMask unit_get_target_mask(Unit *u);
index a99d727f4d6165d203332adde07952e4c19ce928..bef70b6f84e237c1c76bdf33baf6f8aa2031abc1 100644 (file)
 
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
 
+static int property_get_delegate_controllers(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        CGroupContext *c = userdata;
+        CGroupController cc;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(c);
+
+        if (!c->delegate)
+                return sd_bus_message_append(reply, "as", 0);
+
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
+
+        for (cc = 0; cc < _CGROUP_CONTROLLER_MAX; cc++) {
+                if ((c->delegate_controllers & CGROUP_CONTROLLER_TO_MASK(cc)) == 0)
+                        continue;
+
+                r = sd_bus_message_append(reply, "s", cgroup_controller_to_string(cc));
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
 static int property_get_io_device_weight(
                 sd_bus *bus,
                 const char *path,
@@ -255,6 +291,7 @@ static int property_get_ip_address_access(
 const sd_bus_vtable bus_cgroup_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
+        SD_BUS_PROPERTY("DelegateControllers", "as", property_get_delegate_controllers, 0, 0),
         SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
         SD_BUS_PROPERTY("CPUWeight", "t", NULL, offsetof(CGroupContext, cpu_weight), 0),
         SD_BUS_PROPERTY("StartupCPUWeight", "t", NULL, offsetof(CGroupContext, startup_cpu_weight), 0),
@@ -315,9 +352,54 @@ static int bus_cgroup_set_transient_property(
 
                 if (mode != UNIT_CHECK) {
                         c->delegate = b;
+                        c->delegate_controllers = b ? _CGROUP_MASK_ALL : 0;
+
                         unit_write_drop_in_private(u, mode, name, b ? "Delegate=yes" : "Delegate=no");
                 }
 
+                return 1;
+
+        } else if (streq(name, "DelegateControllers")) {
+                CGroupMask mask = 0;
+
+                r = sd_bus_message_enter_container(message, 'a', "s");
+                if (r < 0)
+                        return r;
+
+                for (;;) {
+                        CGroupController cc;
+                        const char *t;
+
+                        r = sd_bus_message_read(message, "s", &t);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                break;
+
+                        cc = cgroup_controller_from_string(t);
+                        if (cc < 0)
+                                return sd_bus_error_set_errnof(error, EINVAL, "Unknown cgroup contoller '%s'", t);
+
+                        mask |= CGROUP_CONTROLLER_TO_MASK(cc);
+                }
+
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
+
+                if (mode != UNIT_CHECK) {
+                        _cleanup_free_ char *t = NULL;
+
+                        r = cg_mask_to_string(mask, &t);
+                        if (r < 0)
+                                return r;
+
+                        c->delegate = true;
+                        c->delegate_controllers |= mask;
+
+                        unit_write_drop_in_private_format(u, mode, name, "Delegate=%s", t);
+                }
+
                 return 1;
         }
 
index 0f6372e8a33bea6551d474335a8083334252ad62..42c2dbb9e49ca61c4ea9e5793bc69b97cd25c02a 100644 (file)
@@ -174,7 +174,7 @@ $1.BlockIOReadBandwidth,         config_parse_blockio_bandwidth,     0,
 $1.BlockIOWriteBandwidth,        config_parse_blockio_bandwidth,     0,                             offsetof($1, cgroup_context)
 $1.TasksAccounting,              config_parse_bool,                  0,                             offsetof($1, cgroup_context.tasks_accounting)
 $1.TasksMax,                     config_parse_tasks_max,             0,                             offsetof($1, cgroup_context.tasks_max)
-$1.Delegate,                     config_parse_bool,                  0,                             offsetof($1, cgroup_context.delegate)
+$1.Delegate,                     config_parse_delegate,              0,                             offsetof($1, cgroup_context)
 $1.IPAccounting,                 config_parse_bool,                  0,                             offsetof($1, cgroup_context.ip_accounting)
 $1.IPAddressAllow,               config_parse_ip_address_access,     0,                             offsetof($1, cgroup_context.ip_address_allow)
 $1.IPAddressDeny,                config_parse_ip_address_access,     0,                             offsetof($1, cgroup_context.ip_address_deny)
index 53a95caeaf33dabf878cf7273f495b136f400878..02d507f7b26df9a9e30362f9e44f78e4e4f775a8 100644 (file)
@@ -3202,6 +3202,67 @@ int config_parse_tasks_max(
         return 0;
 }
 
+int config_parse_delegate(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        CGroupContext *c = data;
+        int r;
+
+        /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
+         * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
+         * mask to delegate. */
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                const char *p = rvalue;
+                CGroupMask mask = 0;
+
+                for (;;) {
+                        _cleanup_free_ char *word = NULL;
+                        CGroupController cc;
+
+                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+                        if (r == 0)
+                                break;
+                        if (r == -ENOMEM)
+                                return log_oom();
+                        if (r < 0) {
+                                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+                                return r;
+                        }
+
+                        cc = cgroup_controller_from_string(word);
+                        if (cc < 0) {
+                                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid controller name '%s', ignoring", rvalue);
+                                continue;
+                        }
+
+                        mask |= CGROUP_CONTROLLER_TO_MASK(cc);
+                }
+
+                c->delegate = true;
+                c->delegate_controllers |= mask;
+
+        } else if (r > 0) {
+                c->delegate = true;
+                c->delegate_controllers = _CGROUP_MASK_ALL;
+        } else {
+                c->delegate = false;
+                c->delegate_controllers = 0;
+        }
+
+        return 0;
+}
+
 int config_parse_device_allow(
                 const char *unit,
                 const char *filename,
index 1353b0a9d0b85d086fe93ad5d8825d01b4504d0c..0bd6ec15d6995b2324452985b1fec40a6d46fc41 100644 (file)
@@ -85,6 +85,7 @@ int config_parse_cpu_weight(const char *unit, const char *filename, unsigned lin
 int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_delegate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_io_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index 7d95f9db0bd71285228839ed61a5983712183bb7..e2446804e7af2659f38cc0e08cc4ea8283d92446 100644 (file)
@@ -1057,8 +1057,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 timespan[FORMAT_TIMESPAN_MAX];
         Unit *following;
         _cleanup_set_free_ Set *following_set = NULL;
-        int r;
         const char *n;
+        CGroupMask m;
+        int r;
 
         assert(u);
         assert(u->type >= 0);
@@ -1105,11 +1106,23 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         if (u->cgroup_realized_mask != 0) {
                 _cleanup_free_ char *s = NULL;
                 (void) cg_mask_to_string(u->cgroup_realized_mask, &s);
-                fprintf(f, "%s\tCGroup mask: %s\n", prefix, strnull(s));
+                fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
+        }
+        if (u->cgroup_enabled_mask != 0) {
+                _cleanup_free_ char *s = NULL;
+                (void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
+                fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
+        }
+        m = unit_get_own_mask(u);
+        if (m != 0) {
+                _cleanup_free_ char *s = NULL;
+                (void) cg_mask_to_string(m, &s);
+                fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
         }
-        if (u->cgroup_members_mask != 0) {
+        m = unit_get_members_mask(u);
+        if (m != 0) {
                 _cleanup_free_ char *s = NULL;
-                (void) cg_mask_to_string(u->cgroup_members_mask, &s);
+                (void) cg_mask_to_string(m, &s);
                 fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
         }
 
index 57de9975c79117d56cafdf414fa6d7e59ac0cfd6..529bc62886f87f43e1145f0b0a2e3c0e1604e9e8 100644 (file)
@@ -183,6 +183,51 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
 
                 r = sd_bus_message_append(m, "sv", field, "t", bytes);
                 goto finish;
+
+        } else if (streq(field, "Delegate")) {
+
+                r = parse_boolean(eq);
+                if (r < 0) {
+                        const char *p = eq;
+
+                        r = sd_bus_message_append(m, "s", "DelegateControllers");
+                        if (r < 0)
+                                goto finish;
+
+                        r = sd_bus_message_open_container(m, 'v', "as");
+                        if (r < 0)
+                                goto finish;
+
+                        r = sd_bus_message_open_container(m, 'a', "s");
+                        if (r < 0)
+                                goto finish;
+
+                        for (;;) {
+                                _cleanup_free_ char *word = NULL;
+
+                                r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+                                if (r == 0)
+                                        break;
+                                if (r == -ENOMEM)
+                                        return log_oom();
+                                if (r < 0)
+                                        return log_error_errno(r, "Invalid syntax: %s", eq);
+
+                                r = sd_bus_message_append(m, "s", word);
+                                if (r < 0)
+                                        goto finish;
+                        }
+
+                        r = sd_bus_message_close_container(m);
+                        if (r < 0)
+                                goto finish;
+
+                        r = sd_bus_message_close_container(m);
+                } else
+                        r = sd_bus_message_append(m, "sv", "Delegate", "b", r);
+
+                goto finish;
+
         } else if (streq(field, "TasksMax")) {
                 uint64_t t;
 
@@ -238,7 +283,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
                               "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem",
                               "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate",
                               "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
-                              "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse",
+                              "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse",
                               "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC",
                               "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS",
                               "CPUSchedulingResetOnFork", "LockPersonality")) {