summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2011-08-20 00:20:41 +0200
committerLennart Poettering <lennart@poettering.net>2011-08-20 00:22:02 +0200
commitab1f063390f55e14a8de87f21c4fad199eb908a6 (patch)
treef99b7dd16628be2c3ebbee678820de0a268392dc
parent5ed27dbdbfe866810a52ff8225bcf61590861823 (diff)
downloadsystemd-ab1f063390f55e14a8de87f21c4fad199eb908a6.tar.gz
exec: optionally apply cgroup attributes to the cgroups we create
-rw-r--r--Makefile.am3
-rw-r--r--man/systemd-nspawn.xml4
-rw-r--r--man/systemd.exec.xml158
-rw-r--r--src/cgroup-attr.c102
-rw-r--r--src/cgroup-attr.h49
-rw-r--r--src/cgroup.c5
-rw-r--r--src/execute.c9
-rw-r--r--src/execute.h2
-rw-r--r--src/kmod-setup.c2
-rw-r--r--src/load-fragment-gperf.gperf.m48
-rw-r--r--src/load-fragment.c195
-rw-r--r--src/load-fragment.h4
-rw-r--r--src/mount.c1
-rw-r--r--src/service.c1
-rw-r--r--src/socket.c1
-rw-r--r--src/swap.c1
-rw-r--r--src/unit.c81
-rw-r--r--src/unit.h3
-rw-r--r--src/util.c69
-rw-r--r--src/util.h3
20 files changed, 663 insertions, 38 deletions
diff --git a/Makefile.am b/Makefile.am
index b9c80740bb..56d1eecc7a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 6a0d21f0a5..d5f8465506 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -132,7 +132,7 @@
<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
@@ -207,7 +207,7 @@
<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>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index d28417da1c..89e3369d3c 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -630,18 +630,6 @@
</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>
<listitem><para>Controls which
@@ -718,9 +706,9 @@
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
@@ -728,21 +716,139 @@
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>
<term><varname>InaccessibleDirectories=</varname></term>
diff --git a/src/cgroup-attr.c b/src/cgroup-attr.c
new file mode 100644
index 0000000000..474a6865c4
--- /dev/null
+++ b/src/cgroup-attr.c
@@ -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
index 0000000000..63a73b8101
--- /dev/null
+++ b/src/cgroup-attr.h
@@ -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
diff --git a/src/cgroup.c b/src/cgroup.c
index 4aa01f1898..0005a4fb67 100644
--- a/src/cgroup.c
+++ b/src/cgroup.c
@@ -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;
diff --git a/src/execute.c b/src/execute.c
index c73b0c6c04..1ab3218517 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -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;
diff --git a/src/execute.h b/src/execute.h
index b376e36a7a..d5fb61ba26 100644
--- a/src/execute.h
+++ b/src/execute.h
@@ -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);
diff --git a/src/kmod-setup.c b/src/kmod-setup.c
index 97b7c870b1..7bd7dcb182 100644
--- a/src/kmod-setup.c
+++ b/src/kmod-setup.c
@@ -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) {
diff --git a/src/load-fragment-gperf.gperf.m4 b/src/load-fragment-gperf.gperf.m4
index 8e52890dbe..bfa1e3d1b8 100644
--- a/src/load-fragment-gperf.gperf.m4
+++ b/src/load-fragment-gperf.gperf.m4
@@ -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)
diff --git a/src/load-fragment.c b/src/load-fragment.c
index e448d04783..28439d9b20 100644
--- a/src/load-fragment.c
+++ b/src/load-fragment.c
@@ -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) {
diff --git a/src/load-fragment.h b/src/load-fragment.h
index a59046d794..8521ca0918 100644
--- a/src/load-fragment.h
+++ b/src/load-fragment.h
@@ -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);
diff --git a/src/mount.c b/src/mount.c
index c1048d5649..bd1612fd8d 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -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;
diff --git a/src/service.c b/src/service.c
index 0464d1e487..f5ad509dae 100644
--- a/src/service.c
+++ b/src/service.c
@@ -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)
diff --git a/src/socket.c b/src/socket.c
index 468d1018c1..572e622011 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -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);
diff --git a/src/swap.c b/src/swap.c
index afc0c5faab..54a8640aa4 100644
--- a/src/swap.c
+++ b/src/swap.c
@@ -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;
diff --git a/src/unit.c b/src/unit.c
index 65ee0cc35f..5006742d9d 100644
--- a/src/unit.c
+++ b/src/unit.c
@@ -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;
diff --git a/src/unit.h b/src/unit.h
index 3c99817390..7da572350e 100644
--- a/src/unit.h
+++ b/src/unit.h
@@ -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);
diff --git a/src/util.c b/src/util.c
index 8d54049754..c24c749a5a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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;
diff --git a/src/util.h b/src/util.h
index 407160d528..54873e648e 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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)