]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/cgroup.c
remove unused lxc_read_line_from_file()
[mirror_lxc.git] / src / lxc / cgroup.c
CommitLineData
576f946d 1/*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
9afe19d6 7 * Daniel Lezcano <daniel.lezcano at free.fr>
576f946d 8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
250b1eec 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
576f946d 22 */
23#define _GNU_SOURCE
24#include <stdio.h>
25#undef _GNU_SOURCE
26#include <stdlib.h>
27#include <errno.h>
576f946d 28#include <unistd.h>
29#include <string.h>
341a9bd8 30#include <dirent.h>
576f946d 31#include <fcntl.h>
b98f7d6e 32#include <ctype.h>
576f946d 33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/param.h>
36#include <sys/inotify.h>
37#include <netinet/in.h>
38#include <net/if.h>
39
e2bcd7db 40#include "error.h"
881450bb 41#include "config.h"
ae5c8b8e 42#include "commands.h"
b98f7d6e
SH
43#include "list.h"
44#include "conf.h"
36eb9bde 45
36eb9bde 46#include <lxc/log.h>
00b3c2e2
CLG
47#include <lxc/cgroup.h>
48#include <lxc/start.h>
36eb9bde 49
edaf8b1b
SG
50#if IS_BIONIC
51#include <../include/lxcmntent.h>
52#else
53#include <mntent.h>
54#endif
55
120ce443
SG
56#ifndef HAVE_GETLINE
57#ifdef HAVE_FGETLN
58#include <../include/getline.h>
59#endif
60#endif
61
36eb9bde 62lxc_log_define(lxc_cgroup, lxc);
576f946d 63
5193cc3d 64#define MTAB "/proc/mounts"
576f946d 65
fd37327f
ÇO
66/* In the case of a bind mount, there could be two long pathnames in the
67 * mntent plus options so use large enough buffer size
68 */
69#define LARGE_MAXPATHLEN 4 * MAXPATHLEN
70
1d39a065
DW
71/* Check if a mount is a cgroup hierarchy for any subsystem.
72 * Return the first subsystem found (or NULL if none).
73 */
74static char *mount_has_subsystem(const struct mntent *mntent)
75{
76 FILE *f;
5270bf4b 77 char *c, *ret = NULL;
1d39a065
DW
78 char line[MAXPATHLEN];
79
80 /* read the list of subsystems from the kernel */
81 f = fopen("/proc/cgroups", "r");
82 if (!f)
83 return 0;
84
85 /* skip the first line, which contains column headings */
00b6be44
SH
86 if (!fgets(line, MAXPATHLEN, f)) {
87 fclose(f);
1d39a065 88 return 0;
00b6be44 89 }
1d39a065
DW
90
91 while (fgets(line, MAXPATHLEN, f)) {
92 c = strchr(line, '\t');
93 if (!c)
94 continue;
95 *c = '\0';
96
97 ret = hasmntopt(mntent, line);
98 if (ret)
99 break;
100 }
101
102 fclose(f);
103 return ret;
104}
105
d08ba6ec 106/*
23622a2a 107 * Determine mountpoint for a cgroup subsystem.
b98f7d6e 108 * @dest: a passed-in buffer of at least size MAXPATHLEN into which the path
ae5c8b8e 109 * is copied.
b98f7d6e 110 * @subsystem: cgroup subsystem (i.e. freezer)
ae5c8b8e 111 *
b98f7d6e 112 * Returns true on success, false on error.
ae5c8b8e 113 */
b98f7d6e 114bool get_subsys_mount(char *dest, const char *subsystem)
576f946d 115{
93d564ed 116 struct mntent mntent_r;
bcbd102c 117 FILE *file = NULL;
b98f7d6e
SH
118 int ret;
119 bool retv = false;
fd37327f
ÇO
120 char buf[LARGE_MAXPATHLEN] = {0};
121
bcbd102c
SH
122 file = setmntent(MTAB, "r");
123 if (!file) {
124 SYSERROR("failed to open %s", MTAB);
5193cc3d 125 return -1;
bcbd102c 126 }
0d9f8e18 127
93d564ed 128 while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) {
b98f7d6e 129 if (strcmp(mntent_r.mnt_type, "cgroup"))
bcbd102c 130 continue;
ef6e34ee 131
1d39a065 132 if (subsystem) {
fd37327f 133 if (!hasmntopt(&mntent_r, subsystem))
1d39a065 134 continue;
ae5c8b8e 135 } else {
fd37327f 136 if (!mount_has_subsystem(&mntent_r))
1d39a065
DW
137 continue;
138 }
ad08bbb7 139
b98f7d6e 140 ret = snprintf(dest, MAXPATHLEN, "%s", mntent_r.mnt_dir);
ad08bbb7
DW
141 if (ret < 0 || ret >= MAXPATHLEN)
142 goto fail;
143
b98f7d6e 144 retv = true;
ad08bbb7 145 goto out;
bcbd102c 146 };
576f946d 147
d08ba6ec
SH
148fail:
149 DEBUG("Failed to find cgroup for %s\n",
150 subsystem ? subsystem : "(NULL)");
ad08bbb7
DW
151out:
152 endmntent(file);
b98f7d6e 153 return retv;
5193cc3d
DL
154}
155
ae5c8b8e 156/*
b98f7d6e
SH
157 * is_in_cgroup: check whether pid is found in the passed-in cgroup tasks
158 * file.
159 * @path: in full path to a cgroup tasks file
160 * Note that in most cases the file will simply not exist, which is ok - it
161 * just means that's not our cgroup.
ae5c8b8e 162 */
b98f7d6e 163static bool is_in_cgroup(pid_t pid, char *path)
0b9c21ab 164{
b98f7d6e
SH
165 int cmppid;
166 FILE *f = fopen(path, "r");
167 char *line = NULL;
168 size_t sz = 0;
fd37327f 169
b98f7d6e
SH
170 if (!f)
171 return false;
172 while (getline(&line, &sz, f) != -1) {
173 if (sscanf(line, "%d", &cmppid) == 1 && cmppid == pid) {
174 fclose(f);
175 free(line);
176 return true;
ae5c8b8e 177 }
ae5c8b8e 178 }
b98f7d6e
SH
179 fclose(f);
180 if (line)
181 free(line);
182 return false;
0b9c21ab
SH
183}
184
ae5c8b8e 185/*
2acf7795
DE
186 * lxc_cgroup_path_get: Get the absolute pathname for a cgroup
187 * file for a running container.
188 *
189 * @subsystem : subsystem of interest (e.g. "freezer"). If NULL, then
190 * the first cgroup entry in mtab will be used.
191 * @name : name of container to connect to
192 * @lxcpath : the lxcpath in which the container is running
ae5c8b8e
SH
193 *
194 * This is the exported function, which determines cgpath from the
2acf7795 195 * lxc-start of the @name container running in @lxcpath.
ae5c8b8e 196 *
2acf7795
DE
197 * Returns path on success, NULL on error. The caller must free()
198 * the returned path.
ae5c8b8e 199 */
2acf7795
DE
200char *lxc_cgroup_path_get(const char *subsystem, const char *name,
201 const char *lxcpath)
fd4f5a56 202{
b98f7d6e
SH
203 char *cgpath, *cgp, path[MAXPATHLEN], *pathp, *p;
204 pid_t initpid = lxc_cmd_get_init_pid(name, lxcpath);
205 int ret;
206
6fe93aa1 207 if (initpid < 0)
b98f7d6e 208 return NULL;
fd4f5a56 209
b98f7d6e
SH
210 cgpath = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
211 if (!cgpath)
2acf7795 212 return NULL;
fd4f5a56 213
b98f7d6e
SH
214 if (!get_subsys_mount(path, subsystem))
215 return NULL;
216
217 pathp = path + strlen(path);
218 /*
219 * find a mntpt where i have the subsystem mounted, then find
220 * a subset cgpath under that which has pid in it.
221 *
222 * If d->mntpt is '/a/b/c/d', and the mountpoint is /x/y/z,
223 * then look for ourselves in:
224 * /x/y/z/a/b/c/d/tasks
225 * /x/y/z/b/c/d/tasks
226 * /x/y/z/c/d/tasks
227 * /x/y/z/d/tasks
228 * /x/y/z/tasks
229 */
230 cgp = cgpath;
231 while (cgp[0]) {
232 ret = snprintf(pathp, MAXPATHLEN - (pathp - path), "%s/tasks", cgp);
233 if (ret < 0 || ret >= MAXPATHLEN)
234 return NULL;
235 if (!is_in_cgroup(initpid, path)) {
236 // does not exist, try the next one
237 cgp = index(cgp+1, '/');
238 if (!cgp)
239 break;
240 continue;
241 }
242 break;
243 }
244 if (!cgp || !*cgp) {
245 // try just the path
246 ret = snprintf(pathp, MAXPATHLEN - (pathp - path), "/tasks");
247 if (ret < 0 || ret >= MAXPATHLEN)
248 return NULL;
249 if (!is_in_cgroup(initpid, path)) {
250 return NULL;
251 }
252 return strdup("/");
253 }
254 // path still has 'tasks' on the end, drop it
c32981c3 255 if ((p = strrchr(path, '/')) != NULL)
b98f7d6e
SH
256 *p = '\0';
257 return strdup(path);
ae5c8b8e
SH
258}
259
260/*
2acf7795
DE
261 * do_cgroup_set: Write a value into a cgroup file
262 *
263 * @path : absolute path to cgroup file
264 * @value : value to write into file
265 *
266 * Returns 0 on success, < 0 on error.
ae5c8b8e
SH
267 */
268static int do_cgroup_set(const char *path, const char *value)
269{
270 int fd, ret;
271
272 if ((fd = open(path, O_WRONLY)) < 0) {
273 SYSERROR("open %s : %s", path, strerror(errno));
274 return -1;
fd4f5a56
DL
275 }
276
ae5c8b8e
SH
277 if ((ret = write(fd, value, strlen(value))) < 0) {
278 close(fd);
279 SYSERROR("write %s : %s", path, strerror(errno));
280 return ret;
281 }
fd4f5a56 282
ae5c8b8e
SH
283 if ((ret = close(fd)) < 0) {
284 SYSERROR("close %s : %s", path, strerror(errno));
285 return ret;
286 }
287 return 0;
fd4f5a56
DL
288}
289
e14f67a7
U
290static int in_subsys_list(const char *s, const char *list)
291{
292 char *token, *str, *saveptr = NULL;
293
294 if (!list || !s)
295 return 0;
296
297 str = alloca(strlen(list)+1);
298 strcpy(str, list);
299 for (; (token = strtok_r(str, ",", &saveptr)); str = NULL) {
300 if (strcmp(s, token) == 0)
301 return 1;
302 }
303
304 return 0;
305}
306
307static char *cgroup_get_subsys_abspath(struct lxc_handler *handler, const char *subsys)
308{
309 struct cgroup_desc *d;
310
311 for (d = handler->cgroup; d; d = d->next) {
312 if (in_subsys_list(subsys, d->subsystems))
313 return d->curcgroup;
314 }
315
316 return NULL;
317}
318
b98f7d6e
SH
319static bool cgroup_devices_has_deny(struct lxc_handler *h, char *v)
320{
321 char *cgabspath, path[MAXPATHLEN];
322 FILE *f;
323 char *line = NULL;
324 size_t len = 0;
325 bool ret = true;
e14f67a7 326 int r;
b98f7d6e
SH
327
328 // XXX FIXME if users could use something other than 'lxc.devices.deny = a'.
329 // not sure they ever do, but they *could*
330 // right now, I'm assuming they do NOT
331 if (strcmp(v, "a") && strcmp(v, "a *:* rwm"))
332 return false;
e14f67a7 333 cgabspath = cgroup_get_subsys_abspath(h, "devices");
b98f7d6e 334 if (!cgabspath)
e14f67a7 335 return false;
b98f7d6e 336
e14f67a7
U
337 r = snprintf(path, MAXPATHLEN, "%s/devices.list", cgabspath);
338 if (r < 0 || r >= MAXPATHLEN) {
b98f7d6e 339 ERROR("pathname too long for devices.list");
e14f67a7 340 return false;
b98f7d6e
SH
341 }
342
6fe93aa1 343 if (!(f = fopen(path, "r")))
e14f67a7 344 return false;
b98f7d6e
SH
345
346 while (getline(&line, &len, f) != -1) {
347 size_t len = strlen(line);
348 if (len > 0 && line[len-1] == '\n')
349 line[len-1] = '\0';
350 if (strcmp(line, "a *:* rwm") == 0) {
351 ret = false;
352 goto out;
353 }
354 }
355
356out:
357 fclose(f);
358 if (line)
359 free(line);
360 return ret;
361}
362
363static bool cgroup_devices_has_allow(struct lxc_handler *h, char *v)
364{
365 char *cgabspath, path[MAXPATHLEN];
e14f67a7 366 int r;
b98f7d6e
SH
367 bool ret = false;
368 FILE *f;
369 char *line = NULL;
370 size_t len = 0;
371
e14f67a7 372 cgabspath = cgroup_get_subsys_abspath(h, "devices");
b98f7d6e 373 if (!cgabspath)
e14f67a7 374 return false;
b98f7d6e 375
e14f67a7
U
376 r = snprintf(path, MAXPATHLEN, "%s/devices.list", cgabspath);
377 if (r < 0 || r >= MAXPATHLEN) {
b98f7d6e 378 ERROR("pathname too long to for devices.list");
e14f67a7 379 return false;
b98f7d6e
SH
380 }
381
6fe93aa1 382 if (!(f = fopen(path, "r")))
e14f67a7 383 return false;
b98f7d6e
SH
384
385 while (getline(&line, &len, f) != -1) {
e14f67a7
U
386 if (len < 1)
387 goto out;
388 if (line[len-1] == '\n')
b98f7d6e 389 line[len-1] = '\0';
e14f67a7 390 if (strcmp(line, "a *:* rwm") == 0 || strcmp(line, v) == 0) {
b98f7d6e
SH
391 ret = true;
392 goto out;
393 }
394 }
395
396out:
397 if (line)
398 free(line);
399 fclose(f);
400 return ret;
401}
402
ae5c8b8e 403/*
2acf7795
DE
404 * lxc_cgroup_set_bypath: Write a value into a cgroup file
405 *
406 * @cgrelpath : a container's relative cgroup path (e.g. "lxc/c1")
407 * @filename : the cgroup file to write (e.g. "freezer.state")
408 * @value : value to write into file
ae5c8b8e
SH
409 *
410 * Returns 0 on success, < 0 on error.
411 */
b98f7d6e
SH
412int lxc_cgroup_set_value(struct lxc_handler *handler, const char *filename,
413 const char *value)
fd4f5a56 414{
b98f7d6e 415 char *cgabspath, path[MAXPATHLEN], *p;
ae5c8b8e 416 int ret;
c8f7c563 417
b98f7d6e
SH
418 ret = snprintf(path, MAXPATHLEN, "%s", filename);
419 if (ret < 0 || ret >= MAXPATHLEN)
420 return -1;
421 if ((p = index(path, '.')) != NULL)
422 *p = '\0';
e14f67a7 423 cgabspath = cgroup_get_subsys_abspath(handler, path);
2acf7795
DE
424 if (!cgabspath)
425 return -1;
c8f7c563 426
2acf7795 427 ret = snprintf(path, MAXPATHLEN, "%s/%s", cgabspath, filename);
ae5c8b8e 428 if (ret < 0 || ret >= MAXPATHLEN) {
b98f7d6e
SH
429 ERROR("pathname too long to set cgroup value %s to %s",
430 filename, value);
431 return -1;
ae5c8b8e 432 }
c8f7c563 433
b98f7d6e 434 return do_cgroup_set(path, value);
c8f7c563
CS
435}
436
ae5c8b8e 437/*
2acf7795 438 * lxc_cgroup_set: Write a value into a cgroup file
ae5c8b8e 439 *
2acf7795
DE
440 * @name : name of container to connect to
441 * @filename : the cgroup file to write (e.g. "freezer.state")
442 * @value : value to write into file
443 * @lxcpath : the lxcpath in which the container is running
ae5c8b8e
SH
444 *
445 * Returns 0 on success, < 0 on error.
446 */
ae5c8b8e
SH
447int lxc_cgroup_set(const char *name, const char *filename, const char *value,
448 const char *lxcpath)
c8f7c563 449{
ae5c8b8e 450 int ret;
2acf7795 451 char *cgabspath;
ae5c8b8e 452 char path[MAXPATHLEN];
b98f7d6e
SH
453 char *subsystem = alloca(strlen(filename)+1), *p;
454 strcpy(subsystem, filename);
455
456 if ((p = index(subsystem, '.')) != NULL)
457 *p = '\0';
ae5c8b8e 458
b98f7d6e 459 cgabspath = lxc_cgroup_path_get(subsystem, name, lxcpath);
2acf7795
DE
460 if (!cgabspath)
461 return -1;
ae5c8b8e 462
2acf7795 463 ret = snprintf(path, MAXPATHLEN, "%s/%s", cgabspath, filename);
ae5c8b8e
SH
464 if (ret < 0 || ret >= MAXPATHLEN) {
465 ERROR("pathname too long");
2acf7795
DE
466 ret = -1;
467 goto out;
ae5c8b8e
SH
468 }
469
2acf7795 470 ret = do_cgroup_set(path, value);
fd37327f 471
2acf7795
DE
472out:
473 free(cgabspath);
474 return ret;
c8f7c563
CS
475}
476
ae5c8b8e 477/*
2acf7795 478 * lxc_cgroup_get: Read value from a cgroup file
ae5c8b8e 479 *
2acf7795
DE
480 * @name : name of container to connect to
481 * @filename : the cgroup file to read (e.g. "freezer.state")
482 * @value : a pre-allocated buffer to copy the answer into
483 * @len : the length of pre-allocated @value
484 * @lxcpath : the lxcpath in which the container is running
ae5c8b8e 485 *
2acf7795 486 * Returns the number of bytes read on success, < 0 on error
ae5c8b8e 487 *
2acf7795
DE
488 * If you pass in NULL value or 0 len, the return value will be the size of
489 * the file, and @value will not contain the contents.
ae5c8b8e
SH
490 *
491 * Note that we can't get the file size quickly through stat or lseek.
492 * Therefore if you pass in len > 0 but less than the file size, your only
493 * indication will be that the return value will be equal to the passed-in ret.
494 * We will not return the actual full file size.
495 */
496int lxc_cgroup_get(const char *name, const char *filename, char *value,
497 size_t len, const char *lxcpath)
c8f7c563 498{
2acf7795
DE
499 int fd, ret;
500 char *cgabspath;
ae5c8b8e 501 char path[MAXPATHLEN];
b98f7d6e 502 char *subsystem = alloca(strlen(filename)+1), *p;
460a1cf0 503
b98f7d6e
SH
504 strcpy(subsystem, filename);
505
506 if ((p = index(subsystem, '.')) != NULL)
507 *p = '\0';
508
509 cgabspath = lxc_cgroup_path_get(subsystem, name, lxcpath);
2acf7795
DE
510 if (!cgabspath)
511 return -1;
fd4f5a56 512
2acf7795
DE
513 ret = snprintf(path, MAXPATHLEN, "%s/%s", cgabspath, filename);
514 if (ret < 0 || ret >= MAXPATHLEN) {
9ba8130c 515 ERROR("pathname too long");
2acf7795
DE
516 ret = -1;
517 goto out;
9ba8130c 518 }
fd4f5a56 519
ae5c8b8e 520 fd = open(path, O_RDONLY);
c8f7c563 521 if (fd < 0) {
ae5c8b8e 522 ERROR("open %s : %s", path, strerror(errno));
2acf7795
DE
523 ret = -1;
524 goto out;
fd4f5a56
DL
525 }
526
ae5c8b8e
SH
527 if (!len || !value) {
528 char buf[100];
529 int count = 0;
530 while ((ret = read(fd, buf, 100)) > 0)
531 count += ret;
532 if (ret >= 0)
533 ret = count;
534 } else {
535 memset(value, 0, len);
536 ret = read(fd, value, len);
fd4f5a56
DL
537 }
538
ae5c8b8e
SH
539 if (ret < 0)
540 ERROR("read %s : %s", path, strerror(errno));
541
542 close(fd);
2acf7795
DE
543out:
544 free(cgabspath);
ae5c8b8e 545 return ret;
c8f7c563
CS
546}
547
b98f7d6e 548int lxc_cgroup_nrtasks(struct lxc_handler *handler)
c8f7c563 549{
ae5c8b8e 550 char path[MAXPATHLEN];
2acf7795 551 int pid, ret;
ae5c8b8e 552 FILE *file;
c8f7c563 553
b98f7d6e 554 if (!handler->cgroup)
2acf7795 555 return -1;
c8f7c563 556
b98f7d6e
SH
557 /* XXX Should we use a specific subsystem rather than the first one we
558 * found (handler->cgroup->curcgroup)? */
559 ret = snprintf(path, MAXPATHLEN, "%s/tasks", handler->cgroup->curcgroup);
2acf7795 560 if (ret < 0 || ret >= MAXPATHLEN) {
ae5c8b8e 561 ERROR("pathname too long");
b98f7d6e 562 return -1;
ae5c8b8e 563 }
c8f7c563 564
ae5c8b8e
SH
565 file = fopen(path, "r");
566 if (!file) {
567 SYSERROR("fopen '%s' failed", path);
b98f7d6e 568 return -1;
c8f7c563
CS
569 }
570
2acf7795 571 ret = 0;
ae5c8b8e 572 while (fscanf(file, "%d", &pid) != EOF)
2acf7795 573 ret++;
fd4f5a56 574
ae5c8b8e 575 fclose(file);
2acf7795 576 return ret;
fd4f5a56
DL
577}
578
4f17323e
CS
579static int subsys_lists_match(const char *list1, const char *list2)
580{
581 char *token, *str, *saveptr = NULL;
582
583 if (!list1 || !list2)
584 return 0;
585
586 if (strlen(list1) != strlen(list2))
587 return 0;
588
589 str = alloca(strlen(list1)+1);
590 strcpy(str, list1);
591 for (; (token = strtok_r(str, ",", &saveptr)); str = NULL) {
592 if (in_subsys_list(token, list2) == 0)
593 return 0;
594 }
595
596 return 1;
597}
598
b98f7d6e 599static void set_clone_children(struct mntent *m)
fc7de561
SH
600{
601 char path[MAXPATHLEN];
602 FILE *fout;
603 int ret;
604
b98f7d6e
SH
605 if (!in_subsys_list("cpuset", m->mnt_opts))
606 return;
607 ret = snprintf(path, MAXPATHLEN, "%s/cgroup.clone_children", m->mnt_dir);
fc7de561
SH
608 if (ret < 0 || ret > MAXPATHLEN)
609 return;
610 fout = fopen(path, "w");
611 if (!fout)
612 return;
613 fprintf(fout, "1\n");
614 fclose(fout);
615}
616
b98f7d6e
SH
617static bool have_visited(char *opts, char *visited, char *all_subsystems)
618{
619 char *str, *s = NULL, *token;
620
621 str = alloca(strlen(opts)+1);
622 strcpy(str, opts);
623 for (; (token = strtok_r(str, ",", &s)); str = NULL) {
624 if (!in_subsys_list(token, all_subsystems))
625 continue;
626 if (visited && in_subsys_list(token, visited))
627 return true;
628 }
629
630 return false;
631}
632
633static bool is_in_desclist(struct cgroup_desc *d, char *opts, char *all_subsystems)
634{
635 while (d) {
636 if (have_visited(opts, d->subsystems, all_subsystems))
637 return true;
638 d = d->next;
639 }
640 return false;
641}
642
643static char *record_visited(char *opts, char *all_subsystems)
644{
645 char *s = NULL, *token, *str;
646 int oldlen = 0, newlen, toklen;
647 char *visited = NULL;
648
649 str = alloca(strlen(opts)+1);
650 strcpy(str, opts);
651 for (; (token = strtok_r(str, ",", &s)); str = NULL) {
652 if (!in_subsys_list(token, all_subsystems))
653 continue;
654 toklen = strlen(token);
655 newlen = oldlen + toklen + 1; // ',' + token or token + '\0'
656 visited = realloc(visited, newlen);
657 if (!visited)
658 return (char *)-ENOMEM;
659 if (oldlen)
660 strcat(visited, ",");
661 else
662 *visited = '\0';
663 strcat(visited, token);
e14f67a7 664 oldlen = newlen;
b98f7d6e
SH
665 }
666
667 return visited;
668}
55c76589 669
b98f7d6e 670static char *get_all_subsystems(void)
9a93d992
SH
671{
672 FILE *f;
673 char *line = NULL, *ret = NULL;
674 size_t len;
675 int first = 1;
676
677 /* read the list of subsystems from the kernel */
678 f = fopen("/proc/cgroups", "r");
679 if (!f)
680 return NULL;
681
682 while (getline(&line, &len, f) != -1) {
683 char *c;
684 int oldlen, newlen, inc;
685
686 /* skip the first line */
687 if (first) {
688 first=0;
689 continue;
690 }
691
692 c = strchr(line, '\t');
693 if (!c)
694 continue;
695 *c = '\0';
696
697 oldlen = ret ? strlen(ret) : 0;
698 newlen = oldlen + strlen(line) + 2;
699 ret = realloc(ret, newlen);
700 if (!ret)
701 goto out;
702 inc = snprintf(ret + oldlen, newlen, ",%s", line);
703 if (inc < 0 || inc >= newlen) {
704 free(ret);
705 ret = NULL;
706 goto out;
707 }
708 }
709
710out:
fa9ac567
SH
711 if (line)
712 free(line);
9a93d992
SH
713 fclose(f);
714 return ret;
715}
716
b98f7d6e
SH
717/*
718 * /etc/lxc/lxc.conf can contain lxc.cgroup.use = entries.
719 * If any of those are present, then lxc will ONLY consider
720 * cgroup filesystems mounted at one of the listed entries.
721 */
722static char *get_cgroup_uselist()
9a93d992 723{
b98f7d6e
SH
724 FILE *f;
725 char *line = NULL, *ret = NULL;
726 size_t sz = 0, retsz = 0, newsz;
9a93d992 727
b98f7d6e
SH
728 if ((f = fopen(LXC_GLOBAL_CONF, "r")) == NULL)
729 return NULL;
730 while (getline(&line, &sz, f) != -1) {
731 char *p = line;
732 while (*p && isblank(*p))
733 p++;
734 if (strncmp(p, "lxc.cgroup.use", 14) != 0)
735 continue;
736 p = index(p, '=');
737 if (!p)
738 continue;
739 p++;
740 while (*p && isblank(*p))
741 p++;
742 if (strlen(p) < 1)
743 continue;
744 newsz = retsz + strlen(p);
745 if (retsz == 0)
746 newsz += 1; // for trailing \0
747 // the last line in the file could lack \n
748 if (p[strlen(p)-1] != '\n')
749 newsz += 1;
750 ret = realloc(ret, newsz);
751 if (!ret) {
752 ERROR("Out of memory reading cgroup uselist");
753 fclose(f);
754 free(line);
755 return (char *)-ENOMEM;
756 }
757 if (retsz == 0)
758 strcpy(ret, p);
759 else
760 strcat(ret, p);
761 if (p[strlen(p)-1] != '\n')
762 ret[newsz-2] = '\0';
763 ret[newsz-1] = '\0';
764 retsz = newsz;
9a93d992
SH
765 }
766
b98f7d6e
SH
767 if (line)
768 free(line);
769 return ret;
9a93d992
SH
770}
771
b98f7d6e 772static bool is_in_uselist(char *uselist, struct mntent *m)
9a93d992 773{
b98f7d6e
SH
774 char *p;
775 if (!uselist)
776 return true;
777 if (!*uselist)
778 return false;
779 while (*uselist) {
780 p = index(uselist, '\n');
781 if (strncmp(m->mnt_dir, uselist, p - uselist) == 0)
782 return true;
783 uselist = p+1;
9a93d992 784 }
b98f7d6e 785 return false;
9a93d992
SH
786}
787
b98f7d6e 788static bool find_real_cgroup(struct cgroup_desc *d, char *path)
9a93d992 789{
b98f7d6e
SH
790 FILE *f;
791 char *line = NULL, *p, *p2;
792 int ret = 0;
793 size_t len;
9a93d992 794
b98f7d6e
SH
795 if ((f = fopen("/proc/self/cgroup", "r")) == NULL) {
796 SYSERROR("Error opening /proc/self/cgroups");
797 return false;
798 }
799
800 // If there is no subsystem, ignore the mount. Note we may want
801 // to change this, so that unprivileged users can use a unbound
802 // cgroup mount to arrange their container tasks.
803 if (!d->subsystems) {
804 fclose(f);
805 return false;
806 }
807 while (getline(&line, &len, f) != -1) {
808 if (!(p = index(line, ':')))
9a93d992 809 continue;
b98f7d6e 810 if (!(p2 = index(++p, ':')))
9a93d992 811 continue;
b98f7d6e 812 *p2 = '\0';
4f17323e
CS
813 // remove trailing newlines
814 if (*(p2 + 1) && p2[strlen(p2 + 1)] == '\n')
815 p2[strlen(p2 + 1)] = '\0';
b98f7d6e
SH
816 // in case of multiple mounts it may be more correct to
817 // insist all subsystems be the same
4f17323e 818 if (subsys_lists_match(p, d->subsystems))
b98f7d6e
SH
819 goto found;
820 }
9a93d992 821
b98f7d6e
SH
822 if (line)
823 free(line);
824 fclose(f);
825 return false;;
826
827found:
828 fclose(f);
829 ret = snprintf(path, MAXPATHLEN, "%s", p2+1);
830 if (ret < 0 || ret >= MAXPATHLEN) {
831 free(line);
832 return false;
833 }
834 free(line);
835 return true;
9a93d992
SH
836}
837
b98f7d6e 838
ae5c8b8e 839/*
b98f7d6e
SH
840 * for a given cgroup mount entry, and a to-be-created container,
841 * 1. Figure out full path of the cgroup we are currently in,
842 * 2. Find a new free cgroup which is $path / $lxc_name with an
843 * optional '-$n' where n is an ever-increasing integer.
ae5c8b8e 844 */
b98f7d6e 845static char *find_free_cgroup(struct cgroup_desc *d, const char *lxc_name)
460a1cf0 846{
b98f7d6e
SH
847 char tail[20], cgpath[MAXPATHLEN], *cgp, path[MAXPATHLEN];
848 int i = 0, ret;
849 size_t l;
fd37327f 850
b98f7d6e
SH
851 if (!find_real_cgroup(d, cgpath)) {
852 ERROR("Failed to find current cgroup");
853 return NULL;
460a1cf0
DW
854 }
855
b98f7d6e
SH
856 /*
857 * If d->mntpt is '/a/b/c/d', and the mountpoint is /x/y/z,
858 * then look for ourselves in:
859 * /x/y/z/a/b/c/d/tasks
860 * /x/y/z/b/c/d/tasks
861 * /x/y/z/c/d/tasks
862 * /x/y/z/d/tasks
863 * /x/y/z/tasks
864 */
865 cgp = cgpath;
866 while (cgp[0]) {
867 ret = snprintf(path, MAXPATHLEN, "%s%s/tasks", d->mntpt, cgp);
ae5c8b8e 868 if (ret < 0 || ret >= MAXPATHLEN)
b98f7d6e
SH
869 return NULL;
870 if (!is_in_cgroup(getpid(), path)) {
871 // does not exist, try the next one
872 cgp = index(cgp+1, '/');
873 if (!cgp)
874 break;
875 continue;
c8f7c563 876 }
b98f7d6e
SH
877 break;
878 }
879 if (!cgp || !*cgp) {
880 // try just the path
881 ret = snprintf(path, MAXPATHLEN, "%s/tasks", d->mntpt);
882 if (ret < 0 || ret >= MAXPATHLEN)
883 return NULL;
884 if (!is_in_cgroup(getpid(), path))
885 return NULL;
886 }
887 // found it
888 // path has '/tasks' at end, drop that
c32981c3 889 if (!(cgp = strrchr(path, '/'))) {
b98f7d6e
SH
890 ERROR("Got nonsensical path name %s\n", path);
891 return NULL;
892 }
893 *cgp = '\0';
c8f7c563 894
b98f7d6e
SH
895 if (strlen(path) + strlen(lxc_name) + 20 > MAXPATHLEN) {
896 ERROR("Error: cgroup path too long");
897 return NULL;
898 }
899 tail[0] = '\0';
900 while (1) {
901 struct stat sb;
902 int freebytes = MAXPATHLEN - (cgp - path);
903
904 if (i) {
905 ret = snprintf(tail, 20, "-%d", i);
906 if (ret < 0 || ret >= 20)
907 return NULL;
908 }
909 ret = snprintf(cgp, freebytes, "/%s%s", lxc_name, tail);
910 if (ret < 0 || ret >= freebytes)
911 return NULL;
912 if (stat(path, &sb) == -1)
913 break;
914 i++;
c8f7c563
CS
915 }
916
b98f7d6e
SH
917 l = strlen(cgpath);
918 ret = snprintf(cgpath + l, MAXPATHLEN - l, "/%s%s", lxc_name, tail);
919 if (ret < 0 || ret >= (MAXPATHLEN - l)) {
920 ERROR("Out of memory");
921 return NULL;
922 }
923 if ((d->realcgroup = strdup(cgpath)) == NULL) {
924 ERROR("Out of memory");
925 return NULL;
926 }
927 l = strlen(d->realcgroup);
928 if (l > 0 && d->realcgroup[l-1] == '\n')
929 d->realcgroup[l-1] = '\0';
930 return strdup(path);
c8f7c563
CS
931}
932
d08ba6ec 933/*
ae5c8b8e
SH
934 * For a new container, find a cgroup path which is unique in all cgroup mounts.
935 * I.e. if r1 is already running, then /lxc/r1-1 may be used.
936 *
937 * @lxcgroup: the cgroup 'group' the contaienr should run in. By default, this
938 * is just 'lxc'. Admins may wish to group some containers into other groups,
939 * i.e. 'build', to take advantage of cgroup hierarchy to simplify group
940 * administration. Also, unprivileged users who are placed into a cgroup by
941 * libcgroup_pam will be using that cgroup rather than the system-wide 'lxc'
942 * group.
943 * @name: the name of the container
944 *
945 * The chosen cgpath is returned as a strdup'd string. The caller will have to
946 * free that eventually, however the lxc monitor will keep that string so as to
947 * return it in response to a LXC_COMMAND_CGROUP query.
948 *
23622a2a
SH
949 * Note the path is relative to cgroup mounts. I.e. if the freezer subsystem
950 * is at /sys/fs/cgroup/freezer, and this fn returns '/lxc/r1', then the
951 * freezer cgroup's full path will be /sys/fs/cgroup/freezer/lxc/r1/.
ae5c8b8e 952 *
ae5c8b8e 953 * Races won't be determintal, you'll just end up with leftover unused cgroups
d08ba6ec 954 */
b98f7d6e 955struct cgroup_desc *lxc_cgroup_path_create(const char *name)
d08ba6ec 956{
b98f7d6e 957 struct cgroup_desc *retdesc = NULL, *newdesc = NULL;
ae5c8b8e 958 FILE *file = NULL;
93d564ed 959 struct mntent mntent_r;
fd37327f 960 char buf[LARGE_MAXPATHLEN] = {0};
b98f7d6e
SH
961 char *all_subsystems = get_all_subsystems();
962 char *cgroup_uselist = get_cgroup_uselist();
d08ba6ec 963
b98f7d6e
SH
964 if (cgroup_uselist == (char *)-ENOMEM) {
965 if (all_subsystems)
966 free(all_subsystems);
967 return NULL;
968 }
969 if (!all_subsystems) {
970 ERROR("failed to get a list of all cgroup subsystems");
971 if (cgroup_uselist)
972 free(cgroup_uselist);
9a93d992 973 return NULL;
9a93d992 974 }
ae5c8b8e
SH
975 file = setmntent(MTAB, "r");
976 if (!file) {
977 SYSERROR("failed to open %s", MTAB);
b98f7d6e
SH
978 free(all_subsystems);
979 if (cgroup_uselist)
980 free(cgroup_uselist);
981 return NULL;
d08ba6ec 982 }
1ac470c0 983
93d564ed 984 while ((getmntent_r(file, &mntent_r, buf, sizeof(buf)))) {
fd4f5a56 985
fd37327f 986 if (strcmp(mntent_r.mnt_type, "cgroup"))
ae5c8b8e 987 continue;
b98f7d6e
SH
988
989 if (cgroup_uselist && !is_in_uselist(cgroup_uselist, &mntent_r))
ae5c8b8e 990 continue;
e4659536 991
9a93d992 992 /* make sure we haven't checked this subsystem already */
b98f7d6e 993 if (is_in_desclist(retdesc, mntent_r.mnt_opts, all_subsystems))
9a93d992 994 continue;
9a93d992 995
b98f7d6e
SH
996 if (!(newdesc = malloc(sizeof(struct cgroup_desc)))) {
997 ERROR("Out of memory reading cgroups");
ae5c8b8e 998 goto fail;
b98f7d6e
SH
999 }
1000 newdesc->subsystems = record_visited(mntent_r.mnt_opts, all_subsystems);
1001 if (newdesc->subsystems == (char *)-ENOMEM) {
1002 ERROR("Out of memory recording cgroup subsystems");
1003 free(newdesc);
1004 newdesc = NULL;
1005 goto fail;
1006 }
1007 if (!newdesc->subsystems) {
1008 free(newdesc);
1009 newdesc = NULL;
1010 continue;
1011 }
1012 newdesc->mntpt = strdup(mntent_r.mnt_dir);
1013 newdesc->realcgroup = NULL;
1014 newdesc->curcgroup = find_free_cgroup(newdesc, name);
1015 if (!newdesc->mntpt || !newdesc->curcgroup) {
1016 ERROR("Out of memory reading cgroups");
1017 goto fail;
1018 }
5193cc3d 1019
b98f7d6e 1020 set_clone_children(&mntent_r);
fd4f5a56 1021
b98f7d6e
SH
1022 if (mkdir(newdesc->curcgroup, 0755)) {
1023 ERROR("Error creating cgroup %s", newdesc->curcgroup);
ae5c8b8e 1024 goto fail;
e7f40d8a 1025 }
b98f7d6e
SH
1026 newdesc->next = retdesc;
1027 retdesc = newdesc;
d08ba6ec
SH
1028 }
1029
ae5c8b8e 1030 endmntent(file);
b98f7d6e
SH
1031 free(all_subsystems);
1032 if (cgroup_uselist)
1033 free(cgroup_uselist);
1034 return retdesc;
ae5c8b8e
SH
1035
1036fail:
1037 endmntent(file);
b98f7d6e
SH
1038 free(all_subsystems);
1039 if (cgroup_uselist)
1040 free(cgroup_uselist);
1041 if (newdesc) {
1042 if (newdesc->mntpt)
1043 free(newdesc->mntpt);
1044 if (newdesc->subsystems)
1045 free(newdesc->subsystems);
1046 if (newdesc->curcgroup)
1047 free(newdesc->curcgroup);
1048 if (newdesc->realcgroup)
1049 free(newdesc->realcgroup);
1050 free(newdesc);
1051 }
1052 while (retdesc) {
1053 struct cgroup_desc *t = retdesc;
1054 retdesc = retdesc->next;
1055 if (t->mntpt)
1056 free(t->mntpt);
1057 if (t->subsystems)
1058 free(t->subsystems);
1059 if (t->curcgroup)
1060 free(t->curcgroup);
1061 if (t->realcgroup)
1062 free(t->realcgroup);
1063 free(t);
1064
1065 }
ae5c8b8e 1066 return NULL;
36b86299
DL
1067}
1068
b98f7d6e 1069static bool lxc_cgroup_enter_one(const char *dir, int pid)
36b86299 1070{
23622a2a 1071 char path[MAXPATHLEN];
b98f7d6e
SH
1072 int ret;
1073 FILE *fout;
ef342abb 1074
b98f7d6e
SH
1075 ret = snprintf(path, MAXPATHLEN, "%s/tasks", dir);
1076 if (ret < 0 || ret >= MAXPATHLEN) {
1077 ERROR("Error entering cgroup");
1078 return false;
ef342abb 1079 }
b98f7d6e
SH
1080 fout = fopen(path, "w");
1081 if (!fout) {
1082 SYSERROR("Error entering cgroup");
1083 return false;
1084 }
1085 if (fprintf(fout, "%d\n", (int)pid) < 0) {
1086 ERROR("Error writing pid to %s to enter cgroup", path);
1087 fclose(fout);
1088 return false;
1089 }
1090 if (fclose(fout) < 0) {
1091 SYSERROR("Error writing pid to %s to enter cgroup", path);
1092 return false;
ae5c8b8e 1093 }
f0e64b8b 1094
b98f7d6e
SH
1095 return true;
1096}
1097
1098int lxc_cgroup_enter(struct cgroup_desc *cgroups, pid_t pid)
1099{
1100 while (cgroups) {
1101 if (!cgroups->subsystems)
1102 goto next;
1103
1104 if (!lxc_cgroup_enter_one(cgroups->curcgroup, pid))
1105 return -1;
1106next:
1107 cgroups = cgroups->next;
1108 }
1109 return 0;
bcbd102c
SH
1110}
1111
60bf62d4 1112static int cgroup_rmdir(char *dirname)
341a9bd8
SH
1113{
1114 struct dirent dirent, *direntp;
341a9bd8
SH
1115 DIR *dir;
1116 int ret;
1117 char pathname[MAXPATHLEN];
1118
1119 dir = opendir(dirname);
1120 if (!dir) {
1121 WARN("failed to open directory: %m");
1122 return -1;
1123 }
1124
341a9bd8
SH
1125 while (!readdir_r(dir, &dirent, &direntp)) {
1126 struct stat mystat;
9ba8130c 1127 int rc;
341a9bd8
SH
1128
1129 if (!direntp)
1130 break;
1131
1132 if (!strcmp(direntp->d_name, ".") ||
b98f7d6e 1133 !strcmp(direntp->d_name, ".."))
341a9bd8
SH
1134 continue;
1135
9ba8130c
SH
1136 rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
1137 if (rc < 0 || rc >= MAXPATHLEN) {
1138 ERROR("pathname too long");
1139 continue;
1140 }
341a9bd8
SH
1141 ret = stat(pathname, &mystat);
1142 if (ret)
1143 continue;
1144 if (S_ISDIR(mystat.st_mode))
60bf62d4 1145 cgroup_rmdir(pathname);
341a9bd8
SH
1146 }
1147
1148 ret = rmdir(dirname);
1149
1150 if (closedir(dir))
1151 ERROR("failed to close directory");
1152 return ret;
341a9bd8 1153}
bcbd102c 1154
bcbd102c
SH
1155/*
1156 * for each mounted cgroup, destroy the cgroup for the container
1157 */
b98f7d6e 1158void lxc_cgroup_destroy_desc(struct cgroup_desc *cgroups)
a6ddef61 1159{
b98f7d6e
SH
1160 while (cgroups) {
1161 struct cgroup_desc *next = cgroups->next;
1162 if (cgroup_rmdir(cgroups->curcgroup) < 0)
1163 SYSERROR("Error removing cgroup directory %s", cgroups->curcgroup);
1164 free(cgroups->mntpt);
1165 free(cgroups->subsystems);
1166 free(cgroups->curcgroup);
1167 free(cgroups->realcgroup);
1168 free(cgroups);
1169 cgroups = next;
bcbd102c 1170 }
a6ddef61
MN
1171}
1172
ae5c8b8e 1173int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath)
576f946d 1174{
b98f7d6e 1175 FILE *f;
4f17323e 1176 char *line = NULL, ret = 0;
b98f7d6e
SH
1177 size_t len = 0;
1178 int first = 1;
ef6e34ee 1179 char *dirpath;
576f946d 1180
b98f7d6e
SH
1181 /* read the list of subsystems from the kernel */
1182 f = fopen("/proc/cgroups", "r");
1183 if (!f)
e14f67a7 1184 return -1;
b98f7d6e
SH
1185
1186 while (getline(&line, &len, f) != -1) {
1187 char *c;
1188
1189 /* skip the first line */
1190 if (first) {
1191 first=0;
1192 continue;
1193 }
1194
1195 c = strchr(line, '\t');
1196 if (!c)
1197 continue;
1198 *c = '\0';
1199 dirpath = lxc_cgroup_path_get(line, name, lxcpath);
1200 if (!dirpath)
1201 continue;
1202
1203 INFO("joining pid %d to cgroup %s", pid, dirpath);
4f17323e 1204 if (!lxc_cgroup_enter_one(dirpath, pid)) {
b98f7d6e 1205 ERROR("Failed joining %d to %s\n", pid, dirpath);
4f17323e 1206 ret = -1;
b98f7d6e
SH
1207 continue;
1208 }
8b92dc3a 1209 }
576f946d 1210
b98f7d6e
SH
1211 if (line)
1212 free(line);
1213 fclose(f);
ef6e34ee 1214 return ret;
e0f888d9 1215}
283678ed 1216
b98f7d6e 1217bool is_in_subcgroup(int pid, const char *subsystem, struct cgroup_desc *d)
283678ed
SH
1218{
1219 char filepath[MAXPATHLEN], *line = NULL, v1[MAXPATHLEN], v2[MAXPATHLEN];
1220 FILE *f;
1221 int ret, junk;
b98f7d6e 1222 size_t sz = 0, l1, l2;
283678ed
SH
1223 char *end = index(subsystem, '.');
1224 int len = end ? (end - subsystem) : strlen(subsystem);
b98f7d6e
SH
1225 const char *cgpath = NULL;
1226
1227 while (d) {
1228 if (in_subsys_list("devices", d->subsystems)) {
1229 cgpath = d->realcgroup;
1230 l1 = strlen(cgpath);
1231 break;
1232 }
1233 d = d->next;
1234 }
1235 if (!d)
1236 return false;
283678ed
SH
1237
1238 ret = snprintf(filepath, MAXPATHLEN, "/proc/%d/cgroup", pid);
1239 if (ret < 0 || ret >= MAXPATHLEN)
1240 return false;
1241 if ((f = fopen(filepath, "r")) == NULL)
1242 return false;
1243 while (getline(&line, &sz, f) != -1) {
1244 // nr:subsystem:path
1245 v2[0] = v2[1] = '\0';
1246 ret = sscanf(line, "%d:%[^:]:%s", &junk, v1, v2);
1247 if (ret != 3) {
1248 fclose(f);
b98f7d6e 1249 free(line);
283678ed
SH
1250 return false;
1251 }
1252 len = end ? end - subsystem : strlen(subsystem);
1253 if (strncmp(v1, subsystem, len) != 0)
1254 continue;
1255 // v2 will start with '/', skip it by using v2+1
1256 // we must be in SUBcgroup, so make sure l2 > l1
1257 l2 = strlen(v2+1);
1258 if (l2 > l1 && strncmp(v2+1, cgpath, l1) == 0) {
1259 fclose(f);
b98f7d6e 1260 free(line);
283678ed
SH
1261 return true;
1262 }
1263 }
1264 fclose(f);
b98f7d6e
SH
1265 if (line)
1266 free(line);
283678ed
SH
1267 return false;
1268}
b113383b 1269
b98f7d6e 1270char *cgroup_get_subsys_path(struct lxc_handler *handler, const char *subsys)
b113383b 1271{
b98f7d6e 1272 struct cgroup_desc *d;
b113383b 1273
b98f7d6e
SH
1274 for (d = handler->cgroup; d; d = d->next) {
1275 if (in_subsys_list(subsys, d->subsystems))
1276 return d->realcgroup;
1277 }
b113383b 1278
b98f7d6e
SH
1279 return NULL;
1280}
1281
1282static int _setup_cgroup(struct lxc_handler *h, struct lxc_list *cgroups,
1283 int devices)
1284{
1285 struct lxc_list *iterator;
1286 struct lxc_cgroup *cg;
1287 int ret = -1;
1288
1289 if (lxc_list_empty(cgroups))
1290 return 0;
1291
1292 lxc_list_for_each(iterator, cgroups) {
1293 cg = iterator->elem;
1294
1295 if (devices == !strncmp("devices", cg->subsystem, 7)) {
1296 if (strcmp(cg->subsystem, "devices.deny") == 0 &&
1297 cgroup_devices_has_deny(h, cg->value))
1298 continue;
1299 if (strcmp(cg->subsystem, "devices.allow") == 0 &&
1300 cgroup_devices_has_allow(h, cg->value))
1301 continue;
1302 if (lxc_cgroup_set_value(h, cg->subsystem, cg->value)) {
1303 ERROR("Error setting %s to %s for %s\n",
1304 cg->subsystem, cg->value, h->name);
1305 goto out;
1306 }
b113383b 1307 }
b98f7d6e
SH
1308
1309 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
b113383b
SH
1310 }
1311
b98f7d6e
SH
1312 ret = 0;
1313 INFO("cgroup has been setup");
1314out:
b113383b
SH
1315 return ret;
1316}
b98f7d6e
SH
1317
1318int setup_cgroup_devices(struct lxc_handler *h, struct lxc_list *cgroups)
1319{
1320 return _setup_cgroup(h, cgroups, 1);
1321}
1322
1323int setup_cgroup(struct lxc_handler *h, struct lxc_list *cgroups)
1324{
1325 return _setup_cgroup(h, cgroups, 0);
1326}