]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/cgfsng.c
nesting: remove the nesting hint from configuration templates
[mirror_lxc.git] / src / lxc / cgfsng.c
CommitLineData
ccb4cabe
SH
1/*
2 * lxc: linux Container library
3 *
4 * Copyright © 2016 Canonical Ltd.
5 *
6 * Authors:
7 * Serge Hallyn <serge.hallyn@ubuntu.com>
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
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24/*
25 * cgfs-ng.c: this is a new, simplified implementation of a filesystem
26 * cgroup backend. The original cgfs.c was designed to be as flexible
27 * as possible. It would try to find cgroup filesystems no matter where
28 * or how you had them mounted, and deduce the most usable mount for
29 * each controller. It also was not designed for unprivileged use, as
30 * that was reserved for cgmanager.
31 *
32 * This new implementation assumes that cgroup filesystems are mounted
33 * under /sys/fs/cgroup/clist where clist is either the controller, or
34 * a comman-separated list of controllers.
35 */
36#include "config.h"
37#include <stdio.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <errno.h>
41#include <sys/types.h>
42#include <unistd.h>
43#include <dirent.h>
44#include <grp.h>
45
46#include "log.h"
47#include "cgroup.h"
48#include "utils.h"
49#include "commands.h"
50
51lxc_log_define(lxc_cgfsng, lxc);
52
53static struct cgroup_ops cgfsng_ops;
54
ccb4cabe
SH
55/*
56 * A descriptor for a mounted hierarchy
57 * @controllers: either NULL, or a null-terminated list of all
58 * the co-mounted controllers
59 * @mountpoint: the mountpoint we will use. It will be either
60 * /sys/fs/cgroup/controller or /sys/fs/cgroup/controllerlist
61 * @base_cgroup: the cgroup under which the container cgroup path
62 is created. This will be either the caller's cgroup (if not
63 root), or init's cgroup (if root).
64 */
65struct hierarchy {
66 char **controllers;
67 char *mountpoint;
68 char *base_cgroup;
69 char *fullcgpath;
70};
71
72/*
73 * The cgroup data which is attached to the lxc_handler.
74 * @hierarchies - a NULL-terminated array of struct hierarchy, one per
75 * hierarchy. No duplicates. First sufficient, writeable mounted
76 * hierarchy wins
77 * @cgroup_use - a copy of the lxc.cgroup.use
78 * @cgroup_pattern - a copy of the lxc.cgroup.pattern
79 * @container_cgroup - if not null, the cgroup which was created for
80 * the container. For each hierarchy, it is created under the
81 * @hierarchy->base_cgroup directory. Relative to the base_cgroup
82 * it is the same for all hierarchies.
83 * @name - the container name
84 */
85struct cgfsng_handler_data {
86 struct hierarchy **hierarchies;
87 char *cgroup_use;
88 char *cgroup_pattern;
89 char *container_cgroup; // cgroup we created for the container
90 char *name; // container name
91};
92
93static void free_string_list(char **clist)
94{
95 if (clist) {
96 int i;
97
98 for (i = 0; clist[i]; i++)
99 free(clist[i]);
100 free(clist);
101 }
102}
103
104/* Re-alllocate a pointer, do not fail */
105static void *must_realloc(void *orig, size_t sz)
106{
107 void *ret;
108
109 do {
110 ret = realloc(orig, sz);
111 } while (!ret);
112 return ret;
113}
114
115/* Allocate a pointer, do not fail */
116static void *must_alloc(size_t sz)
117{
118 return must_realloc(NULL, sz);
119}
120
121/* return copy of string @entry; do not fail. */
122static char *must_copy_string(const char *entry)
123{
124 char *ret;
125
126 if (!entry)
127 return NULL;
128 do {
129 ret = strdup(entry);
130 } while (!ret);
131 return ret;
132}
133
134/*
135 * This is a special case - return a copy of @entry
136 * prepending 'name='. I.e. turn systemd into name=systemd.
137 * Do not fail.
138 */
139static char *must_prefix_named(char *entry)
140{
141 char *ret;
142 size_t len = strlen(entry);
143
144 ret = must_alloc(len + 6);
145 snprintf(ret, len + 6, "name=%s", entry);
146 return ret;
147}
148
149/*
150 * Given a pointer to a null-terminated array of pointers, realloc to
151 * add one entry, and point the new entry to NULL. Do not fail. Return
152 * the index to the second-to-last entry - that is, the one which is
153 * now available for use (keeping the list null-terminated).
154 */
155static int append_null_to_list(void ***list)
156{
157 int newentry = 0;
158
159 if (*list)
160 for (; (*list)[newentry]; newentry++);
161
162 *list = must_realloc(*list, (newentry + 2) * sizeof(void **));
163 (*list)[newentry + 1] = NULL;
164 return newentry;
165}
166
167/*
168 * Given a null-terminated array of strings, check whether @entry
169 * is one of the strings
170 */
171static bool string_in_list(char **list, const char *entry)
172{
173 int i;
174
175 if (!list)
176 return false;
177 for (i = 0; list[i]; i++)
178 if (strcmp(list[i], entry) == 0)
179 return true;
180
181 return false;
182}
183
184/*
185 * append an entry to the clist. Do not fail.
186 * *clist must be NULL the first time we are called.
187 *
188 * We also handle named subsystems here. Any controller which is not a
189 * kernel subsystem, we prefix 'name='. Any which is both a kernel and
190 * named subsystem, we refuse to use because we're not sure which we
191 * have here. (TODO - we could work around this in some cases by just
192 * remounting to be unambiguous, or by comparing mountpoint contents
193 * with current cgroup)
194 *
195 * The last entry will always be NULL.
196 */
197static void must_append_controller(char **klist, char **nlist, char ***clist, char *entry)
198{
199 int newentry;
200 char *copy;
201
202 if (string_in_list(klist, entry) && string_in_list(nlist, entry)) {
203 ERROR("Refusing to use ambiguous controller '%s'", entry);
204 ERROR("It is both a named and kernel subsystem");
205 return;
206 }
207
208 newentry = append_null_to_list((void ***)clist);
209
210 if (strncmp(entry, "name=", 5) == 0)
211 copy = must_copy_string(entry);
212 else if (string_in_list(klist, entry))
213 copy = must_copy_string(entry);
214 else
215 copy = must_prefix_named(entry);
216
217 (*clist)[newentry] = copy;
218}
219
220static void free_hierarchies(struct hierarchy **hlist)
221{
222 if (hlist) {
223 int i;
224
225 for (i = 0; hlist[i]; i++) {
226 free(hlist[i]->mountpoint);
227 free(hlist[i]->base_cgroup);
228 free(hlist[i]->fullcgpath);
229 free_string_list(hlist[i]->controllers);
230 }
231 free(hlist);
232 }
233}
234
235static void free_handler_data(struct cgfsng_handler_data *d)
236{
237 free_hierarchies(d->hierarchies);
238 free(d->cgroup_use);
239 free(d->cgroup_pattern);
240 free(d->container_cgroup);
241 free(d->name);
242 free(d);
243}
244
245/*
246 * Given a handler's cgroup data, return the struct hierarchy for the
247 * controller @c, or NULL if there is none.
248 */
249struct hierarchy *get_hierarchy(struct cgfsng_handler_data *d, const char *c)
250{
251 int i;
252
253 if (!d || !d->hierarchies)
254 return NULL;
255 for (i = 0; d->hierarchies[i]; i++) {
256 if (string_in_list(d->hierarchies[i]->controllers, c))
257 return d->hierarchies[i];
258 }
259 return NULL;
260}
261
e3a3fecf
SH
262static char *must_make_path(const char *first, ...) __attribute__((sentinel));
263
264/* Copy contents of parent(@path)/@file to @path/@file */
265static bool copy_parent_file(char *path, char *file)
266{
267 char *lastslash, *value = NULL, *fpath, oldv;
268 int len = 0;
269 int ret;
270
271 lastslash = strrchr(path, '/');
272 if (!lastslash) { // bug... this shouldn't be possible
273 ERROR("cgfsng:copy_parent_file: bad path %s", path);
274 return false;
275 }
276 oldv = *lastslash;
277 *lastslash = '\0';
278 fpath = must_make_path(path, file, NULL);
279 len = lxc_read_from_file(fpath, NULL, 0);
280 if (len <= 0)
281 goto bad;
282 value = must_alloc(len + 1);
283 if (lxc_read_from_file(fpath, value, len) != len)
284 goto bad;
285 free(fpath);
286 *lastslash = oldv;
287 fpath = must_make_path(path, file, NULL);
288 ret = lxc_write_to_file(fpath, value, len, false);
289 if (ret < 0)
290 SYSERROR("Unable to write %s to %s", value, fpath);
291 free(fpath);
292 free(value);
293 return ret >= 0;
294
295bad:
296 SYSERROR("Error reading '%s'", fpath);
297 free(fpath);
298 free(value);
299 return false;
300}
301
302/*
303 * Initialize the cpuset hierarchy in first directory of @gname and
304 * set cgroup.clone_children so that children inherit settings.
305 * Since the h->base_path is populated by init or ourselves, we know
306 * it is already initialized.
307 */
308bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname)
309{
310 char *cgpath, *clonechildrenpath, v, *slash;
311
312 if (!string_in_list(h->controllers, "cpuset"))
313 return true;
314
315 if (*cgname == '/')
316 cgname++;
317 slash = strchr(cgname, '/');
318 if (slash)
319 *slash = '\0';
320
321 cgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL);
322 if (slash)
323 *slash = '/';
324 if (mkdir(cgpath, 0755) < 0 && errno != EEXIST) {
325 SYSERROR("Failed to create '%s'", cgpath);
326 free(cgpath);
327 return false;
328 }
329 clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL);
330 if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */
331 free(clonechildrenpath);
332 free(cgpath);
333 return true;
334 }
335 if (lxc_read_from_file(clonechildrenpath, &v, 1) < 0) {
336 SYSERROR("Failed to read '%s'", clonechildrenpath);
337 free(clonechildrenpath);
338 free(cgpath);
339 return false;
340 }
341
342 if (v == '1') { /* already set for us by someone else */
343 free(clonechildrenpath);
344 free(cgpath);
345 return true;
346 }
347
348 /* copy parent's settings */
349 if (!copy_parent_file(cgpath, "cpuset.cpus") ||
350 !copy_parent_file(cgpath, "cpuset.mems")) {
351 free(cgpath);
352 free(clonechildrenpath);
353 return false;
354 }
355 free(cgpath);
356
357 if (lxc_write_to_file(clonechildrenpath, "1", 1, false) < 0) {
358 /* Set clone_children so children inherit our settings */
359 SYSERROR("Failed to write 1 to %s", clonechildrenpath);
360 free(clonechildrenpath);
361 return false;
362 }
363 free(clonechildrenpath);
364 return true;
365}
366
ccb4cabe
SH
367/*
368 * Given two null-terminated lists of strings, return true if any string
369 * is in both.
370 */
371static bool controller_lists_intersect(char **l1, char **l2)
372{
373 int i;
374
375 if (!l1 || !l2)
376 return false;
377
378 for (i = 0; l1[i]; i++) {
379 if (string_in_list(l2, l1[i]))
380 return true;
381 }
382 return false;
383}
384
385/*
386 * For a null-terminated list of controllers @clist, return true if any of
387 * those controllers is already listed the null-terminated list of
388 * hierarchies @hlist. Realistically, if one is present, all must be present.
389 */
390static bool controller_list_is_dup(struct hierarchy **hlist, char **clist)
391{
392 int i;
393
394 if (!hlist)
395 return false;
396 for (i = 0; hlist[i]; i++)
397 if (controller_lists_intersect(hlist[i]->controllers, clist))
398 return true;
399 return false;
400
401}
402
403/*
404 * Return true if the controller @entry is found in the null-terminated
405 * list of hierarchies @hlist
406 */
407static bool controller_found(struct hierarchy **hlist, char *entry)
408{
409 int i;
410 if (!hlist)
411 return false;
412
413 for (i = 0; hlist[i]; i++)
414 if (string_in_list(hlist[i]->controllers, entry))
415 return true;
416 return false;
417}
418
419/*
420 * Return true if all of the controllers which we require have been
421 * found. The required list is systemd, freezer, and anything in
422 * lxc.cgroup.use.
423 */
424static bool all_controllers_found(struct cgfsng_handler_data *d)
425{
426 char *p, *saveptr = NULL;
427 struct hierarchy ** hlist = d->hierarchies;
428
429 if (!controller_found(hlist, "name=systemd")) {
430 ERROR("no systemd controller mountpoint found");
431 return false;
432 }
433 if (!controller_found(hlist, "freezer")) {
434 ERROR("no freezer controller mountpoint found");
435 return false;
436 }
437
438 if (!d->cgroup_use)
439 return true;
440 for (p = strtok_r(d->cgroup_use, ",", &saveptr); p;
441 p = strtok_r(NULL, ",", &saveptr)) {
442 if (!controller_found(hlist, p)) {
443 ERROR("no %s controller mountpoint found", p);
444 return false;
445 }
446 }
447 return true;
448}
449
450/* Return true if the fs type is fuse.lxcfs */
451static bool is_lxcfs(const char *line)
452{
453 char *p = strstr(line, " - ");
454 if (!p)
455 return false;
456 return strncmp(p, " - fuse.lxcfs ", 14);
457}
458
459/*
460 * Get the controllers from a mountinfo line
461 * There are other ways we could get this info. For lxcfs, field 3
462 * is /cgroup/controller-list. For cgroupfs, we could parse the mount
463 * options. But we simply assume that the mountpoint must be
464 * /sys/fs/cgroup/controller-list
465 */
466static char **get_controllers(char **klist, char **nlist, char *line)
467{
468 // the fourth field is /sys/fs/cgroup/comma-delimited-controller-list
469 int i;
470 char *p = line, *p2, *tok, *saveptr = NULL;
471 char **aret = NULL;
472
473 for (i = 0; i < 4; i++) {
235f1815 474 p = strchr(p, ' ');
ccb4cabe
SH
475 if (!p)
476 return NULL;
477 p++;
478 }
479 if (!p)
480 return NULL;
481 /* note - if we change how mountinfo works, then our caller
482 * will need to verify /sys/fs/cgroup/ in this field */
483 if (strncmp(p, "/sys/fs/cgroup/", 15) != 0)
484 return NULL;
485 p += 15;
235f1815 486 p2 = strchr(p, ' ');
ccb4cabe
SH
487 if (!p2) {
488 ERROR("corrupt mountinfo");
489 return NULL;
490 }
491 *p2 = '\0';
492 for (tok = strtok_r(p, ",", &saveptr); tok;
493 tok = strtok_r(NULL, ",", &saveptr)) {
494 must_append_controller(klist, nlist, &aret, tok);
495 }
496
497 return aret;
498}
499
500/* return true if the fstype is cgroup */
501static bool is_cgroupfs(char *line)
502{
503 char *p = strstr(line, " - ");
504 if (!p)
505 return false;
506 return strncmp(p, " - cgroup ", 10);
507}
508
509/* Add a controller to our list of hierarchies */
510static void add_controller(struct cgfsng_handler_data *d, char **clist,
511 char *mountpoint, char *base_cgroup)
512{
513 struct hierarchy *new;
514 int newentry;
515
516 new = must_alloc(sizeof(*new));
517 new->controllers = clist;
518 new->mountpoint = mountpoint;
519 new->base_cgroup = base_cgroup;
520 new->fullcgpath = NULL;
521
522 newentry = append_null_to_list((void ***)&d->hierarchies);
523 d->hierarchies[newentry] = new;
524}
525
526/*
527 * Get a copy of the mountpoint from @line, which is a line from
528 * /proc/self/mountinfo
529 */
530static char *get_mountpoint(char *line)
531{
532 int i;
533 char *p = line, *sret;
534 size_t len;
535
536 for (i = 0; i < 4; i++) {
235f1815 537 p = strchr(p, ' ');
ccb4cabe
SH
538 if (!p)
539 return NULL;
540 p++;
541 }
542 /* we've already stuck a \0 after the mountpoint */
543 len = strlen(p);
544 sret = must_alloc(len + 1);
545 memcpy(sret, p, len);
546 sret[len] = '\0';
547 return sret;
548}
549
550/*
551 * Given a multi-line string, return a null-terminated copy of the
552 * current line.
553 */
554static char *copy_to_eol(char *p)
555{
235f1815 556 char *p2 = strchr(p, '\n'), *sret;
ccb4cabe
SH
557 size_t len;
558
559 if (!p2)
560 return NULL;
561
562 len = p2 - p;
563 sret = must_alloc(len + 1);
564 memcpy(sret, p, len);
565 sret[len] = '\0';
566 return sret;
567}
568
569/*
570 * cgline: pointer to character after the first ':' in a line in a
571 * \n-terminated /proc/self/cgroup file. Check whether * controller c is
572 * present.
573 */
574static bool controller_in_clist(char *cgline, char *c)
575{
576 char *tok, *saveptr = NULL, *eol, *tmp;
577 size_t len;
578
235f1815 579 eol = strchr(cgline, ':');
ccb4cabe
SH
580 if (!eol)
581 return false;
582
583 len = eol - cgline;
584 tmp = alloca(len + 1);
585 memcpy(tmp, cgline, len);
586 tmp[len] = '\0';
587
588 for (tok = strtok_r(tmp, ",", &saveptr); tok;
589 tok = strtok_r(NULL, ",", &saveptr)) {
590 if (strcmp(tok, c) == 0)
591 return true;
592 }
593 return false;
594}
595
596/*
597 * @basecginfo is a copy of /proc/$$/cgroup. Return the current
598 * cgroup for @controller
599 */
600static char *get_current_cgroup(char *basecginfo, char *controller)
601{
602 char *p = basecginfo;
603
604 while (1) {
235f1815 605 p = strchr(p, ':');
ccb4cabe
SH
606 if (!p)
607 return NULL;
608 p++;
609 if (controller_in_clist(p, controller)) {
235f1815 610 p = strchr(p, ':');
ccb4cabe
SH
611 if (!p)
612 return NULL;
613 p++;
614 return copy_to_eol(p);
615 }
616
235f1815 617 p = strchr(p, '\n');
ccb4cabe
SH
618 if (!p)
619 return NULL;
620 p++;
621 }
622}
623
b4ffcca8
SH
624#define BATCH_SIZE 50
625static void batch_realloc(char **mem, size_t oldlen, size_t newlen)
626{
627 int newbatches = (newlen / BATCH_SIZE) + 1;
628 int oldbatches = (oldlen / BATCH_SIZE) + 1;
629
630 if (!*mem || newbatches > oldbatches) {
631 *mem = must_realloc(*mem, newbatches * BATCH_SIZE);
632 }
633}
634
ccb4cabe
SH
635static void append_line(char **dest, size_t oldlen, char *new, size_t newlen)
636{
637 size_t full = oldlen + newlen;
638
b4ffcca8 639 batch_realloc(dest, oldlen, full + 1);
ccb4cabe 640
b4ffcca8 641 memcpy(*dest + oldlen, new, newlen + 1);
ccb4cabe
SH
642}
643
644/* Slurp in a whole file */
645static char *read_file(char *fnam)
646{
647 FILE *f;
648 char *line = NULL, *buf = NULL;
649 size_t len = 0, fulllen = 0;
b4ffcca8 650 int linelen;
ccb4cabe
SH
651
652 f = fopen(fnam, "r");
653 if (!f)
654 return NULL;
b4ffcca8
SH
655 while ((linelen = getline(&line, &len, f)) != -1) {
656 append_line(&buf, fulllen, line, linelen);
657 fulllen += linelen;
ccb4cabe
SH
658 }
659 fclose(f);
660 free(line);
661 return buf;
662}
663
ccb4cabe
SH
664/*
665 * Given a hierarchy @mountpoint and base @path, verify that we can create
666 * directories underneath it.
667 */
668static bool test_writeable(char *mountpoint, char *path)
669{
670 char *fullpath = must_make_path(mountpoint, path, NULL);
671 int ret;
672
673 ret = access(fullpath, W_OK);
674 free(fullpath);
675 return ret == 0;
676}
677
678static void must_append_string(char ***list, char *entry)
679{
680 int newentry = append_null_to_list((void ***)list);
681 char *copy;
682
683 copy = must_copy_string(entry);
684 (*list)[newentry] = copy;
685}
686
687static void get_existing_subsystems(char ***klist, char ***nlist)
688{
689 FILE *f;
690 char *line = NULL;
691 size_t len = 0;
692
693 if ((f = fopen("/proc/self/cgroup", "r")) == NULL)
694 return;
695 while (getline(&line, &len, f) != -1) {
696 char *p, *p2, *tok, *saveptr = NULL;
235f1815 697 p = strchr(line, ':');
ccb4cabe
SH
698 if (!p)
699 continue;
700 p++;
235f1815 701 p2 = strchr(p, ':');
ccb4cabe
SH
702 if (!p2)
703 continue;
704 *p2 = '\0';
705 for (tok = strtok_r(p, ",", &saveptr); tok;
706 tok = strtok_r(NULL, ",", &saveptr)) {
707 if (strncmp(tok, "name=", 5) == 0)
708 must_append_string(nlist, tok);
709 else
710 must_append_string(klist, tok);
711 }
712 }
713
714 free(line);
715 fclose(f);
716}
717
718static void trim(char *s)
719{
720 size_t len = strlen(s);
721 while (s[len-1] == '\n')
722 s[--len] = '\0';
723}
724
ccb4cabe
SH
725static void print_init_debuginfo(struct cgfsng_handler_data *d)
726{
727 int i;
41c33dbe
SH
728
729 if (!getenv("LXC_DEBUG_CGFSNG"))
730 return;
731
ccb4cabe
SH
732 printf("Cgroup information:\n");
733 printf(" container name: %s\n", d->name);
734 printf(" lxc.cgroup.use: %s\n", d->cgroup_use ? d->cgroup_use : "(none)");
735 printf(" lxc.cgroup.pattern: %s\n", d->cgroup_pattern);
736 printf(" cgroup: %s\n", d->container_cgroup ? d->container_cgroup : "(none)");
737 if (!d->hierarchies) {
738 printf(" No hierarchies found.\n");
739 return;
740 }
741 printf(" Hierarchies:\n");
742 for (i = 0; d->hierarchies[i]; i++) {
743 struct hierarchy *h = d->hierarchies[i];
744 int j;
745 printf(" %d: base_cgroup %s\n", i, h->base_cgroup);
746 printf(" mountpoint %s\n", h->mountpoint);
747 printf(" controllers:\n");
748 for (j = 0; h->controllers[j]; j++)
749 printf(" %d: %s\n", j, h->controllers[j]);
750 }
751}
41c33dbe
SH
752
753static void print_basecg_debuginfo(char *basecginfo, char **klist, char **nlist)
754{
755 int k;
756 if (!getenv("LXC_DEBUG_CGFSNG"))
757 return;
758
759 printf("basecginfo is %s\n", basecginfo);
760
761 for (k = 0; klist[k]; k++)
762 printf("kernel subsystem %d: %s\n", k, klist[k]);
763 for (k = 0; nlist[k]; k++)
764 printf("named subsystem %d: %s\n", k, nlist[k]);
765}
ccb4cabe
SH
766
767/*
768 * At startup, parse_hierarchies finds all the info we need about
769 * cgroup mountpoints and current cgroups, and stores it in @d.
770 */
771static bool parse_hierarchies(struct cgfsng_handler_data *d)
772{
773 FILE *f;
774 char * line = NULL, *basecginfo;
775 char **klist = NULL, **nlist = NULL;
776 size_t len = 0;
777
d30ec4cb
SH
778 /*
779 * Root spawned containers escape the current cgroup, so use init's
780 * cgroups as our base in that case.
781 */
ccb4cabe
SH
782 if (geteuid())
783 basecginfo = read_file("/proc/self/cgroup");
784 else
785 basecginfo = read_file("/proc/1/cgroup");
786 if (!basecginfo)
787 return false;
788
789 if ((f = fopen("/proc/self/mountinfo", "r")) == NULL) {
d3b00a8f 790 SYSERROR("Failed opening /proc/self/mountinfo");
ccb4cabe
SH
791 return false;
792 }
793
794 get_existing_subsystems(&klist, &nlist);
41c33dbe
SH
795
796 print_basecg_debuginfo(basecginfo, klist, nlist);
ccb4cabe
SH
797
798 /* we support simple cgroup mounts and lxcfs mounts */
799 while (getline(&line, &len, f) != -1) {
800 char **controller_list = NULL;
801 char *mountpoint, *base_cgroup;
802
803 if (!is_lxcfs(line) && !is_cgroupfs(line))
804 continue;
805
806 controller_list = get_controllers(klist, nlist, line);
807 if (!controller_list)
808 continue;
809
810 if (controller_list_is_dup(d->hierarchies, controller_list)) {
811 free(controller_list);
812 continue;
813 }
814
815 mountpoint = get_mountpoint(line);
816 if (!mountpoint) {
817 ERROR("Error reading mountinfo: bad line '%s'", line);
818 free_string_list(controller_list);
819 continue;
820 }
821
822 base_cgroup = get_current_cgroup(basecginfo, controller_list[0]);
823 if (!base_cgroup) {
824 ERROR("Failed to find current cgroup for controller '%s'", controller_list[0]);
825 free_string_list(controller_list);
826 free(mountpoint);
827 continue;
828 }
829 trim(base_cgroup);
830 prune_init_scope(base_cgroup);
831 if (!test_writeable(mountpoint, base_cgroup)) {
832 free_string_list(controller_list);
833 free(mountpoint);
834 free(base_cgroup);
835 continue;
836 }
837 add_controller(d, controller_list, mountpoint, base_cgroup);
838 }
839
840 free_string_list(klist);
841 free_string_list(nlist);
842
843 free(basecginfo);
844
845 fclose(f);
846 free(line);
847
848 print_init_debuginfo(d);
849
850 /* verify that all controllers in cgroup.use and all crucial
851 * controllers are accounted for
852 */
853 if (!all_controllers_found(d))
854 return false;
855
856 return true;
857}
858
859static void *cgfsng_init(const char *name)
860{
861 struct cgfsng_handler_data *d;
862 const char *cgroup_use, *cgroup_pattern;
863
864 d = must_alloc(sizeof(*d));
865 memset(d, 0, sizeof(*d));
866
867 d->name = must_copy_string(name);
868
869 errno = 0;
870 cgroup_use = lxc_global_config_value("lxc.cgroup.use");
871 if (!cgroup_use && errno != 0) { // lxc.cgroup.use can be NULL
872 SYSERROR("Error reading list of cgroups to use");
873 goto out_free;
874 }
875 d->cgroup_use = must_copy_string(cgroup_use);
876
877 cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
878 if (!cgroup_pattern) { // lxc.cgroup.pattern is only NULL on error
879 ERROR("Error getting cgroup pattern");
880 goto out_free;
881 }
882 d->cgroup_pattern = must_copy_string(cgroup_pattern);
883
884 if (!parse_hierarchies(d))
885 goto out_free;
886
887 print_init_debuginfo(d);
888
889 return d;
890
891out_free:
892 free_handler_data(d);
893 return NULL;
894}
895
896/*
897 * Concatenate all passed-in strings into one path. Do not fail. If any piece is
898 * not prefixed with '/', add a '/'.
899 */
900static char *must_make_path(const char *first, ...)
901{
902 va_list args;
903 char *cur, *dest;
904 size_t full_len = strlen(first);
905
906 dest = must_copy_string(first);
907
908 va_start(args, first);
909 while ((cur = va_arg(args, char *)) != NULL) {
910 full_len += strlen(cur);
911 if (cur[0] != '/')
912 full_len++;
913 dest = must_realloc(dest, full_len + 1);
914 if (cur[0] != '/')
915 strcat(dest, "/");
916 strcat(dest, cur);
917 }
918 va_end(args);
919
920 return dest;
921}
922
923static int cgroup_rmdir(char *dirname)
924{
925 struct dirent dirent, *direntp;
926 DIR *dir;
927 int r = 0;
928
929 dir = opendir(dirname);
930 if (!dir)
931 return -1;
932
933 while (!readdir_r(dir, &dirent, &direntp)) {
934 struct stat mystat;
935 char *pathname;
936
937 if (!direntp)
938 break;
939
940 if (!strcmp(direntp->d_name, ".") ||
941 !strcmp(direntp->d_name, ".."))
942 continue;
943
944 pathname = must_make_path(dirname, direntp->d_name, NULL);
945
946 if (lstat(pathname, &mystat)) {
947 if (!r)
1c9da8da 948 WARN("failed to stat %s", pathname);
ccb4cabe
SH
949 r = -1;
950 goto next;
951 }
952
953 if (!S_ISDIR(mystat.st_mode))
954 goto next;
955 if (cgroup_rmdir(pathname) < 0)
956 r = -1;
957next:
958 free(pathname);
959 }
960
961 if (rmdir(dirname) < 0) {
962 if (!r)
963 WARN("%s: failed to delete %s: %m", __func__, dirname);
964 r = -1;
965 }
966
967 if (closedir(dir) < 0) {
968 if (!r)
969 WARN("%s: failed to delete %s: %m", __func__, dirname);
970 r = -1;
971 }
972 return r;
973}
974
975static int rmdir_wrapper(void *data)
976{
977 char *path = data;
978
979 if (setresgid(0,0,0) < 0)
980 SYSERROR("Failed to setgid to 0");
981 if (setresuid(0,0,0) < 0)
982 SYSERROR("Failed to setuid to 0");
983 if (setgroups(0, NULL) < 0)
984 SYSERROR("Failed to clear groups");
985
986 return cgroup_rmdir(path);
987}
988
989void recursive_destroy(char *path, struct lxc_conf *conf)
990{
991 int r;
992 if (conf && !lxc_list_empty(&conf->id_map))
993 r = userns_exec_1(conf, rmdir_wrapper, path);
994 else
995 r = cgroup_rmdir(path);
996
997 if (r < 0)
1c9da8da 998 ERROR("Error destroying %s", path);
ccb4cabe
SH
999}
1000
1001static void cgfsng_destroy(void *hdata, struct lxc_conf *conf)
1002{
1003 struct cgfsng_handler_data *d = hdata;
1004
1005 if (!d)
1006 return;
1007
1008 if (d->container_cgroup && d->hierarchies) {
1009 int i;
1010 for (i = 0; d->hierarchies[i]; i++) {
1011 struct hierarchy *h = d->hierarchies[i];
e2db2a89 1012 if (h->fullcgpath) {
ccb4cabe
SH
1013 recursive_destroy(h->fullcgpath, conf);
1014 free(h->fullcgpath);
1015 h->fullcgpath = NULL;
1016 }
1017 }
1018 }
1019
1020 free_handler_data(d);
1021}
1022
1023struct cgroup_ops *cgfsng_ops_init(void)
1024{
1025 /* TODO - when cgroup_mount is implemented, drop this check */
1026 if (!file_exists("/proc/self/ns/cgroup"))
1027 return NULL;
1028 return &cgfsng_ops;
1029}
1030
1031static bool create_path_for_hierarchy(struct hierarchy *h, char *cgname)
1032{
e3a3fecf 1033 h->fullcgpath = must_make_path(h->mountpoint, h->base_cgroup, cgname, NULL);
d8da679e
SH
1034 if (dir_exists(h->fullcgpath)) // it must not already exist
1035 return false;
e3a3fecf
SH
1036 if (!handle_cpuset_hierarchy(h, cgname))
1037 return false;
1038 return mkdir_p(h->fullcgpath, 0755) == 0;
ccb4cabe
SH
1039}
1040
1041static void remove_path_for_hierarchy(struct hierarchy *h, char *cgname)
1042{
1043 if (rmdir(h->fullcgpath) < 0)
1044 SYSERROR("Failed to clean up cgroup %s from failed creation attempt", h->fullcgpath);
1045 free(h->fullcgpath);
1046 h->fullcgpath = NULL;
1047}
1048
1049/*
d30ec4cb 1050 * Try to create the same cgroup in all hierarchies.
ccb4cabe
SH
1051 * Start with cgroup_pattern; next cgroup_pattern-1, -2, ..., -999
1052 */
1053static inline bool cgfsng_create(void *hdata)
1054{
1055 struct cgfsng_handler_data *d = hdata;
1056 char *tmp, *cgname, *offset;
1057 int i, idx = 0;
1058 size_t len;
1059
1060 if (!d)
1061 return false;
1062 if (d->container_cgroup) {
1063 WARN("cgfsng_create called a second time");
1064 return false;
1065 }
1066
1067 tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
1068 if (!tmp) {
1069 ERROR("Failed expanding cgroup name pattern");
1070 return false;
1071 }
1072 len = strlen(tmp) + 5; // leave room for -NNN\0
1073 cgname = must_alloc(len);
1074 strcpy(cgname, tmp);
1075 free(tmp);
1076 offset = cgname + len - 5;
1077
1078again:
95adfe93
SH
1079 if (idx == 1000) {
1080 ERROR("Too many conflicting cgroup names");
ccb4cabe 1081 goto out_free;
95adfe93 1082 }
ccb4cabe
SH
1083 if (idx)
1084 snprintf(offset, 5, "-%d", idx);
1085 for (i = 0; d->hierarchies[i]; i++) {
1086 if (!create_path_for_hierarchy(d->hierarchies[i], cgname)) {
1087 int j;
1088 SYSERROR("Failed to create %s: %s", d->hierarchies[i]->fullcgpath, strerror(errno));
1089 free(d->hierarchies[i]->fullcgpath);
1090 d->hierarchies[i]->fullcgpath = NULL;
1091 for (j = 0; j < i; j++)
1092 remove_path_for_hierarchy(d->hierarchies[j], cgname);
1093 idx++;
1094 goto again;
1095 }
1096 }
1097 /* Done */
1098 d->container_cgroup = cgname;
1099 return true;
1100
1101out_free:
1102 free(cgname);
1103 return false;
1104}
1105
1106static const char *cgfsng_canonical_path(void *hdata)
1107{
1108 struct cgfsng_handler_data *d = hdata;
1109
1110 return d->container_cgroup;
1111}
1112
1113static bool cgfsng_enter(void *hdata, pid_t pid)
1114{
1115 struct cgfsng_handler_data *d = hdata;
1116 char pidstr[25];
1117 int i, len;
1118
1119 len = snprintf(pidstr, 25, "%d", pid);
1120 if (len < 0 || len > 25)
1121 return false;
1122
1123 for (i = 0; d->hierarchies[i]; i++) {
1124 char *fullpath = must_make_path(d->hierarchies[i]->fullcgpath,
1125 "cgroup.procs", NULL);
1126 if (lxc_write_to_file(fullpath, pidstr, len, false) != 0) {
d3b00a8f 1127 SYSERROR("Failed to enter %s", fullpath);
ccb4cabe
SH
1128 free(fullpath);
1129 return false;
1130 }
1131 free(fullpath);
1132 }
1133
1134 return true;
1135}
1136
1137struct chown_data {
1138 struct cgfsng_handler_data *d;
1139 uid_t origuid; // target uid in parent namespace
1140};
1141
c0888dfe
SH
1142/*
1143 * chgrp the container cgroups to container group. We leave
1144 * the container owner as cgroup owner. So we must make the
1145 * directories 775 so that the container can create sub-cgroups.
43647298
SH
1146 *
1147 * Also chown the tasks and cgroup.procs files. Those may not
1148 * exist depending on kernel version.
c0888dfe 1149 */
ccb4cabe
SH
1150static int chown_cgroup_wrapper(void *data)
1151{
1152 struct chown_data *arg = data;
1153 struct cgfsng_handler_data *d = arg->d;
1154 uid_t destuid;
1155 int i;
1156
1157 if (setresgid(0,0,0) < 0)
1158 SYSERROR("Failed to setgid to 0");
1159 if (setresuid(0,0,0) < 0)
1160 SYSERROR("Failed to setuid to 0");
1161 if (setgroups(0, NULL) < 0)
1162 SYSERROR("Failed to clear groups");
1163
1164 destuid = get_ns_uid(arg->origuid);
1165
1166 for (i = 0; d->hierarchies[i]; i++) {
43647298
SH
1167 char *fullpath, *path = d->hierarchies[i]->fullcgpath;
1168
1169 if (chown(path, destuid, 0) < 0) {
ab8f5424 1170 SYSERROR("Error chowning %s to %d", path, (int) destuid);
ccb4cabe
SH
1171 return -1;
1172 }
c0888dfe 1173
43647298 1174 if (chmod(path, 0775) < 0) {
ab8f5424 1175 SYSERROR("Error chmoding %s", path);
c0888dfe
SH
1176 return -1;
1177 }
ccb4cabe 1178
ab8f5424
SH
1179 /*
1180 * Failures to chown these are inconvenient but not detrimental
1181 * We leave these owned by the container launcher, so that container
1182 * root can write to the files to attach. We chmod them 664 so that
1183 * container systemd can write to the files (which systemd in wily
1184 * insists on doing)
1185 */
43647298
SH
1186 fullpath = must_make_path(path, "tasks", NULL);
1187 if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
1188 WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid);
ab8f5424
SH
1189 if (chmod(fullpath, 0664) < 0)
1190 WARN("Error chmoding %s: %m", path);
43647298
SH
1191 free(fullpath);
1192
1193 fullpath = must_make_path(path, "cgroup.procs", NULL);
1194 if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
1195 WARN("Failed chowning %s to %d: %m", fullpath, (int) destuid);
ab8f5424
SH
1196 if (chmod(fullpath, 0664) < 0)
1197 WARN("Error chmoding %s: %m", path);
ccb4cabe
SH
1198 free(fullpath);
1199 }
1200
1201 return 0;
1202}
1203
1204static bool cgfsns_chown(void *hdata, struct lxc_conf *conf)
1205{
1206 struct cgfsng_handler_data *d = hdata;
1207 struct chown_data wrap;
1208
1209 if (!d)
1210 return false;
1211
1212 if (lxc_list_empty(&conf->id_map))
1213 return true;
1214
1215 wrap.d = d;
1216 wrap.origuid = geteuid();
1217
1218 if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap) < 0) {
1219 ERROR("Error requesting cgroup chown in new namespace");
1220 return false;
1221 }
1222
1223 return true;
1224}
1225
1226static bool cgfsng_mount(void *hdata, const char *root, int type)
1227{
1228 if (cgns_supported())
1229 return true;
1230 // TODO - implement this. Not needed for cgroup namespaces
1231 return false;
1232}
1233
1234static int recursive_count_nrtasks(char *dirname)
1235{
1236 struct dirent dirent, *direntp;
1237 DIR *dir;
1238 int count = 0, ret;
1239 char *path;
1240
1241 dir = opendir(dirname);
1242 if (!dir)
1243 return 0;
1244
1245 while (!readdir_r(dir, &dirent, &direntp)) {
1246 struct stat mystat;
1247
1248 if (!direntp)
1249 break;
1250
1251 if (!strcmp(direntp->d_name, ".") ||
1252 !strcmp(direntp->d_name, ".."))
1253 continue;
1254
1255 path = must_make_path(dirname, direntp->d_name, NULL);
1256
1257 if (lstat(path, &mystat))
1258 goto next;
1259
1260 if (!S_ISDIR(mystat.st_mode))
1261 goto next;
1262
1263 count += recursive_count_nrtasks(path);
1264next:
1265 free(path);
1266 }
1267
1268 path = must_make_path(dirname, "cgroup.procs", NULL);
1269 ret = lxc_count_file_lines(path);
1270 if (ret != -1)
1271 count += ret;
1272 free(path);
1273
1274 (void) closedir(dir);
1275
1276 return count;
1277}
1278
1279static int cgfsng_nrtasks(void *hdata) {
1280 struct cgfsng_handler_data *d = hdata;
1281 char *path;
1282 int count;
1283
1284 if (!d || !d->container_cgroup || !d->hierarchies)
1285 return -1;
1286 path = must_make_path(d->hierarchies[0]->fullcgpath, NULL);
1287 count = recursive_count_nrtasks(path);
1288 free(path);
1289 return count;
1290}
1291
1292/* Only root needs to escape to the cgroup of its init */
7103fe6f 1293static bool cgfsng_escape()
ccb4cabe 1294{
7103fe6f 1295 struct cgfsng_handler_data *d;
ccb4cabe 1296 int i;
7103fe6f 1297 bool ret = false;
ccb4cabe
SH
1298
1299 if (geteuid())
1300 return true;
1301
7103fe6f
TA
1302 d = cgfsng_init("criu-temp-cgfsng");
1303 if (!d) {
1304 ERROR("cgfsng_init failed");
1305 return false;
1306 }
1307
ccb4cabe
SH
1308 for (i = 0; d->hierarchies[i]; i++) {
1309 char *fullpath = must_make_path(d->hierarchies[i]->mountpoint,
1310 d->hierarchies[i]->base_cgroup,
1311 "cgroup.procs", NULL);
1312 if (lxc_write_to_file(fullpath, "0", 2, false) != 0) {
d3b00a8f 1313 SYSERROR("Failed to escape to %s", fullpath);
ccb4cabe 1314 free(fullpath);
7103fe6f 1315 goto out;
ccb4cabe
SH
1316 }
1317 free(fullpath);
1318 }
1319
7103fe6f
TA
1320 ret = true;
1321out:
1322 free_handler_data(d);
1323 return ret;
ccb4cabe
SH
1324}
1325
1326#define THAWED "THAWED"
1327#define THAWED_LEN (strlen(THAWED))
1328
1329static bool cgfsng_unfreeze(void *hdata)
1330{
1331 struct cgfsng_handler_data *d = hdata;
1332 char *fullpath;
1333 struct hierarchy *h = get_hierarchy(d, "freezer");
1334
1335 if (!d || !h)
1336 return false;
1337 fullpath = must_make_path(h->fullcgpath, "freezer.state", NULL);
1338 if (lxc_write_to_file(fullpath, THAWED, THAWED_LEN, false) != 0) {
1339 free(fullpath);
1340 return false;
1341 }
1342 free(fullpath);
1343 return true;
1344}
1345
1346static const char *cgfsng_get_cgroup(void *hdata, const char *subsystem)
1347{
1348 struct cgfsng_handler_data *d = hdata;
1349 struct hierarchy *h;
1350 if (!d)
1351 return NULL;
1352
1353 h = get_hierarchy(d, subsystem);
1354 if (!h)
1355 return NULL;
1356
371f834d
SH
1357 return h->fullcgpath ? h->fullcgpath + strlen(h->mountpoint) : NULL;
1358}
1359
1360/*
1361 * Given a cgroup path returned from lxc_cmd_get_cgroup_path, build a
1362 * full path, which must be freed by the caller.
1363 */
1364static char *build_full_cgpath_from_monitorpath(struct hierarchy *h,
1365 const char *inpath,
1366 const char *filename)
1367{
1368 /*
1369 * XXX Remove this case after 2.0 release. It's for dealing with
1370 * containers spawned under the old buggy cgfsng which wasn't around
1371 * for long.
1372 */
1373 if (strncmp(inpath, "/sys/fs/cgroup/", 15) == 0)
1374 return must_make_path(inpath, filename, NULL);
1375 return must_make_path(h->mountpoint, inpath, filename, NULL);
ccb4cabe
SH
1376}
1377
1378static bool cgfsng_attach(const char *name, const char *lxcpath, pid_t pid)
1379{
1380 struct cgfsng_handler_data *d;
1381 char pidstr[25];
1382 int i, len;
1383
1384 len = snprintf(pidstr, 25, "%d", pid);
1385 if (len < 0 || len > 25)
1386 return false;
1387
1388 d = cgfsng_init(name);
1389 if (!d)
1390 return false;
1391
1392 for (i = 0; d->hierarchies[i]; i++) {
1393 char *path, *fullpath;
1394 struct hierarchy *h = d->hierarchies[i];
1395
1396 path = lxc_cmd_get_cgroup_path(name, lxcpath, h->controllers[0]);
1397 if (!path) // not running
1398 continue;
1399
371f834d
SH
1400 fullpath = build_full_cgpath_from_monitorpath(h, path, "cgroup.procs");
1401 free(path);
ccb4cabe
SH
1402 if (lxc_write_to_file(fullpath, pidstr, len, false) != 0) {
1403 SYSERROR("Failed to attach %d to %s", (int)pid, fullpath);
1404 free(fullpath);
ccb4cabe
SH
1405 free_handler_data(d);
1406 return false;
1407 }
ccb4cabe
SH
1408 free(fullpath);
1409 }
1410
1411 free_handler_data(d);
1412 return true;
1413}
1414
1415/*
1416 * Called externally (i.e. from 'lxc-cgroup') to query cgroup limits.
1417 * Here we don't have a cgroup_data set up, so we ask the running
1418 * container through the commands API for the cgroup path
1419 */
1420static int cgfsng_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
1421{
1422 char *subsystem, *p, *path;
1423 struct cgfsng_handler_data *d;
1424 struct hierarchy *h;
1425 int ret = -1;
1426
1427 subsystem = alloca(strlen(filename) + 1);
1428 strcpy(subsystem, filename);
1429 if ((p = strchr(subsystem, '.')) != NULL)
1430 *p = '\0';
1431
1432 path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
1433 if (!path) // not running
1434 return -1;
1435
1436 d = cgfsng_init(name);
1437 if (!d) {
1438 free(path);
1439 return false;
1440 }
1441
1442 h = get_hierarchy(d, subsystem);
1443 if (h) {
371f834d 1444 char *fullpath = build_full_cgpath_from_monitorpath(h, path, filename);
ccb4cabe
SH
1445 ret = lxc_read_from_file(fullpath, value, len);
1446 free(fullpath);
1447 }
1448
1449 free_handler_data(d);
1450 free(path);
1451
1452 return ret;
1453}
1454
1455/*
1456 * Called externally (i.e. from 'lxc-cgroup') to set new cgroup limits.
1457 * Here we don't have a cgroup_data set up, so we ask the running
1458 * container through the commands API for the cgroup path
1459 */
1460static int cgfsng_set(const char *filename, const char *value, const char *name, const char *lxcpath)
1461{
1462 char *subsystem, *p, *path;
1463 struct cgfsng_handler_data *d;
1464 struct hierarchy *h;
1465 int ret = -1;
1466
1467 subsystem = alloca(strlen(filename) + 1);
1468 strcpy(subsystem, filename);
1469 if ((p = strchr(subsystem, '.')) != NULL)
1470 *p = '\0';
1471
1472 path = lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
1473 if (!path) // not running
1474 return -1;
1475
1476 d = cgfsng_init(name);
1477 if (!d) {
1478 free(path);
1479 return false;
1480 }
1481
1482 h = get_hierarchy(d, subsystem);
1483 if (h) {
371f834d 1484 char *fullpath = build_full_cgpath_from_monitorpath(h, path, filename);
ccb4cabe
SH
1485 ret = lxc_write_to_file(fullpath, value, strlen(value), false);
1486 free(fullpath);
1487 }
1488
1489 free_handler_data(d);
1490 free(path);
1491
1492 return ret;
1493}
1494
ccb4cabe
SH
1495/*
1496 * Called from setup_limits - here we have the container's cgroup_data because
1497 * we created the cgroups
1498 */
1499static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfsng_handler_data *d)
1500{
1501 char *subsystem = NULL, *p;
1502 int ret = -1;
1503 struct hierarchy *h;
1504
1505 subsystem = alloca(strlen(filename) + 1);
1506 strcpy(subsystem, filename);
1507 if ((p = strchr(subsystem, '.')) != NULL)
1508 *p = '\0';
1509
1510 h = get_hierarchy(d, subsystem);
1511 if (h) {
1512 char *fullpath = must_make_path(h->fullcgpath, filename, NULL);
1513 ret = lxc_write_to_file(fullpath, value, strlen(value), false);
1514 free(fullpath);
1515 }
1516 return ret;
1517}
1518
1519static bool cgfsng_setup_limits(void *hdata, struct lxc_list *cgroup_settings,
1520 bool do_devices)
1521{
1522 struct cgfsng_handler_data *d = hdata;
1523 struct lxc_list *iterator, *sorted_cgroup_settings, *next;
1524 struct lxc_cgroup *cg;
1525 struct hierarchy *h;
1526 char *listpath = NULL;
1527 bool ret = false;
1528
1529 if (lxc_list_empty(cgroup_settings))
1530 return true;
1531
1532 sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
1533 if (!sorted_cgroup_settings) {
1534 return false;
1535 }
1536
1537 if (do_devices) {
1538 h = get_hierarchy(d, "devices");
1539 if (!h) {
1c9da8da 1540 ERROR("No devices cgroup setup for %s", d->name);
ccb4cabe
SH
1541 return false;
1542 }
1543 listpath = must_make_path(h->fullcgpath, "devices.list", NULL);
1544 }
1545
1546 lxc_list_for_each(iterator, sorted_cgroup_settings) {
1547 cg = iterator->elem;
1548
1549 if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
ccb4cabe
SH
1550 if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) {
1551 if (do_devices && (errno == EACCES || errno == EPERM)) {
1552 WARN("Error setting %s to %s for %s",
1553 cg->subsystem, cg->value, d->name);
1554 continue;
1555 }
1556 SYSERROR("Error setting %s to %s for %s",
1557 cg->subsystem, cg->value, d->name);
1558 goto out;
1559 }
1560 }
1561
1562 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
1563 }
1564
1565 ret = true;
1566 INFO("cgroup has been setup");
1567out:
1568 free(listpath);
1569 lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
1570 lxc_list_del(iterator);
1571 free(iterator);
1572 }
1573 free(sorted_cgroup_settings);
1574 return ret;
1575}
1576
1577static struct cgroup_ops cgfsng_ops = {
1578 .init = cgfsng_init,
1579 .destroy = cgfsng_destroy,
1580 .create = cgfsng_create,
1581 .enter = cgfsng_enter,
1582 .canonical_path = cgfsng_canonical_path,
1583 .escape = cgfsng_escape,
1584 .get_cgroup = cgfsng_get_cgroup,
1585 .get = cgfsng_get,
1586 .set = cgfsng_set,
1587 .unfreeze = cgfsng_unfreeze,
1588 .setup_limits = cgfsng_setup_limits,
1589 .name = "cgroupfs-ng",
1590 .attach = cgfsng_attach,
1591 .chown = cgfsns_chown,
1592 .mount_cgroup = cgfsng_mount,
1593 .nrtasks = cgfsng_nrtasks,
1594 .driver = CGFSNG,
1595
1596 /* unsupported */
1597 .create_legacy = NULL,
1598};