334 lines
9.7 KiB
C
334 lines
9.7 KiB
C
/*-*- 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", "CPUShares", 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;
|
|
}
|