chiark / gitweb /
exec: optionally apply cgroup attributes to the cgroups we create
authorLennart Poettering <lennart@poettering.net>
Fri, 19 Aug 2011 22:20:41 +0000 (00:20 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 19 Aug 2011 22:22:02 +0000 (00:22 +0200)
20 files changed:
Makefile.am
man/systemd-nspawn.xml
man/systemd.exec.xml
src/cgroup-attr.c [new file with mode: 0644]
src/cgroup-attr.h [new file with mode: 0644]
src/cgroup.c
src/execute.c
src/execute.h
src/kmod-setup.c
src/load-fragment-gperf.gperf.m4
src/load-fragment.c
src/load-fragment.h
src/mount.c
src/service.c
src/socket.c
src/swap.c
src/unit.c
src/unit.h
src/util.c
src/util.h

index b9c80740bbb9e9fad969bf52f6c626461a1e08d9..56d1eecc7a18bf79b4dd5bea60a29563d85ffb5d 100644 (file)
@@ -627,7 +627,8 @@ libsystemd_core_la_SOURCES = \
        src/condition.c \
         src/dbus-common.c \
         src/sd-daemon.c \
-        src/install.c
+        src/install.c \
+        src/cgroup-attr.c
 
 nodist_libsystemd_core_la_SOURCES = \
         src/load-fragment-gperf.c \
index 6a0d21f0a5e042ce692bb6fc3b35d17f8d3b6a39..d5f8465506b914ef495513a6cbb143886c6c281f 100644 (file)
 
                         <varlistentry>
                                 <term><option>--directory=</option></term>
-                                <term><option>--D</option></term>
+                                <term><option>-D</option></term>
 
                                 <listitem><para>Directory to use as
                                 file system root for the namespace
                 <para>
                         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-                        <citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+                        <citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>mock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
                 </para>
         </refsect1>
index d28417da1c5e4c0afc7a65bfaf0978375a87389c..89e3369d3c4916b7fdce95adf31a48f906a12ef3 100644 (file)
                                 for details.</para></listitem>
                         </varlistentry>
 
-                        <varlistentry>
-                                <term><varname>ControlGroupModify=</varname></term>
-                                <listitem><para>Takes a boolean
-                                argument. If true, the control groups
-                                created for this unit will be owned by
-                                ther user specified with
-                                <varname>User=</varname> (and the
-                                configured group), and he can create
-                                subgroups as well as add processes to
-                                the group.</para></listitem>
-                        </varlistentry>
-
                         <varlistentry>
                                 <term><varname>CapabilityBoundingSet=</varname></term>
 
                                 where "cpu" identifies the kernel
                                 control group controller used, and
                                 <filename>/foo/bar</filename> is the
-                                control group path. The controller name
-                                and ":" may be omitted in which case
-                                the named systemd control group
+                                control group path. The controller
+                                name and ":" may be omitted in which
+                                case the named systemd control group
                                 hierarchy is implied. Alternatively,
                                 the path and ":" may be omitted, in
                                 which case the default control group
                                 option may be used to place executed
                                 processes in arbitrary groups in
                                 arbitrary hierarchies -- which can be
-                                configured externally with additional execution limits. By default
-                                systemd will place all executed
-                                processes in separate per-unit control
-                                groups (named after the unit) in the
-                                systemd named hierarchy. Since every
-                                process can be in one group per
-                                hierarchy only overriding the control group
-                                path in the named systemd hierarchy
-                                will disable automatic placement in
-                                the default group. For details about control
-                                groups see <ulink
+                                configured externally with additional
+                                execution limits. By default systemd
+                                will place all executed processes in
+                                separate per-unit control groups
+                                (named after the unit) in the systemd
+                                named hierarchy. Since every process
+                                can be in one group per hierarchy only
+                                overriding the control group path in
+                                the named systemd hierarchy will
+                                disable automatic placement in the
+                                default group. This option is
+                                primarily intended to place executed
+                                processes in specific paths in
+                                specific kernel controller
+                                hierarchies. It is however not
+                                recommended to manipulate the service
+                                control group path in the systemd
+                                named hierarchy. For details about
+                                control groups see <ulink
                                 url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>.</para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><varname>ControlGroupModify=</varname></term>
+                                <listitem><para>Takes a boolean
+                                argument. If true, the control groups
+                                created for this unit will be owned by
+                                the user specified with
+                                <varname>User=</varname> (and the
+                                appropriate group), and he/she can create
+                                subgroups as well as add processes to
+                                the group.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>ControlGroupAttribute=</varname></term>
+
+                                <listitem><para>Set a specific control
+                                group attribute for executed
+                                processes, and (if needed) add the the
+                                executed processes to a cgroup in the
+                                hierarchy of the controller the
+                                attribute belongs to. Takes two
+                                space-separated arguments: the
+                                attribute name (syntax is
+                                <literal>cpu.shares</literal> where
+                                <literal>cpu</literal> refers to a
+                                specific controller and
+                                <literal>shares</literal> to the
+                                attribute name), and the attribute
+                                value. Example:
+                                <literal>ControlGroupAttribute=cpu.shares
+                                512</literal>. If this option is used
+                                for an attribute that belongs to a
+                                kernel controller hierarchy the unit
+                                is not already configured to be added
+                                to (for example via the
+                                <literal>ControlGroup=</literal>
+                                option) then the unit will be added to
+                                the controller and the default unit
+                                cgroup path is implied. Thus, using
+                                <varname>ControlGroupAttribute=</varname>
+                                is in most case sufficient to make use
+                                of control group enforcements,
+                                explicit
+                                <varname>ControlGroup=</varname> are
+                                only necessary in case the implied
+                                default control group path for a
+                                service is not desirable. For details
+                                about control group attributes see
+                                <ulink
+                                url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>. This
+                                option may appear more than once, in
+                                order to set multiple control group
+                                attributes.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>CPUShares=</varname></term>
+
+                                <listitem><para>Assign the specified
+                                overall CPU time shares to the processes executed. Takes
+                                an integer value. This controls the
+                                <literal>cpu.shares</literal> control
+                                group attribute. For details about
+                                this control group attribute see <ulink
+                                url="http://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>MemoryLimit=</varname></term>
+                                <term><varname>MemorySoftLimit=</varname></term>
+
+                                <listitem><para>Limit the overall memory usage
+                                of the executed processes to a certain
+                                size. Takes a memory size in bytes. If
+                                the value is suffixed with K, M, G or
+                                T the specified memory size is parsed
+                                as Kilobytes, Megabytes, Gigabytes
+                                resp. Terabytes (to the base
+                                1024). This controls the
+                                <literal>memory.limit_in_bytes</literal>
+                                and
+                                <literal>memory.soft_limit_in_bytes</literal>
+                                control group attributes. For details
+                                about these control group attributes
+                                see <ulink
+                                url="http://www.kernel.org/doc/Documentation/cgroups/memory.txt">memory.txt</ulink>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>DeviceAllow=</varname></term>
+                                <term><varname>DeviceDeny=</varname></term>
+
+                                <listitem><para>Control access to
+                                specific device nodes by the executed processes. Takes two
+                                space separated strings: a device node
+                                path (such as
+                                <filename>/dev/null</filename>)
+                                followed by a combination of r, w, m
+                                to control reading, writing resp.
+                                creating of the specific device node
+                                by the unit. This controls the
+                                <literal>devices.allow</literal>
+                                and
+                                <literal>devices.deny</literal>
+                                control group attributes. For details
+                                about these control group attributes
+                                see <ulink
+                                url="http://www.kernel.org/doc/Documentation/cgroups/devices.txt">devices.txt</ulink>.</para></listitem>
+                        </varlistentry>
+
                         <varlistentry>
                                 <term><varname>ReadWriteDirectories=</varname></term>
                                 <term><varname>ReadOnlyDirectories=</varname></term>
diff --git a/src/cgroup-attr.c b/src/cgroup-attr.c
new file mode 100644 (file)
index 0000000..474a686
--- /dev/null
@@ -0,0 +1,102 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "cgroup-attr.h"
+#include "cgroup-util.h"
+#include "list.h"
+
+int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
+        int r;
+        char *path = NULL;
+        char *v = NULL;
+
+        assert(a);
+
+        b = cgroup_bonding_find_list(b, a->controller);
+        if (!b)
+                return 0;
+
+        if (a->map_callback) {
+                r = a->map_callback(a->controller, a->name, a->value, &v);
+                if (r < 0)
+                        return r;
+        }
+
+        r = cg_get_path(a->controller, b->path, a->name, &path);
+        if (r < 0) {
+                free(v);
+                return r;
+        }
+
+        r = write_one_line_file(path, v ? v : a->value);
+        if (r < 0)
+                log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
+
+        free(path);
+        free(v);
+
+        return r;
+}
+
+int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
+        CGroupAttribute *a;
+        int r = 0;
+
+        LIST_FOREACH(by_unit, a, first) {
+                int k;
+
+                k = cgroup_attribute_apply(a, b);
+                if (r == 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+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;
+
+        return NULL;
+}
+
+static void cgroup_attribute_free(CGroupAttribute *a) {
+        assert(a);
+
+        free(a->controller);
+        free(a->name);
+        free(a->value);
+        free(a);
+}
+
+void cgroup_attribute_free_list(CGroupAttribute *first) {
+        CGroupAttribute *a, *n;
+
+        LIST_FOREACH_SAFE(by_unit, a, n, first)
+                cgroup_attribute_free(a);
+}
diff --git a/src/cgroup-attr.h b/src/cgroup-attr.h
new file mode 100644 (file)
index 0000000..63a73b8
--- /dev/null
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocgroupattrhfoo
+#define foocgroupattrhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct CGroupAttribute CGroupAttribute;
+
+typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
+
+#include "unit.h"
+#include "cgroup.h"
+
+struct CGroupAttribute {
+        char *controller;
+        char *name;
+        char *value;
+
+        CGroupAttributeMapCallback map_callback;
+
+        LIST_FIELDS(CGroupAttribute, by_unit);
+};
+
+int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
+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_list(CGroupAttribute *first);
+
+#endif
index 4aa01f18981b5c7a84e7ba2b66c78855201f344d..0005a4fb67cae9bef98466a9c4234d598a4db305 100644 (file)
@@ -41,8 +41,11 @@ int cgroup_bonding_realize(CGroupBonding *b) {
         if (b->realized)
                 return 0;
 
-        if ((r = cg_create(b->controller, b->path)) < 0)
+        r = cg_create(b->controller, b->path);
+        if (r < 0) {
+                log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
                 return r;
+        }
 
         b->realized = true;
 
index c73b0c6c04866704d3af9f750ea5143669daebfa..1ab321851792f8d772dd4176f38d4cfccb9948e9 100644 (file)
@@ -930,6 +930,7 @@ int exec_spawn(ExecCommand *command,
                bool apply_tty_stdin,
                bool confirm_spawn,
                CGroupBonding *cgroup_bondings,
+               CGroupAttribute *cgroup_attributes,
                pid_t *ret) {
 
         pid_t pid;
@@ -973,9 +974,11 @@ int exec_spawn(ExecCommand *command,
         log_debug("About to execute: %s", line);
         free(line);
 
-        if (cgroup_bondings)
-                if ((r = cgroup_bonding_realize_list(cgroup_bondings)))
-                        goto fail_parent;
+        r = cgroup_bonding_realize_list(cgroup_bondings);
+        if (r < 0)
+                goto fail_parent;
+
+        cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
 
         if ((pid = fork()) < 0) {
                 r = -errno;
index b376e36a7aacaae720b20e6cdaa99d10b0d74e4a..d5fb61ba265e6fad9a63a96259db1531327d58e5 100644 (file)
@@ -35,6 +35,7 @@ typedef struct ExecContext ExecContext;
 #include <sched.h>
 
 struct CGroupBonding;
+struct CGroupAttribute;
 
 #include "list.h"
 #include "util.h"
@@ -187,6 +188,7 @@ int exec_spawn(ExecCommand *command,
                bool apply_tty_stdin,
                bool confirm_spawn,
                struct CGroupBonding *cgroup_bondings,
+               struct CGroupAttribute *cgroup_attributes,
                pid_t *ret);
 
 void exec_command_done(ExecCommand *c);
index 97b7c870b16797a2f56f26e2747d812d124ca694..7bd7dcb182ed8795dad52927342ef1a4e37e1e7a 100644 (file)
@@ -70,7 +70,7 @@ int kmod_setup(void) {
         command.argv = (char**) cmdline;
 
         exec_context_init(&context);
-        r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, &pid);
+        r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, NULL, &pid);
         exec_context_done(&context);
 
         if (r < 0) {
index 8e52890dbea5fac1d639230e08f0e1c06b93d8b1..bfa1e3d1b886d505566427ed3e43dc86e3da522c 100644 (file)
@@ -64,7 +64,13 @@ $1.LimitMSGQUEUE,                config_parse_limit,                 RLIMIT_MSGQ
 $1.LimitNICE,                    config_parse_limit,                 RLIMIT_NICE,                   offsetof($1, exec_context.rlimit)
 $1.LimitRTPRIO,                  config_parse_limit,                 RLIMIT_RTPRIO,                 offsetof($1, exec_context.rlimit)
 $1.LimitRTTIME,                  config_parse_limit,                 RLIMIT_RTTIME,                 offsetof($1, exec_context.rlimit)
-$1.ControlGroup,                 config_parse_unit_cgroup,           0,                             offsetof($1, exec_context)
+$1.ControlGroup,                 config_parse_unit_cgroup,           0,                             0
+$1.ControlGroupAttribute,        config_parse_unit_cgroup_attr,      0,                             0
+$1.CPUShares,                    config_parse_unit_cpu_shares,       0,                             0
+$1.MemoryLimit,                  config_parse_unit_memory_limit,     0,                             0
+$1.MemorySoftLimit,              config_parse_unit_memory_limit,     0,                             0
+$1.DeviceAllow,                  config_parse_unit_device_allow,     0,                             0
+$1.DeviceDeny,                   config_parse_unit_device_allow,     0,                             0
 $1.ReadWriteDirectories,         config_parse_path_strv,             0,                             offsetof($1, exec_context.read_write_dirs)
 $1.ReadOnlyDirectories,          config_parse_path_strv,             0,                             offsetof($1, exec_context.read_only_dirs)
 $1.InaccessibleDirectories,      config_parse_path_strv,             0,                             offsetof($1, exec_context.inaccessible_dirs)
index e448d04783919eff3729a4058d0fe7ecab66f4cd..28439d9b20677a338f611755e7addd655959a23e 100644 (file)
@@ -1639,6 +1639,201 @@ int config_parse_unit_condition_null(
 
 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
 
+int config_parse_unit_cgroup_attr(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = data;
+        char **l;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        l = strv_split_quoted(rvalue);
+        if (!l)
+                return -ENOMEM;
+
+        if (strv_length(l) != 2) {
+                log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
+                strv_free(l);
+                return 0;
+        }
+
+        r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
+        strv_free(l);
+
+        if (r < 0) {
+                log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+        Unit *u = data;
+        int r;
+        unsigned long ul;
+        char *t;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
+                log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (asprintf(&t, "%lu", ul) < 0)
+                return -ENOMEM;
+
+        r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
+        free(t);
+
+        if (r < 0) {
+                log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+        Unit *u = data;
+        int r;
+        off_t sz;
+        char *t;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
+                log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
+                return -ENOMEM;
+
+        r = unit_add_cgroup_attribute(u,
+                                      "memory",
+                                      streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
+                                      t, NULL);
+        free(t);
+
+        if (r < 0) {
+                log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+static int device_map(const char *controller, const char *name, const char *value, char **ret) {
+        struct stat st;
+        char **l;
+
+        l = strv_split_quoted(value);
+        if (!l)
+                return -ENOMEM;
+
+        assert(strv_length(l) >= 1);
+
+        if (streq(l[0], "*")) {
+
+                if (asprintf(ret, "a *:*%s%s",
+                             isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+
+        } else {
+                if (lstat(l[0], &st) < 0) {
+                        log_warning("Couldn't stat device %s", l[0]);
+                        strv_free(l);
+                        return -errno;
+                }
+
+                if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+                        log_warning("%s is not a device.", l[0]);
+                        strv_free(l);
+                        return -ENODEV;
+                }
+
+                if (asprintf(ret, "%c %u:%u%s%s",
+                             S_ISCHR(st.st_mode) ? 'c' : 'b',
+                             major(st.st_rdev), minor(st.st_rdev),
+                             isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
+
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+        }
+
+        strv_free(l);
+        return 0;
+}
+
+int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+        Unit *u = data;
+        char **l;
+        int r;
+        unsigned k;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        l = strv_split_quoted(rvalue);
+        if (!l)
+                return -ENOMEM;
+
+        k = strv_length(l);
+        if (k < 1 || k > 2) {
+                log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
+                strv_free(l);
+                return 0;
+        }
+
+        if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
+                log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
+                strv_free(l);
+                return 0;
+        }
+
+        if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
+                log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
+                strv_free(l);
+                return 0;
+        }
+        strv_free(l);
+
+        r = unit_add_cgroup_attribute(u, "devices",
+                                      streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
+                                      rvalue, device_map);
+
+        if (r < 0) {
+                log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
 #define FOLLOW_MAX 8
 
 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
index a59046d79477efce3f9735cac26063e62c485b7e..8521ca091850883b40e498a18a05f32a9caaccc7 100644 (file)
@@ -76,6 +76,10 @@ int config_parse_unit_condition_string(const char *filename, unsigned line, cons
 int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 
 /* gperf prototypes */
 const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
index c1048d56495ca5e20a413e8989837f0cfcfc9dd9..bd1612fd8de83a719e979b2fffda40058f7d1278 100644 (file)
@@ -791,6 +791,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
                             true,
                             m->meta.manager->confirm_spawn,
                             m->meta.cgroup_bondings,
+                            m->meta.cgroup_attributes,
                             &pid)) < 0)
                 goto fail;
 
index 0464d1e48760a307da4929db16b16a31ba27997c..f5ad509dae97ae558d49f9b4e0dbbd853fbe1cc6 100644 (file)
@@ -1728,6 +1728,7 @@ static int service_spawn(
                        apply_tty_stdin,
                        s->meta.manager->confirm_spawn,
                        s->meta.cgroup_bondings,
+                       s->meta.cgroup_attributes,
                        &pid);
 
         if (r < 0)
index 468d1018c1f1d61e2c32c223647f35b8044fcdd4..572e622011bfc02678925ef06826d5bbdce03fd9 100644 (file)
@@ -1112,6 +1112,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
                        true,
                        s->meta.manager->confirm_spawn,
                        s->meta.cgroup_bondings,
+                       s->meta.cgroup_attributes,
                        &pid);
 
         strv_free(argv);
index afc0c5faab55a6d6fd42d4374f2d378a8c2ec784..54a8640aa4948e86e316579f25f56051bf4b7e65 100644 (file)
@@ -613,6 +613,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
                             true,
                             s->meta.manager->confirm_spawn,
                             s->meta.cgroup_bondings,
+                            s->meta.cgroup_attributes,
                             &pid)) < 0)
                 goto fail;
 
index 65ee0cc35f9abccf0b560e4c5a4cd0f87bb76e9c..5006742d9da83f65922c38694c74e2dc226e93c3 100644 (file)
@@ -42,6 +42,7 @@
 #include "special.h"
 #include "cgroup-util.h"
 #include "missing.h"
+#include "cgroup-attr.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -372,6 +373,7 @@ void unit_free(Unit *u) {
         }
 
         cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_reloading <= 0);
+        cgroup_attribute_free_list(u->meta.cgroup_attributes);
 
         free(u->meta.description);
         free(u->meta.fragment_path);
@@ -592,7 +594,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         Iterator i;
         char *p2;
         const char *prefix2;
-        CGroupBonding *b;
         char
                 timestamp1[FORMAT_TIMESTAMP_MAX],
                 timestamp2[FORMAT_TIMESTAMP_MAX],
@@ -662,6 +663,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         }
 
         if (u->meta.load_state == UNIT_LOADED) {
+                CGroupBonding *b;
+                CGroupAttribute *a;
+
                 fprintf(f,
                         "%s\tStopWhenUnneeded: %s\n"
                         "%s\tRefuseManualStart: %s\n"
@@ -682,6 +686,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         fprintf(f, "%s\tControlGroup: %s:%s\n",
                                 prefix, b->controller, b->path);
 
+                LIST_FOREACH(by_unit, a, u->meta.cgroup_attributes)
+                        fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
+                                prefix, a->controller, a->name, a->value);
+
                 if (UNIT_VTABLE(u)->dump)
                         UNIT_VTABLE(u)->dump(u, f, prefix2);
 
@@ -1885,8 +1893,10 @@ fail:
 }
 
 int unit_add_default_cgroups(Unit *u) {
+        CGroupAttribute *a;
         char **c;
         int r;
+
         assert(u);
 
         /* Adds in the default cgroups, if they weren't specified
@@ -1899,8 +1909,10 @@ int unit_add_default_cgroups(Unit *u) {
                 return r;
 
         STRV_FOREACH(c, u->meta.manager->default_controllers)
-                if ((r = unit_add_one_default_cgroup(u, *c)) < 0)
-                        return r;
+                unit_add_one_default_cgroup(u, *c);
+
+        LIST_FOREACH(by_unit, a, u->meta.cgroup_attributes)
+                unit_add_one_default_cgroup(u, a->controller);
 
         return 0;
 }
@@ -1911,6 +1923,69 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) {
         return cgroup_bonding_find_list(u->meta.cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
 }
 
+int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
+        int r;
+        char *c = NULL;
+        CGroupAttribute *a;
+
+        assert(u);
+        assert(name);
+        assert(value);
+
+        if (!controller) {
+                const char *dot;
+
+                dot = strchr(name, '.');
+                if (!dot)
+                        return -EINVAL;
+
+                c = strndup(name, dot - name);
+                if (!c)
+                        return -ENOMEM;
+
+                controller = c;
+        }
+
+        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        a = new0(CGroupAttribute, 1);
+        if (!a) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (c) {
+                a->controller = c;
+                c = NULL;
+        } else
+                a->controller = strdup(controller);
+
+        a->name = strdup(name);
+        a->value = strdup(value);
+
+        if (!a->controller || !a->name || !a->value) {
+                free(a->controller);
+                free(a->name);
+                free(a->value);
+                free(a);
+
+                return -ENOMEM;
+        }
+
+        a->map_callback = map_callback;
+
+        LIST_PREPEND(CGroupAttribute, by_unit, u->meta.cgroup_attributes, a);
+
+        r = 0;
+
+finish:
+        free(c);
+        return r;
+}
+
 int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
         char *t;
         int r;
index 3c998173907fc6d55f045333d1e49da07c55432d..7da572350ef5dbaa5c11265acee138f9a533221e 100644 (file)
@@ -130,6 +130,7 @@ enum UnitDependency {
 #include "manager.h"
 #include "job.h"
 #include "cgroup.h"
+#include "cgroup-attr.h"
 
 struct Meta {
         Manager *manager;
@@ -167,6 +168,7 @@ struct Meta {
 
         /* Counterparts in the cgroup filesystem */
         CGroupBonding *cgroup_bondings;
+        CGroupAttribute *cgroup_attributes;
 
         /* Per type list */
         LIST_FIELDS(Meta, units_by_type);
@@ -432,6 +434,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b);
 int unit_add_cgroup_from_text(Unit *u, const char *name);
 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_choose_id(Unit *u, const char *name);
 int unit_set_description(Unit *u, const char *description);
index 8d54049754c0495b2d4394483c9d2825fcc84f73..c24c749a5a6696996cb1da4fa9f6fcb9ccd4e804 100644 (file)
@@ -1439,6 +1439,19 @@ char *delete_chars(char *s, const char *bad) {
         return s;
 }
 
+bool in_charset(const char *s, const char* charset) {
+        const char *i;
+
+        assert(s);
+        assert(charset);
+
+        for (i = s; *i; i++)
+                if (!strchr(charset, *i))
+                        return false;
+
+        return true;
+}
+
 char *file_in_same_dir(const char *path, const char *filename) {
         char *e, *r;
         size_t k;
@@ -2969,6 +2982,62 @@ int parse_usec(const char *t, usec_t *usec) {
         return 0;
 }
 
+int parse_bytes(const char *t, off_t *bytes) {
+        static const struct {
+                const char *suffix;
+                off_t factor;
+        } table[] = {
+                { "B", 1 },
+                { "K", 1024ULL },
+                { "M", 1024ULL*1024ULL },
+                { "G", 1024ULL*1024ULL*1024ULL },
+                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+                { "", 1 },
+        };
+
+        const char *p;
+        off_t r = 0;
+
+        assert(t);
+        assert(bytes);
+
+        p = t;
+        do {
+                long long l;
+                char *e;
+                unsigned i;
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno != 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (e == p)
+                        return -EINVAL;
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = 0; i < ELEMENTSOF(table); i++)
+                        if (startswith(e, table[i].suffix)) {
+                                r += (off_t) l * table[i].factor;
+                                p = e + strlen(table[i].suffix);
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table))
+                        return -EINVAL;
+
+        } while (*p != 0);
+
+        *bytes = r;
+
+        return 0;
+}
+
 int make_stdio(int fd) {
         int r, s, t;
 
index 407160d528c88eda7e4092bf496851f24098b57b..54873e648e1006949abbba3a9c27af300198c630 100644 (file)
@@ -135,6 +135,7 @@ void close_many(const int fds[], unsigned n_fd);
 
 int parse_boolean(const char *v);
 int parse_usec(const char *t, usec_t *usec);
+int parse_bytes(const char *t, off_t *bytes);
 int parse_pid(const char *s, pid_t* ret_pid);
 int parse_uid(const char *s, uid_t* ret_uid);
 #define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
@@ -462,6 +463,8 @@ char *join(const char *x, ...) _sentinel_;
 
 bool is_main_thread(void);
 
+bool in_charset(const char *s, const char* charset);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)