1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "path-util.h"
25 #include "cgroup-util.h"
27 #include "cgroup-semantics.h"
29 static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
36 if (safe_atolu(value, &ul) < 0 || ul < 1)
39 if (asprintf(ret, "%lu", ul) < 0)
45 static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
52 if (parse_bytes(value, &sz) < 0 || sz <= 0)
55 if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
61 static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
62 _cleanup_strv_free_ char **l = NULL;
70 l = strv_split_quoted(value);
78 if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
81 if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
92 static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
93 _cleanup_strv_free_ char **l = NULL;
100 l = strv_split_quoted(value);
104 if (strv_length(l) != 1)
105 return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
107 if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
110 if (asprintf(ret, "%lu", ul) < 0)
116 static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
117 _cleanup_strv_free_ char **l = NULL;
124 l = strv_split_quoted(value);
128 if (strv_length(l) != 2)
131 if (!path_startswith(l[0], "/dev"))
134 if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
137 if (asprintf(ret, "%s %lu", l[0], ul) < 0)
143 static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
144 _cleanup_strv_free_ char **l = NULL;
151 l = strv_split_quoted(value);
155 if (strv_length(l) != 2)
158 if (!path_startswith(l[0], "/dev")) {
162 if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
165 if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
171 static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
172 _cleanup_strv_free_ char **l = NULL;
179 l = strv_split_quoted(value);
187 if (streq(l[0], "*")) {
189 if (asprintf(ret, "a *:*%s%s",
190 isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
195 if (stat(l[0], &st) < 0) {
196 log_warning("Couldn't stat device %s", l[0]);
200 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
201 log_warning("%s is not a device.", l[0]);
205 if (asprintf(ret, "%c %u:%u%s%s",
206 S_ISCHR(st.st_mode) ? 'c' : 'b',
207 major(st.st_rdev), minor(st.st_rdev),
208 isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
215 static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
216 _cleanup_strv_free_ char **l = NULL;
224 l = strv_split_quoted(value);
228 if (strv_length(l) != 2)
231 if (stat(l[0], &st) < 0) {
232 log_warning("Couldn't stat device %s", l[0]);
236 if (S_ISBLK(st.st_mode))
238 else if (major(st.st_dev) != 0) {
239 /* If this is not a device node then find the block
240 * device this file is stored on */
243 /* If this is a partition, try to get the originating
245 block_get_whole_disk(d, &d);
247 log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
251 if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
257 static const CGroupSemantics semantics[] = {
258 { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
259 { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
260 { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
261 { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
262 { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
263 { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
264 { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
265 { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
266 { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
269 int cgroup_semantics_find(
270 const char *controller,
274 const CGroupSemantics **_s) {
276 _cleanup_free_ char *c = NULL;
282 assert(!value == !ret);
285 r = cg_controller_from_attr(name, &c);
292 for (i = 0; i < ELEMENTSOF(semantics); i++) {
293 const CGroupSemantics *s = semantics + i;
294 bool matches_name, matches_pretty;
296 if (controller && s->controller && !streq(s->controller, controller))
299 matches_name = s->name && streq(s->name, name);
300 matches_pretty = s->pretty && streq(s->pretty, name);
302 if (!matches_name && !matches_pretty)
306 if (matches_pretty && s->map_pretty) {
308 r = s->map_pretty(s, value, ret);