summaryrefslogtreecommitdiff
path: root/src/core/cgroup-semantics.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2013-02-27 18:50:41 +0100
committerLennart Poettering <lennart@poettering.net>2013-02-27 18:50:41 +0100
commit26d04f86a36595e3565c74d67863e076c3e3c773 (patch)
tree374ba1bdf5dfc95a9c05df1232ffc44d8bd98397 /src/core/cgroup-semantics.c
parent416389f7ae262ae7a0848302e7a6516597f9fad1 (diff)
unit: rework resource management API
This introduces a new static list of known attributes and their special semantics. This means that cgroup attribute values can now be automatically translated from user to kernel notation for command line set settings, too. This also adds proper support for multi-line attributes.
Diffstat (limited to 'src/core/cgroup-semantics.c')
-rw-r--r--src/core/cgroup-semantics.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c
new file mode 100644
index 0000000000..82b02bbd78
--- /dev/null
+++ b/src/core/cgroup-semantics.c
@@ -0,0 +1,333 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "cgroup-util.h"
+
+#include "cgroup-semantics.h"
+
+static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (safe_atolu(value, &ul) < 0 || ul < 1)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
+ off_t sz;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (parse_bytes(value, &sz) < 0 || sz <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ char *x;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
+ return -EINVAL;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ return 1;
+}
+
+static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 1)
+ return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
+
+ if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %lu", l[0], ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ off_t bytes;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev")) {
+ return -EINVAL;
+ }
+
+ if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (streq(l[0], "*")) {
+
+ if (asprintf(ret, "a *:*%s%s",
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ } else {
+ struct stat st;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", l[0]);
+ 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)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ struct stat st;
+ dev_t d;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return log_oom();
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (S_ISBLK(st.st_mode))
+ d = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ d = st.st_dev;
+
+ /* If this is a partition, try to get the originating
+ * block device */
+ block_get_whole_disk(d, &d);
+ } else {
+ log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const CGroupSemantics semantics[] = {
+ { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
+ { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
+ { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
+ { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
+ { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
+ { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
+ { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
+ { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
+ { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
+};
+
+int cgroup_semantics_find(
+ const char *controller,
+ const char *name,
+ const char *value,
+ char **ret,
+ const CGroupSemantics **_s) {
+
+ _cleanup_free_ char *c = NULL;
+ unsigned i;
+ int r;
+
+ assert(name);
+ assert(_s);
+ assert(!value == !ret);
+
+ if (!controller) {
+ r = cg_controller_from_attr(name, &c);
+ if (r < 0)
+ return r;
+
+ controller = c;
+ }
+
+ for (i = 0; i < ELEMENTSOF(semantics); i++) {
+ const CGroupSemantics *s = semantics + i;
+ bool matches_name, matches_pretty;
+
+ if (controller && s->controller && !streq(s->controller, controller))
+ continue;
+
+ matches_name = s->name && streq(s->name, name);
+ matches_pretty = s->pretty && streq(s->pretty, name);
+
+ if (!matches_name && !matches_pretty)
+ continue;
+
+ if (value) {
+ if (matches_pretty && s->map_pretty) {
+
+ r = s->map_pretty(s, value, ret);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ continue;
+
+ } else {
+ char *x;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ }
+ }
+
+ *_s = s;
+ return 1;
+ }
+
+ *ret = NULL;
+ *_s = NULL;
+ return 0;
+}