chiark / gitweb /
systemctl: try to reload daemon after enable/disable only when not running in a chroot
[elogind.git] / src / core / cgroup-semantics.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include "util.h"
23 #include "strv.h"
24 #include "path-util.h"
25 #include "cgroup-util.h"
26
27 #include "cgroup-semantics.h"
28
29 static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
30         unsigned long ul;
31
32         assert(s);
33         assert(value);
34         assert(ret);
35
36         if (safe_atolu(value, &ul) < 0 || ul < 1)
37                 return -EINVAL;
38
39         if (asprintf(ret, "%lu", ul) < 0)
40                 return -ENOMEM;
41
42         return 1;
43 }
44
45 static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
46         off_t sz;
47
48         assert(s);
49         assert(value);
50         assert(ret);
51
52         if (parse_bytes(value, &sz) < 0 || sz <= 0)
53                 return -EINVAL;
54
55         if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
56                 return -ENOMEM;
57
58         return 1;
59 }
60
61 static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
62         _cleanup_strv_free_ char **l = NULL;
63         char *x;
64         unsigned k;
65
66         assert(s);
67         assert(value);
68         assert(ret);
69
70         l = strv_split_quoted(value);
71         if (!l)
72                 return -ENOMEM;
73
74         k = strv_length(l);
75         if (k < 1 || k > 2)
76                 return -EINVAL;
77
78         if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
79                 return -EINVAL;
80
81         if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
82                 return -EINVAL;
83
84         x = strdup(value);
85         if (!x)
86                 return -ENOMEM;
87
88         *ret = x;
89         return 1;
90 }
91
92 static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
93         _cleanup_strv_free_ char **l = NULL;
94         unsigned long ul;
95
96         assert(s);
97         assert(value);
98         assert(ret);
99
100         l = strv_split_quoted(value);
101         if (!l)
102                 return -ENOMEM;
103
104         if (strv_length(l) != 1)
105                 return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
106
107         if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
108                 return -EINVAL;
109
110         if (asprintf(ret, "%lu", ul) < 0)
111                 return -ENOMEM;
112
113         return 1;
114 }
115
116 static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
117         _cleanup_strv_free_ char **l = NULL;
118         unsigned long ul;
119
120         assert(s);
121         assert(value);
122         assert(ret);
123
124         l = strv_split_quoted(value);
125         if (!l)
126                 return -ENOMEM;
127
128         if (strv_length(l) != 2)
129                 return -EINVAL;
130
131         if (!path_startswith(l[0], "/dev"))
132                 return -EINVAL;
133
134         if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
135                 return -EINVAL;
136
137         if (asprintf(ret, "%s %lu", l[0], ul) < 0)
138                 return -ENOMEM;
139
140         return 1;
141 }
142
143 static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
144         _cleanup_strv_free_ char **l = NULL;
145         off_t bytes;
146
147         assert(s);
148         assert(value);
149         assert(ret);
150
151         l = strv_split_quoted(value);
152         if (!l)
153                 return -ENOMEM;
154
155         if (strv_length(l) != 2)
156                 return -EINVAL;
157
158         if (!path_startswith(l[0], "/dev")) {
159                 return -EINVAL;
160         }
161
162         if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
163                 return -EINVAL;
164
165         if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
166                 return -ENOMEM;
167
168         return 0;
169 }
170
171 static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
172         _cleanup_strv_free_ char **l = NULL;
173         unsigned k;
174
175         assert(s);
176         assert(value);
177         assert(ret);
178
179         l = strv_split_quoted(value);
180         if (!l)
181                 return -ENOMEM;
182
183         k = strv_length(l);
184         if (k < 1 || k > 2)
185                 return -EINVAL;
186
187         if (streq(l[0], "*")) {
188
189                 if (asprintf(ret, "a *:*%s%s",
190                              isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
191                         return -ENOMEM;
192         } else {
193                 struct stat st;
194
195                 if (stat(l[0], &st) < 0) {
196                         log_warning("Couldn't stat device %s", l[0]);
197                         return -errno;
198                 }
199
200                 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
201                         log_warning("%s is not a device.", l[0]);
202                         return -ENODEV;
203                 }
204
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)
209                         return -ENOMEM;
210         }
211
212         return 0;
213 }
214
215 static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
216         _cleanup_strv_free_ char **l = NULL;
217         struct stat st;
218         dev_t d;
219
220         assert(s);
221         assert(value);
222         assert(ret);
223
224         l = strv_split_quoted(value);
225         if (!l)
226                 return log_oom();
227
228         if (strv_length(l) != 2)
229                 return -EINVAL;
230
231         if (stat(l[0], &st) < 0) {
232                 log_warning("Couldn't stat device %s", l[0]);
233                 return -errno;
234         }
235
236         if (S_ISBLK(st.st_mode))
237                 d = st.st_rdev;
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 */
241                 d = st.st_dev;
242
243                 /* If this is a partition, try to get the originating
244                  * block device */
245                 block_get_whole_disk(d, &d);
246         } else {
247                 log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
248                 return -ENODEV;
249         }
250
251         if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
252                 return -ENOMEM;
253
254         return 0;
255 }
256
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 }
267 };
268
269 int cgroup_semantics_find(
270                 const char *controller,
271                 const char *name,
272                 const char *value,
273                 char **ret,
274                 const CGroupSemantics **_s) {
275
276         _cleanup_free_ char *c = NULL;
277         unsigned i;
278         int r;
279
280         assert(name);
281         assert(_s);
282         assert(!value == !ret);
283
284         if (!controller) {
285                 r = cg_controller_from_attr(name, &c);
286                 if (r < 0)
287                         return r;
288
289                 controller = c;
290         }
291
292         for (i = 0; i < ELEMENTSOF(semantics); i++) {
293                 const CGroupSemantics *s = semantics + i;
294                 bool matches_name, matches_pretty;
295
296                 if (controller && s->controller && !streq(s->controller, controller))
297                         continue;
298
299                 matches_name = s->name && streq(s->name, name);
300                 matches_pretty = s->pretty && streq(s->pretty, name);
301
302                 if (!matches_name && !matches_pretty)
303                         continue;
304
305                 if (value) {
306                         if (matches_pretty && s->map_pretty) {
307
308                                 r = s->map_pretty(s, value, ret);
309                                 if (r < 0)
310                                         return r;
311
312                                 if (r == 0)
313                                         continue;
314
315                         } else {
316                                 char *x;
317
318                                 x = strdup(value);
319                                 if (!x)
320                                         return -ENOMEM;
321
322                                 *ret = x;
323                         }
324                 }
325
326                 *_s = s;
327                 return 1;
328         }
329
330         *ret = NULL;
331         *_s = NULL;
332         return 0;
333 }