]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/cgroup.c
bionic: Replace rindex by strrchr
[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>
aae1f3c4 37#include <sys/mount.h>
576f946d 38#include <netinet/in.h>
39#include <net/if.h>
40
e2bcd7db 41#include "error.h"
881450bb 42#include "config.h"
ae5c8b8e 43#include "commands.h"
b98f7d6e
SH
44#include "list.h"
45#include "conf.h"
33ad9f1a 46#include "utils.h"
740d1928 47#include "bdev.h"
f2363e38
ÇO
48#include "log.h"
49#include "cgroup.h"
50#include "start.h"
484ed030 51#include "state.h"
36eb9bde 52
edaf8b1b
SG
53#if IS_BIONIC
54#include <../include/lxcmntent.h>
55#else
56#include <mntent.h>
57#endif
58
36eb9bde 59lxc_log_define(lxc_cgroup, lxc);
576f946d 60
33ad9f1a
CS
61static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta);
62static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list);
63static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp);
64static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h);
65static bool is_valid_cgroup(const char *name);
33ad9f1a 66static int create_cgroup(struct cgroup_mount_point *mp, const char *path);
603c64c2 67static int remove_cgroup(struct cgroup_mount_point *mp, const char *path, bool recurse);
33ad9f1a
CS
68static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char *path, const char *suffix);
69static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem);
70static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len);
71static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value);
72static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h, char *v, bool for_allow);
9daf6f5d 73static int do_setup_cgroup_limits(struct lxc_handler *h, struct lxc_list *cgroup_settings, bool do_devices);
33ad9f1a
CS
74static int cgroup_recursive_task_count(const char *cgroup_path);
75static int count_lines(const char *fn);
1ea59ad2 76static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path);
0a4d9378 77static void setup_cpuset_if_needed(char **subsystems, char *path);
33ad9f1a 78
d4ef7c50
SH
79static struct cgroup_ops cgfs_ops;
80struct cgroup_ops *active_cg_ops = &cgfs_ops;
81static void init_cg_ops(void);
82
83#ifdef HAVE_CGMANAGER
84/* this needs to be mutexed for api use */
85extern bool cgmanager_initialized;
86extern bool use_cgmanager;
87extern bool lxc_init_cgmanager(void);
88#else
89static bool cgmanager_initialized = false;
90static bool use_cgmanager = false;
91static bool lxc_init_cgmanager(void) { return false; }
92#endif
93
603c64c2
SH
94static int cgroup_rmdir(char *dirname)
95{
96 struct dirent dirent, *direntp;
97 int saved_errno = 0;
98 DIR *dir;
99 int ret, failed=0;
100 char pathname[MAXPATHLEN];
101
102 dir = opendir(dirname);
103 if (!dir) {
104 ERROR("%s: failed to open %s", __func__, dirname);
105 return -1;
106 }
107
108 while (!readdir_r(dir, &dirent, &direntp)) {
109 struct stat mystat;
110 int rc;
111
112 if (!direntp)
113 break;
114
115 if (!strcmp(direntp->d_name, ".") ||
116 !strcmp(direntp->d_name, ".."))
117 continue;
118
119 rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
120 if (rc < 0 || rc >= MAXPATHLEN) {
121 ERROR("pathname too long");
122 failed=1;
123 if (!saved_errno)
124 saved_errno = -ENOMEM;
125 continue;
126 }
127 ret = lstat(pathname, &mystat);
128 if (ret) {
129 SYSERROR("%s: failed to stat %s", __func__, pathname);
130 failed=1;
131 if (!saved_errno)
132 saved_errno = errno;
133 continue;
134 }
135 if (S_ISDIR(mystat.st_mode)) {
136 if (cgroup_rmdir(pathname) < 0) {
137 if (!saved_errno)
138 saved_errno = errno;
139 failed=1;
140 }
141 }
142 }
143
144 if (rmdir(dirname) < 0) {
145 SYSERROR("%s: failed to delete %s", __func__, dirname);
146 if (!saved_errno)
147 saved_errno = errno;
148 failed=1;
149 }
150
151 ret = closedir(dir);
152 if (ret) {
153 SYSERROR("%s: failed to close directory %s", __func__, dirname);
154 if (!saved_errno)
155 saved_errno = errno;
156 failed=1;
157 }
158
159 errno = saved_errno;
160 return failed ? -1 : 0;
161}
162
33ad9f1a
CS
163struct cgroup_meta_data *lxc_cgroup_load_meta()
164{
165 const char *cgroup_use = NULL;
166 char **cgroup_use_list = NULL;
167 struct cgroup_meta_data *md = NULL;
168 int saved_errno;
169
170 errno = 0;
593e8478 171 cgroup_use = lxc_global_config_value("lxc.cgroup.use");
33ad9f1a
CS
172 if (!cgroup_use && errno != 0)
173 return NULL;
174 if (cgroup_use) {
175 cgroup_use_list = lxc_string_split_and_trim(cgroup_use, ',');
176 if (!cgroup_use_list)
177 return NULL;
178 }
576f946d 179
33ad9f1a
CS
180 md = lxc_cgroup_load_meta2((const char **)cgroup_use_list);
181 saved_errno = errno;
182 lxc_free_array((void **)cgroup_use_list, free);
183 errno = saved_errno;
184 return md;
185}
fd37327f 186
b653309a 187/* Step 1: determine all kernel subsystems */
d4ef7c50 188bool find_cgroup_subsystems(char ***kernel_subsystems)
1d39a065 189{
b653309a
SH
190 FILE *proc_cgroups;
191 bool bret = false;
33ad9f1a
CS
192 char *line = NULL;
193 size_t sz = 0;
b653309a
SH
194 size_t kernel_subsystems_count = 0;
195 size_t kernel_subsystems_capacity = 0;
196 int r;
1d39a065 197
33ad9f1a
CS
198 proc_cgroups = fopen_cloexec("/proc/cgroups", "r");
199 if (!proc_cgroups)
b653309a 200 return false;
1d39a065 201
33ad9f1a
CS
202 while (getline(&line, &sz, proc_cgroups) != -1) {
203 char *tab1;
204 char *tab2;
205 int hierarchy_number;
1d39a065 206
33ad9f1a
CS
207 if (line[0] == '#')
208 continue;
209 if (!line[0])
210 continue;
1d39a065 211
33ad9f1a
CS
212 tab1 = strchr(line, '\t');
213 if (!tab1)
8900b9eb 214 continue;
33ad9f1a
CS
215 *tab1++ = '\0';
216 tab2 = strchr(tab1, '\t');
217 if (!tab2)
218 continue;
219 *tab2 = '\0';
fd37327f 220
33ad9f1a
CS
221 tab2 = NULL;
222 hierarchy_number = strtoul(tab1, &tab2, 10);
223 if (!tab2 || *tab2)
224 continue;
225 (void)hierarchy_number;
226
b653309a 227 r = lxc_grow_array((void ***)kernel_subsystems, &kernel_subsystems_capacity, kernel_subsystems_count + 1, 12);
33ad9f1a 228 if (r < 0)
b653309a
SH
229 goto out;
230 (*kernel_subsystems)[kernel_subsystems_count] = strdup(line);
231 if (!(*kernel_subsystems)[kernel_subsystems_count])
232 goto out;
33ad9f1a 233 kernel_subsystems_count++;
bcbd102c 234 }
b653309a 235 bret = true;
0d9f8e18 236
b653309a 237out:
33ad9f1a 238 fclose(proc_cgroups);
0ccf7c2a 239 free(line);
b653309a
SH
240 return bret;
241}
242
243/* Step 2: determine all hierarchies (by reading /proc/self/cgroup),
244 * since mount points don't specify hierarchy number and
245 * /proc/cgroups does not contain named hierarchies
246 */
247static bool find_cgroup_hierarchies(struct cgroup_meta_data *meta_data,
248 bool all_kernel_subsystems, bool all_named_subsystems,
249 const char **subsystem_whitelist)
250{
251 FILE *proc_self_cgroup;
252 char *line = NULL;
253 size_t sz = 0;
254 int r;
255 bool bret = false;
256 size_t hierarchy_capacity = 0;
ef6e34ee 257
33ad9f1a
CS
258 proc_self_cgroup = fopen_cloexec("/proc/self/cgroup", "r");
259 /* if for some reason (because of setns() and pid namespace for example),
260 * /proc/self is not valid, we try /proc/1/cgroup... */
261 if (!proc_self_cgroup)
262 proc_self_cgroup = fopen_cloexec("/proc/1/cgroup", "r");
263 if (!proc_self_cgroup)
b653309a 264 return false;
33ad9f1a
CS
265
266 while (getline(&line, &sz, proc_self_cgroup) != -1) {
267 /* file format: hierarchy:subsystems:group,
268 * we only extract hierarchy and subsystems
269 * here */
270 char *colon1;
271 char *colon2;
272 int hierarchy_number;
273 struct cgroup_hierarchy *h = NULL;
274 char **p;
275
276 if (!line[0])
277 continue;
ad08bbb7 278
33ad9f1a
CS
279 colon1 = strchr(line, ':');
280 if (!colon1)
8900b9eb 281 continue;
33ad9f1a
CS
282 *colon1++ = '\0';
283 colon2 = strchr(colon1, ':');
284 if (!colon2)
285 continue;
286 *colon2 = '\0';
ad08bbb7 287
33ad9f1a
CS
288 colon2 = NULL;
289 hierarchy_number = strtoul(line, &colon2, 10);
290 if (!colon2 || *colon2)
291 continue;
576f946d 292
33ad9f1a
CS
293 if (hierarchy_number > meta_data->maximum_hierarchy) {
294 /* lxc_grow_array will never shrink, so even if we find a lower
295 * hierarchy number here, the array will never be smaller
296 */
297 r = lxc_grow_array((void ***)&meta_data->hierarchies, &hierarchy_capacity, hierarchy_number + 1, 12);
298 if (r < 0)
b653309a 299 goto out;
5193cc3d 300
33ad9f1a
CS
301 meta_data->maximum_hierarchy = hierarchy_number;
302 }
fd37327f 303
33ad9f1a
CS
304 /* this shouldn't happen, we had this already */
305 if (meta_data->hierarchies[hierarchy_number])
b653309a 306 goto out;
33ad9f1a
CS
307
308 h = calloc(1, sizeof(struct cgroup_hierarchy));
309 if (!h)
b653309a 310 goto out;
33ad9f1a
CS
311
312 meta_data->hierarchies[hierarchy_number] = h;
313
314 h->index = hierarchy_number;
315 h->subsystems = lxc_string_split_and_trim(colon1, ',');
316 if (!h->subsystems)
b653309a 317 goto out;
33ad9f1a
CS
318 /* see if this hierarchy should be considered */
319 if (!all_kernel_subsystems || !all_named_subsystems) {
320 for (p = h->subsystems; *p; p++) {
321 if (!strncmp(*p, "name=", 5)) {
322 if (all_named_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) {
323 h->used = true;
324 break;
325 }
326 } else {
327 if (all_kernel_subsystems || (subsystem_whitelist && lxc_string_in_array(*p, subsystem_whitelist))) {
328 h->used = true;
329 break;
330 }
331 }
332 }
333 } else {
334 /* we want all hierarchy anyway */
335 h->used = true;
ae5c8b8e 336 }
ae5c8b8e 337 }
b653309a 338 bret = true;
0b9c21ab 339
b653309a 340out:
33ad9f1a 341 fclose(proc_self_cgroup);
0ccf7c2a 342 free(line);
b653309a
SH
343 return bret;
344}
345
346/* Step 3: determine all mount points of each hierarchy */
347static bool find_hierarchy_mountpts( struct cgroup_meta_data *meta_data, char **kernel_subsystems)
348{
349 bool bret = false;
350 FILE *proc_self_mountinfo;
351 char *line = NULL;
352 size_t sz = 0;
353 char **tokens = NULL;
354 size_t mount_point_count = 0;
355 size_t mount_point_capacity = 0;
356 size_t token_capacity = 0;
357 int r;
358
33ad9f1a
CS
359 proc_self_mountinfo = fopen_cloexec("/proc/self/mountinfo", "r");
360 /* if for some reason (because of setns() and pid namespace for example),
361 * /proc/self is not valid, we try /proc/1/cgroup... */
362 if (!proc_self_mountinfo)
363 proc_self_mountinfo = fopen_cloexec("/proc/1/mountinfo", "r");
364 if (!proc_self_mountinfo)
b653309a 365 return false;
33ad9f1a
CS
366
367 while (getline(&line, &sz, proc_self_mountinfo) != -1) {
178938fe 368 char *token, *line_tok, *saveptr = NULL;
33ad9f1a
CS
369 size_t i, j, k;
370 struct cgroup_mount_point *mount_point;
371 struct cgroup_hierarchy *h;
372 char **subsystems;
373
374 if (line[0] && line[strlen(line) - 1] == '\n')
375 line[strlen(line) - 1] = '\0';
376
178938fe 377 for (i = 0, line_tok = line; (token = strtok_r(line_tok, " ", &saveptr)); line_tok = NULL) {
33ad9f1a
CS
378 r = lxc_grow_array((void ***)&tokens, &token_capacity, i + 1, 64);
379 if (r < 0)
b653309a 380 goto out;
33ad9f1a
CS
381 tokens[i++] = token;
382 }
b98f7d6e 383
33ad9f1a
CS
384 /* layout of /proc/self/mountinfo:
385 * 0: id
386 * 1: parent id
387 * 2: device major:minor
388 * 3: mount prefix
8900b9eb 389 * 4: mount point
33ad9f1a
CS
390 * 5: per-mount options
391 * [optional X]: additional data
392 * X+7: "-"
393 * X+8: type
394 * X+9: source
395 * X+10: per-superblock options
396 */
397 for (j = 6; j < i && tokens[j]; j++)
398 if (!strcmp(tokens[j], "-"))
399 break;
fd4f5a56 400
33ad9f1a
CS
401 /* could not find separator */
402 if (j >= i || !tokens[j])
403 continue;
404 /* there should be exactly three fields after
405 * the separator
406 */
407 if (i != j + 4)
408 continue;
fd4f5a56 409
33ad9f1a
CS
410 /* not a cgroup filesystem */
411 if (strcmp(tokens[j + 1], "cgroup") != 0)
412 continue;
b98f7d6e 413
33ad9f1a
CS
414 subsystems = subsystems_from_mount_options(tokens[j + 3], kernel_subsystems);
415 if (!subsystems)
b653309a 416 goto out;
33ad9f1a
CS
417
418 h = NULL;
419 for (k = 1; k <= meta_data->maximum_hierarchy; k++) {
420 if (meta_data->hierarchies[k] &&
421 meta_data->hierarchies[k]->subsystems[0] &&
422 lxc_string_in_array(meta_data->hierarchies[k]->subsystems[0], (const char **)subsystems)) {
423 /* TODO: we could also check if the lists really match completely,
424 * just to have an additional sanity check */
425 h = meta_data->hierarchies[k];
b98f7d6e 426 break;
33ad9f1a 427 }
b98f7d6e 428 }
33ad9f1a
CS
429 lxc_free_array((void **)subsystems, free);
430
431 r = lxc_grow_array((void ***)&meta_data->mount_points, &mount_point_capacity, mount_point_count + 1, 12);
432 if (r < 0)
b653309a 433 goto out;
33ad9f1a
CS
434
435 /* create mount point object */
436 mount_point = calloc(1, sizeof(*mount_point));
437 if (!mount_point)
b653309a 438 goto out;
33ad9f1a
CS
439
440 meta_data->mount_points[mount_point_count++] = mount_point;
441
442 mount_point->hierarchy = h;
443 mount_point->mount_point = strdup(tokens[4]);
444 mount_point->mount_prefix = strdup(tokens[3]);
445 if (!mount_point->mount_point || !mount_point->mount_prefix)
b653309a 446 goto out;
33ad9f1a
CS
447 mount_point->read_only = !lxc_string_in_list("rw", tokens[5], ',');
448
449 if (!strcmp(mount_point->mount_prefix, "/")) {
450 if (mount_point->read_only) {
451 if (!h->ro_absolute_mount_point)
452 h->ro_absolute_mount_point = mount_point;
453 } else {
454 if (!h->rw_absolute_mount_point)
455 h->rw_absolute_mount_point = mount_point;
456 }
b98f7d6e 457 }
ae5c8b8e 458
33ad9f1a
CS
459 k = lxc_array_len((void **)h->all_mount_points);
460 r = lxc_grow_array((void ***)&h->all_mount_points, &h->all_mount_point_capacity, k + 1, 4);
461 if (r < 0)
b653309a 462 goto out;
33ad9f1a 463 h->all_mount_points[k] = mount_point;
fd4f5a56 464 }
b653309a
SH
465 bret = true;
466
467out:
b653309a 468 fclose(proc_self_mountinfo);
b653309a 469 free(tokens);
2cdafc54 470 free(line);
b653309a
SH
471 return bret;
472}
473
474struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist)
475{
476 bool all_kernel_subsystems = true;
477 bool all_named_subsystems = false;
478 struct cgroup_meta_data *meta_data = NULL;
479 char **kernel_subsystems = NULL;
480 int saved_errno = 0;
481
482 /* if the subsystem whitelist is not specified, include all
483 * hierarchies that contain kernel subsystems by default but
484 * no hierarchies that only contain named subsystems
485 *
486 * if it is specified, the specifier @all will select all
487 * hierarchies, @kernel will select all hierarchies with
488 * kernel subsystems and @named will select all named
489 * hierarchies
490 */
491 all_kernel_subsystems = subsystem_whitelist ?
492 (lxc_string_in_array("@kernel", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) :
493 true;
494 all_named_subsystems = subsystem_whitelist ?
495 (lxc_string_in_array("@named", subsystem_whitelist) || lxc_string_in_array("@all", subsystem_whitelist)) :
496 false;
497
498 meta_data = calloc(1, sizeof(struct cgroup_meta_data));
499 if (!meta_data)
500 return NULL;
501 meta_data->ref = 1;
502
503 if (!find_cgroup_subsystems(&kernel_subsystems))
504 goto out_error;
505
506 if (!find_cgroup_hierarchies(meta_data, all_kernel_subsystems,
507 all_named_subsystems, subsystem_whitelist))
508 goto out_error;
509
510 if (!find_hierarchy_mountpts(meta_data, kernel_subsystems))
511 goto out_error;
fd4f5a56 512
33ad9f1a
CS
513 /* oops, we couldn't find anything */
514 if (!meta_data->hierarchies || !meta_data->mount_points) {
515 errno = EINVAL;
516 goto out_error;
ae5c8b8e 517 }
fd4f5a56 518
3a0abb3a 519 lxc_free_array((void **)kernel_subsystems, free);
33ad9f1a
CS
520 return meta_data;
521
522out_error:
523 saved_errno = errno;
33ad9f1a
CS
524 lxc_free_array((void **)kernel_subsystems, free);
525 lxc_cgroup_put_meta(meta_data);
526 errno = saved_errno;
527 return NULL;
fd4f5a56
DL
528}
529
33ad9f1a 530struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data)
e14f67a7 531{
33ad9f1a
CS
532 meta_data->ref++;
533 return meta_data;
534}
e14f67a7 535
33ad9f1a
CS
536struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data)
537{
538 size_t i;
539 if (!meta_data)
540 return NULL;
541 if (--meta_data->ref > 0)
542 return meta_data;
543 lxc_free_array((void **)meta_data->mount_points, (lxc_free_fn)lxc_cgroup_mount_point_free);
544 if (meta_data->hierarchies) {
545 for (i = 0; i <= meta_data->maximum_hierarchy; i++)
546 lxc_cgroup_hierarchy_free(meta_data->hierarchies[i]);
e14f67a7 547 }
33ad9f1a 548 free(meta_data->hierarchies);
178938fe 549 free(meta_data);
33ad9f1a 550 return NULL;
e14f67a7
U
551}
552
33ad9f1a 553struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem)
e14f67a7 554{
33ad9f1a
CS
555 size_t i;
556 for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
557 struct cgroup_hierarchy *h = meta_data->hierarchies[i];
558 if (h && lxc_string_in_array(subsystem, (const char **)h->subsystems))
559 return h;
e14f67a7 560 }
e14f67a7
U
561 return NULL;
562}
563
33ad9f1a 564struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable)
b98f7d6e 565{
33ad9f1a
CS
566 struct cgroup_mount_point **mps;
567 struct cgroup_mount_point *current_result = NULL;
568 ssize_t quality = -1;
b98f7d6e 569
33ad9f1a
CS
570 /* trivial case */
571 if (hierarchy->rw_absolute_mount_point)
572 return hierarchy->rw_absolute_mount_point;
573 if (!should_be_writable && hierarchy->ro_absolute_mount_point)
574 return hierarchy->ro_absolute_mount_point;
b98f7d6e 575
33ad9f1a
CS
576 for (mps = hierarchy->all_mount_points; mps && *mps; mps++) {
577 struct cgroup_mount_point *mp = *mps;
578 size_t prefix_len = mp->mount_prefix ? strlen(mp->mount_prefix) : 0;
b98f7d6e 579
33ad9f1a
CS
580 if (prefix_len == 1 && mp->mount_prefix[0] == '/')
581 prefix_len = 0;
b98f7d6e 582
33ad9f1a
CS
583 if (should_be_writable && mp->read_only)
584 continue;
585
586 if (!prefix_len ||
587 (strncmp(group, mp->mount_prefix, prefix_len) == 0 &&
588 (group[prefix_len] == '\0' || group[prefix_len] == '/'))) {
589 /* search for the best quality match, i.e. the match with the
590 * shortest prefix where this group is still contained
591 */
592 if (quality == -1 || prefix_len < quality) {
593 current_result = mp;
594 quality = prefix_len;
595 }
b98f7d6e
SH
596 }
597 }
598
33ad9f1a
CS
599 if (!current_result)
600 errno = ENOENT;
601 return current_result;
b98f7d6e
SH
602}
603
33ad9f1a 604char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix)
b98f7d6e 605{
33ad9f1a
CS
606 struct cgroup_meta_data *meta_data;
607 struct cgroup_hierarchy *h;
608 struct cgroup_mount_point *mp;
609 char *result;
610 int saved_errno;
611
612 meta_data = lxc_cgroup_load_meta();
613 if (!meta_data)
614 return NULL;
b98f7d6e 615
33ad9f1a
CS
616 h = lxc_cgroup_find_hierarchy(meta_data, subsystem);
617 if (!h)
618 goto out_error;
b98f7d6e 619
33ad9f1a
CS
620 mp = lxc_cgroup_find_mount_point(h, group, should_be_writable);
621 if (!mp)
622 goto out_error;
b98f7d6e 623
33ad9f1a
CS
624 result = cgroup_to_absolute_path(mp, group, suffix);
625 if (!result)
626 goto out_error;
b98f7d6e 627
33ad9f1a
CS
628 lxc_cgroup_put_meta(meta_data);
629 return result;
b98f7d6e 630
33ad9f1a
CS
631out_error:
632 saved_errno = errno;
633 lxc_cgroup_put_meta(meta_data);
634 errno = saved_errno;
635 return NULL;
b98f7d6e
SH
636}
637
33ad9f1a 638struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta)
fd4f5a56 639{
33ad9f1a
CS
640 char pid_buf[32];
641 snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid);
642 return lxc_cgroup_process_info_getx(pid_buf, meta);
c8f7c563
CS
643}
644
33ad9f1a 645struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta)
c8f7c563 646{
33ad9f1a
CS
647 return lxc_cgroup_process_info_get(1, meta);
648}
b98f7d6e 649
33ad9f1a
CS
650struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta)
651{
652 struct cgroup_process_info *i;
653 i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta);
654 if (!i)
655 i = lxc_cgroup_process_info_get(getpid(), meta);
656 return i;
657}
ae5c8b8e 658
692ba18f
SH
659/*
660 * If a controller has ns cgroup mounted, then in that cgroup the handler->pid
661 * is already in a new cgroup named after the pid. 'mnt' is passed in as
662 * the full current cgroup. Say that is /sys/fs/cgroup/lxc/2975 and the container
663 * name is c1. . We want to rename the cgroup directory to /sys/fs/cgroup/lxc/c1,
664 * and return the string /sys/fs/cgroup/lxc/c1.
665 */
cea0552e 666static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, pid_t pid, const char *name)
692ba18f
SH
667{
668 char *dir, *fulloldpath;
669 char *newname, *fullnewpath;
cea0552e 670 int len, newlen, ret;
692ba18f
SH
671
672 /*
673 * if cgroup is mounted at /cgroup and task is in cgroup /ab/, pid 2375 and
674 * name is c1,
675 * dir: /ab
676 * fulloldpath = /cgroup/ab/2375
677 * fullnewpath = /cgroup/ab/c1
678 * newname = /ab/c1
679 */
680 dir = alloca(strlen(oldname) + 1);
681 strcpy(dir, oldname);
682
cea0552e
SH
683 len = strlen(oldname) + strlen(mountpath) + 22;
684 fulloldpath = alloca(len);
685 ret = snprintf(fulloldpath, len, "%s/%s/%ld", mountpath, oldname, (unsigned long)pid);
686 if (ret < 0 || ret >= len)
687 return NULL;
692ba18f
SH
688
689 len = strlen(dir) + strlen(name) + 2;
690 newname = malloc(len);
691 if (!newname) {
692 SYSERROR("Out of memory");
693 return NULL;
694 }
cea0552e
SH
695 ret = snprintf(newname, len, "%s/%s", dir, name);
696 if (ret < 0 || ret >= len) {
697 free(newname);
698 return NULL;
699 }
692ba18f 700
cea0552e
SH
701 newlen = strlen(mountpath) + len + 2;
702 fullnewpath = alloca(newlen);
703 ret = snprintf(fullnewpath, newlen, "%s/%s", mountpath, newname);
704 if (ret < 0 || ret >= newlen) {
705 free(newname);
706 return NULL;
707 }
692ba18f
SH
708
709 if (access(fullnewpath, F_OK) == 0) {
710 if (rmdir(fullnewpath) != 0) {
711 SYSERROR("container cgroup %s already exists.", fullnewpath);
712 free(newname);
713 return NULL;
714 }
715 }
716 if (rename(fulloldpath, fullnewpath)) {
717 SYSERROR("failed to rename cgroup %s->%s", fulloldpath, fullnewpath);
718 free(newname);
719 return NULL;
720 }
721
722 DEBUG("'%s' renamed to '%s'", oldname, newname);
723
724 return newname;
725}
726
0a4d9378
SH
727static long get_value(const char *dir, const char *file)
728{
729 FILE *f;
730 char path[MAXPATHLEN];
731 int ret, retv;
732
733 retv = snprintf(path, MAXPATHLEN, "%s/%s", dir, file);
734 if (retv < 0 || retv >= MAXPATHLEN)
735 return 0;
736 f = fopen(path, "r");
737 ret = fscanf(f, "%d", &retv);
738 fclose(f);
739 if (ret != 1)
740 return 0;
741 return retv;
742}
743
744static void set_value(const char *dir, const char *file, long v)
745{
746 FILE *f;
747 char path[MAXPATHLEN];
748 int retv;
749
750 retv = snprintf(path, MAXPATHLEN, "%s/%s", dir, file);
751 if (retv < 0 || retv >= MAXPATHLEN)
752 return;
753 f = fopen(path, "w");
754 fprintf(f, "%ld\n", v);
755 fclose(f);
756}
757
758static bool file_exists(const char *dir, const char *file)
759{
760 char path[MAXPATHLEN];
761 struct stat sb;
762 int ret;
763
764 ret = snprintf(path, MAXPATHLEN, "%s/%s", dir, file);
765 if (ret < 0 || ret >= MAXPATHLEN)
766 return true;
767 ret = stat(path, &sb);
768 return ret == 0;
769}
770
771static void setup_cpuset_if_needed(char **subsystems, char *path)
772{
773 char *parentpath, *p;
774 long v;
775
776 if (!lxc_string_in_array("cpuset", (const char **) subsystems))
777 return;
778 if (file_exists(path, "cgroup.clone_children"))
779 return;
780 parentpath = strdup(path);
781 if (!parentpath)
782 return;
86f0eb65 783 if ((p = strrchr(parentpath, '/')))
0a4d9378
SH
784 *p = '\0';
785 v = get_value(parentpath, "cpuset.mems");
786 set_value(path, "cpuset.mems", v);
787 v = get_value(parentpath, "cpuset.cpus");
788 set_value(path, "cpuset.cpus", v);
789 free(parentpath);
790}
791
33ad9f1a 792/* create a new cgroup */
d4ef7c50 793struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern)
33ad9f1a 794{
001b026e 795 char **cgroup_path_components = NULL;
33ad9f1a
CS
796 char **p = NULL;
797 char *path_so_far = NULL;
798 char **new_cgroup_paths = NULL;
799 char **new_cgroup_paths_sub = NULL;
800 struct cgroup_mount_point *mp;
801 struct cgroup_hierarchy *h;
802 struct cgroup_process_info *base_info = NULL;
803 struct cgroup_process_info *info_ptr;
804 int saved_errno;
805 int r;
806 unsigned suffix = 0;
807 bool had_sub_pattern = false;
808 size_t i;
ae5c8b8e 809
33ad9f1a
CS
810 if (!is_valid_cgroup(name)) {
811 ERROR("Invalid cgroup name: '%s'", name);
812 errno = EINVAL;
813 return NULL;
ae5c8b8e
SH
814 }
815
33ad9f1a
CS
816 if (!strstr(path_pattern, "%n")) {
817 ERROR("Invalid cgroup path pattern: '%s'; contains no %%n for specifying container name", path_pattern);
818 errno = EINVAL;
819 return NULL;
820 }
fd37327f 821
33ad9f1a
CS
822 /* we will modify the result of this operation directly,
823 * so we don't have to copy the data structure
824 */
825 base_info = (path_pattern[0] == '/') ?
826 lxc_cgroup_process_info_get_init(meta_data) :
827 lxc_cgroup_process_info_get_self(meta_data);
828 if (!base_info)
829 return NULL;
c8f7c563 830
33ad9f1a
CS
831 new_cgroup_paths = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));
832 if (!new_cgroup_paths)
833 goto out_initial_error;
834
835 new_cgroup_paths_sub = calloc(meta_data->maximum_hierarchy + 1, sizeof(char *));
836 if (!new_cgroup_paths_sub)
837 goto out_initial_error;
838
839 /* find mount points we can use */
840 for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) {
841 h = info_ptr->hierarchy;
842 mp = lxc_cgroup_find_mount_point(h, info_ptr->cgroup_path, true);
843 if (!mp) {
844 ERROR("Could not find writable mount point for cgroup hierarchy %d while trying to create cgroup.", h->index);
845 goto out_initial_error;
846 }
847 info_ptr->designated_mount_point = mp;
460a1cf0 848
692ba18f
SH
849 if (lxc_string_in_array("ns", (const char **)h->subsystems))
850 continue;
1ea59ad2 851 if (handle_cgroup_settings(mp, info_ptr->cgroup_path) < 0) {
33ad9f1a
CS
852 ERROR("Could not set clone_children to 1 for cpuset hierarchy in parent cgroup.");
853 goto out_initial_error;
854 }
855 }
b98f7d6e 856
33ad9f1a
CS
857 /* normalize the path */
858 cgroup_path_components = lxc_normalize_path(path_pattern);
859 if (!cgroup_path_components)
860 goto out_initial_error;
861
862 /* go through the path components to see if we can create them */
863 for (p = cgroup_path_components; *p || (sub_pattern && !had_sub_pattern); p++) {
864 /* we only want to create the same component with -1, -2, etc.
865 * if the component contains the container name itself, otherwise
866 * it's not an error if it already exists
867 */
868 char *p_eff = *p ? *p : (char *)sub_pattern;
869 bool contains_name = strstr(p_eff, "%n");
870 char *current_component = NULL;
871 char *current_subpath = NULL;
872 char *current_entire_path = NULL;
873 char *parts[3];
874 size_t j = 0;
875 i = 0;
876
877 /* if we are processing the subpattern, we want to make sure
878 * loop is ended the next time around
879 */
880 if (!*p) {
881 had_sub_pattern = true;
882 p--;
883 }
b98f7d6e 884
33ad9f1a
CS
885 goto find_name_on_this_level;
886
887 cleanup_name_on_this_level:
888 /* This is reached if we found a name clash.
889 * In that case, remove the cgroup from all previous hierarchies
890 */
891 for (j = 0, info_ptr = base_info; j < i && info_ptr; info_ptr = info_ptr->next, j++) {
603c64c2 892 r = remove_cgroup(info_ptr->designated_mount_point, info_ptr->created_paths[info_ptr->created_paths_count - 1], false);
33ad9f1a
CS
893 if (r < 0)
894 WARN("could not clean up cgroup we created when trying to create container");
895 free(info_ptr->created_paths[info_ptr->created_paths_count - 1]);
896 info_ptr->created_paths[--info_ptr->created_paths_count] = NULL;
897 }
898 if (current_component != current_subpath)
899 free(current_subpath);
900 if (current_component != p_eff)
901 free(current_component);
902 current_component = current_subpath = NULL;
903 /* try again with another suffix */
904 ++suffix;
905
906 find_name_on_this_level:
907 /* determine name of the path component we should create */
908 if (contains_name && suffix > 0) {
909 char *buf = calloc(strlen(name) + 32, 1);
910 if (!buf)
911 goto out_initial_error;
912 snprintf(buf, strlen(name) + 32, "%s-%u", name, suffix);
913 current_component = lxc_string_replace("%n", buf, p_eff);
914 free(buf);
915 } else {
916 current_component = contains_name ? lxc_string_replace("%n", name, p_eff) : p_eff;
917 }
918 parts[0] = path_so_far;
919 parts[1] = current_component;
920 parts[2] = NULL;
921 current_subpath = path_so_far ? lxc_string_join("/", (const char **)parts, false) : current_component;
922
923 /* Now go through each hierarchy and try to create the
924 * corresponding cgroup
925 */
926 for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) {
927 char *parts2[3];
692ba18f
SH
928
929 if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
930 continue;
33ad9f1a
CS
931 current_entire_path = NULL;
932
933 parts2[0] = !strcmp(info_ptr->cgroup_path, "/") ? "" : info_ptr->cgroup_path;
934 parts2[1] = current_subpath;
935 parts2[2] = NULL;
936 current_entire_path = lxc_string_join("/", (const char **)parts2, false);
937
938 if (!*p) {
939 /* we are processing the subpath, so only update that one */
940 free(new_cgroup_paths_sub[i]);
941 new_cgroup_paths_sub[i] = strdup(current_entire_path);
942 if (!new_cgroup_paths_sub[i])
943 goto cleanup_from_error;
944 } else {
945 /* remember which path was used on this controller */
946 free(new_cgroup_paths[i]);
947 new_cgroup_paths[i] = strdup(current_entire_path);
948 if (!new_cgroup_paths[i])
949 goto cleanup_from_error;
950 }
fd4f5a56 951
33ad9f1a
CS
952 r = create_cgroup(info_ptr->designated_mount_point, current_entire_path);
953 if (r < 0 && errno == EEXIST && contains_name) {
954 /* name clash => try new name with new suffix */
955 free(current_entire_path);
956 current_entire_path = NULL;
957 goto cleanup_name_on_this_level;
958 } else if (r < 0 && errno != EEXIST) {
959 SYSERROR("Could not create cgroup %s", current_entire_path);
960 goto cleanup_from_error;
961 } else if (r == 0) {
962 /* successfully created */
963 r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8);
964 if (r < 0)
965 goto cleanup_from_error;
966 info_ptr->created_paths[info_ptr->created_paths_count++] = current_entire_path;
0a4d9378
SH
967 setup_cpuset_if_needed(info_ptr->hierarchy->subsystems,
968 current_entire_path);
33ad9f1a
CS
969 } else {
970 /* if we didn't create the cgroup, then we have to make sure that
971 * further cgroups will be created properly
972 */
1ea59ad2 973 if (handle_cgroup_settings(mp, info_ptr->cgroup_path) < 0) {
33ad9f1a
CS
974 ERROR("Could not set clone_children to 1 for cpuset hierarchy in pre-existing cgroup.");
975 goto cleanup_from_error;
976 }
977
978 /* already existed but path component of pattern didn't contain '%n',
979 * so this is not an error; but then we don't need current_entire_path
980 * anymore...
981 */
982 free(current_entire_path);
983 current_entire_path = NULL;
984 }
985 }
fd4f5a56 986
33ad9f1a
CS
987 /* save path so far */
988 free(path_so_far);
989 path_so_far = strdup(current_subpath);
990 if (!path_so_far)
991 goto cleanup_from_error;
992
993 /* cleanup */
994 if (current_component != current_subpath)
995 free(current_subpath);
996 if (current_component != p_eff)
997 free(current_component);
998 current_component = current_subpath = NULL;
999 continue;
1000
1001 cleanup_from_error:
1002 /* called if an error occured in the loop, so we
1003 * do some additional cleanup here
1004 */
1005 saved_errno = errno;
1006 if (current_component != current_subpath)
1007 free(current_subpath);
1008 if (current_component != p_eff)
1009 free(current_component);
1010 free(current_entire_path);
1011 errno = saved_errno;
1012 goto out_initial_error;
fd4f5a56
DL
1013 }
1014
33ad9f1a
CS
1015 /* we're done, now update the paths */
1016 for (i = 0, info_ptr = base_info; info_ptr; info_ptr = info_ptr->next, i++) {
47d8fb3b
CS
1017 /* ignore legacy 'ns' subsystem here, lxc_cgroup_create_legacy
1018 * will take care of it
1019 * Since we do a continue in above loop, new_cgroup_paths[i] is
1020 * unset anyway, as is new_cgroup_paths_sub[i]
692ba18f 1021 */
47d8fb3b
CS
1022 if (lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
1023 continue;
1024 free(info_ptr->cgroup_path);
1025 info_ptr->cgroup_path = new_cgroup_paths[i];
1026 info_ptr->cgroup_path_sub = new_cgroup_paths_sub[i];
fd4f5a56 1027 }
33ad9f1a
CS
1028 /* don't use lxc_free_array since we used the array members
1029 * to store them in our result...
1030 */
1031 free(new_cgroup_paths);
1032 free(new_cgroup_paths_sub);
1033 free(path_so_far);
1034 lxc_free_array((void **)cgroup_path_components, free);
1035 return base_info;
1036
1037out_initial_error:
1038 saved_errno = errno;
1039 free(path_so_far);
1040 lxc_cgroup_process_info_free_and_remove(base_info);
1041 lxc_free_array((void **)new_cgroup_paths, free);
1042 lxc_free_array((void **)new_cgroup_paths_sub, free);
1043 lxc_free_array((void **)cgroup_path_components, free);
1044 errno = saved_errno;
1045 return NULL;
c8f7c563
CS
1046}
1047
47d8fb3b
CS
1048int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid)
1049{
1050 struct cgroup_process_info *info_ptr;
1051 int r;
1052
1053 for (info_ptr = base_info; info_ptr; info_ptr = info_ptr->next) {
1054 if (!lxc_string_in_array("ns", (const char **)info_ptr->hierarchy->subsystems))
1055 continue;
1056 /*
1057 * For any path which has ns cgroup mounted, handler->pid is already
1058 * moved into a container called '%d % (handler->pid)'. Rename it to
1059 * the cgroup name and record that.
1060 */
1061 char *tmp = cgroup_rename_nsgroup((const char *)info_ptr->designated_mount_point->mount_point,
1062 info_ptr->cgroup_path, pid, name);
1063 if (!tmp)
1064 return -1;
1065 free(info_ptr->cgroup_path);
1066 info_ptr->cgroup_path = tmp;
1067 r = lxc_grow_array((void ***)&info_ptr->created_paths, &info_ptr->created_paths_capacity, info_ptr->created_paths_count + 1, 8);
1068 if (r < 0)
1069 return -1;
1070 tmp = strdup(tmp);
1071 if (!tmp)
1072 return -1;
1073 info_ptr->created_paths[info_ptr->created_paths_count++] = tmp;
1074 }
1075 return 0;
1076}
1077
33ad9f1a
CS
1078/* get the cgroup membership of a given container */
1079struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data)
c8f7c563 1080{
33ad9f1a
CS
1081 struct cgroup_process_info *result = NULL;
1082 int saved_errno = 0;
1083 size_t i;
1084 struct cgroup_process_info **cptr = &result;
1085 struct cgroup_process_info *entry = NULL;
1086 char *path = NULL;
1087
1088 for (i = 0; i <= meta_data->maximum_hierarchy; i++) {
1089 struct cgroup_hierarchy *h = meta_data->hierarchies[i];
1090 if (!h || !h->used)
1091 continue;
c8f7c563 1092
33ad9f1a
CS
1093 /* use the command interface to look for the cgroup */
1094 path = lxc_cmd_get_cgroup_path(name, lxcpath, h->subsystems[0]);
1095 if (!path)
1096 goto out_error;
1097
1098 entry = calloc(1, sizeof(struct cgroup_process_info));
1099 if (!entry)
1100 goto out_error;
1101 entry->meta_ref = lxc_cgroup_get_meta(meta_data);
1102 entry->hierarchy = h;
1103 entry->cgroup_path = path;
1104 path = NULL;
1105
1106 /* it is not an error if we don't find anything here,
1107 * it is up to the caller to decide what to do in that
1108 * case */
1109 entry->designated_mount_point = lxc_cgroup_find_mount_point(h, entry->cgroup_path, true);
1110
1111 *cptr = entry;
1112 cptr = &entry->next;
1113 entry = NULL;
c8f7c563
CS
1114 }
1115
33ad9f1a
CS
1116 return result;
1117out_error:
1118 saved_errno = errno;
1119 free(path);
1120 lxc_cgroup_process_info_free(result);
1121 lxc_cgroup_process_info_free(entry);
1122 errno = saved_errno;
1123 return NULL;
fd4f5a56
DL
1124}
1125
33ad9f1a 1126/* move a processs to the cgroups specified by the membership */
d4ef7c50 1127int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub)
4f17323e 1128{
33ad9f1a
CS
1129 char pid_buf[32];
1130 char *cgroup_tasks_fn;
1131 int r;
1132 struct cgroup_process_info *info_ptr;
1133
1134 snprintf(pid_buf, 32, "%lu", (unsigned long)pid);
1135 for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) {
1136 char *cgroup_path = (enter_sub && info_ptr->cgroup_path_sub) ?
1137 info_ptr->cgroup_path_sub :
1138 info_ptr->cgroup_path;
1139
1140 if (!info_ptr->designated_mount_point) {
1141 info_ptr->designated_mount_point = lxc_cgroup_find_mount_point(info_ptr->hierarchy, cgroup_path, true);
1142 if (!info_ptr->designated_mount_point) {
1143 SYSERROR("Could not add pid %lu to cgroup %s: internal error (couldn't find any writable mountpoint to cgroup filesystem)", (unsigned long)pid, cgroup_path);
1144 return -1;
1145 }
1146 }
4f17323e 1147
33ad9f1a
CS
1148 cgroup_tasks_fn = cgroup_to_absolute_path(info_ptr->designated_mount_point, cgroup_path, "/tasks");
1149 if (!cgroup_tasks_fn) {
1150 SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path);
1151 return -1;
1152 }
4f17323e 1153
33ad9f1a 1154 r = lxc_write_to_file(cgroup_tasks_fn, pid_buf, strlen(pid_buf), false);
5903da82 1155 free(cgroup_tasks_fn);
33ad9f1a
CS
1156 if (r < 0) {
1157 SYSERROR("Could not add pid %lu to cgroup %s: internal error", (unsigned long)pid, cgroup_path);
1158 return -1;
1159 }
4f17323e
CS
1160 }
1161
33ad9f1a 1162 return 0;
4f17323e
CS
1163}
1164
33ad9f1a
CS
1165/* free process membership information */
1166void lxc_cgroup_process_info_free(struct cgroup_process_info *info)
fc7de561 1167{
33ad9f1a
CS
1168 struct cgroup_process_info *next;
1169 if (!info)
b98f7d6e 1170 return;
33ad9f1a
CS
1171 next = info->next;
1172 lxc_cgroup_put_meta(info->meta_ref);
1173 free(info->cgroup_path);
1174 free(info->cgroup_path_sub);
1175 lxc_free_array((void **)info->created_paths, free);
1176 free(info);
1177 lxc_cgroup_process_info_free(next);
fc7de561
SH
1178}
1179
33ad9f1a
CS
1180/* free process membership information and remove cgroups that were created */
1181void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info)
b98f7d6e 1182{
33ad9f1a
CS
1183 struct cgroup_process_info *next;
1184 char **pp;
1185 if (!info)
1186 return;
1187 next = info->next;
603c64c2 1188 {
33ad9f1a
CS
1189 struct cgroup_mount_point *mp = info->designated_mount_point;
1190 if (!mp)
1191 mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
1192 if (mp)
1193 /* ignore return value here, perhaps we created the
1194 * '/lxc' cgroup in this container but another container
1195 * is still running (for example)
1196 */
603c64c2
SH
1197 (void)remove_cgroup(mp, info->cgroup_path, true);
1198 }
1199 for (pp = info->created_paths; pp && *pp; pp++);
1200 for ((void)(pp && --pp); info->created_paths && pp >= info->created_paths; --pp) {
33ad9f1a 1201 free(*pp);
b98f7d6e 1202 }
33ad9f1a
CS
1203 free(info->created_paths);
1204 lxc_cgroup_put_meta(info->meta_ref);
1205 free(info->cgroup_path);
1206 free(info->cgroup_path_sub);
1207 free(info);
9431aa65 1208 lxc_cgroup_process_info_free_and_remove(next);
33ad9f1a 1209}
b98f7d6e 1210
d4ef7c50 1211static char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct lxc_handler *handler)
33ad9f1a 1212{
d4ef7c50
SH
1213 struct cgfs_data *d = handler->cgroup_info->data;
1214 struct cgroup_process_info *info = d->info;
1215 info = find_info_for_subsystem(info, subsystem);
33ad9f1a
CS
1216 if (!info)
1217 return NULL;
1218 return info->cgroup_path;
b98f7d6e
SH
1219}
1220
33ad9f1a 1221char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, const char *lxcpath)
b98f7d6e 1222{
33ad9f1a 1223 return lxc_cmd_get_cgroup_path(name, lxcpath, subsystem);
b98f7d6e
SH
1224}
1225
33ad9f1a 1226char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lxc_handler *handler)
b98f7d6e 1227{
d4ef7c50
SH
1228 struct cgfs_data *d = handler->cgroup_info->data;
1229 struct cgroup_process_info *info = d->info;
33ad9f1a 1230 struct cgroup_mount_point *mp = NULL;
d4ef7c50
SH
1231
1232 info = find_info_for_subsystem(info, subsystem);
33ad9f1a
CS
1233 if (!info)
1234 return NULL;
1235 if (info->designated_mount_point) {
8900b9eb 1236 mp = info->designated_mount_point;
33ad9f1a
CS
1237 } else {
1238 mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
1239 if (!mp)
1240 return NULL;
b98f7d6e 1241 }
33ad9f1a 1242 return cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
b98f7d6e 1243}
55c76589 1244
33ad9f1a 1245char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath)
9a93d992 1246{
33ad9f1a
CS
1247 struct cgroup_meta_data *meta;
1248 struct cgroup_process_info *base_info, *info;
1249 struct cgroup_mount_point *mp;
1250 char *result = NULL;
33ad9f1a
CS
1251
1252 meta = lxc_cgroup_load_meta();
1253 if (!meta)
9a93d992 1254 return NULL;
33ad9f1a
CS
1255 base_info = lxc_cgroup_get_container_info(name, lxcpath, meta);
1256 if (!base_info)
178938fe 1257 goto out1;
33ad9f1a
CS
1258 info = find_info_for_subsystem(base_info, subsystem);
1259 if (!info)
178938fe 1260 goto out2;
33ad9f1a 1261 if (info->designated_mount_point) {
8900b9eb 1262 mp = info->designated_mount_point;
33ad9f1a
CS
1263 } else {
1264 mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
1265 if (!mp)
178938fe 1266 goto out3;
33ad9f1a
CS
1267 }
1268 result = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
178938fe 1269out3:
178938fe 1270out2:
33ad9f1a 1271 lxc_cgroup_process_info_free(base_info);
178938fe 1272out1:
33ad9f1a 1273 lxc_cgroup_put_meta(meta);
33ad9f1a
CS
1274 return result;
1275}
9a93d992 1276
33ad9f1a
CS
1277int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler)
1278{
1279 char *subsystem = NULL, *p, *path;
1280 int ret = -1;
9a93d992 1281
33ad9f1a
CS
1282 subsystem = alloca(strlen(filename) + 1);
1283 strcpy(subsystem, filename);
1284 if ((p = index(subsystem, '.')) != NULL)
1285 *p = '\0';
9a93d992 1286
33ad9f1a
CS
1287 path = lxc_cgroup_get_hierarchy_abs_path_handler(subsystem, handler);
1288 if (path) {
1289 ret = do_cgroup_set(path, filename, value);
1290 free(path);
9a93d992 1291 }
33ad9f1a
CS
1292 return ret;
1293}
9a93d992 1294
33ad9f1a
CS
1295int lxc_cgroup_get_handler(const char *filename, char *value, size_t len, struct lxc_handler *handler)
1296{
1297 char *subsystem = NULL, *p, *path;
1298 int ret = -1;
1299
1300 subsystem = alloca(strlen(filename) + 1);
1301 strcpy(subsystem, filename);
1302 if ((p = index(subsystem, '.')) != NULL)
1303 *p = '\0';
1304
1305 path = lxc_cgroup_get_hierarchy_abs_path_handler(subsystem, handler);
1306 if (path) {
1307 ret = do_cgroup_get(path, filename, value, len);
1308 free(path);
1309 }
9a93d992
SH
1310 return ret;
1311}
1312
d4ef7c50 1313int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath)
9a93d992 1314{
33ad9f1a
CS
1315 char *subsystem = NULL, *p, *path;
1316 int ret = -1;
9a93d992 1317
33ad9f1a
CS
1318 subsystem = alloca(strlen(filename) + 1);
1319 strcpy(subsystem, filename);
1320 if ((p = index(subsystem, '.')) != NULL)
1321 *p = '\0';
9a93d992 1322
33ad9f1a
CS
1323 path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath);
1324 if (path) {
1325 ret = do_cgroup_set(path, filename, value);
1326 free(path);
1327 }
b98f7d6e 1328 return ret;
9a93d992
SH
1329}
1330
d4ef7c50 1331int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
9a93d992 1332{
33ad9f1a
CS
1333 char *subsystem = NULL, *p, *path;
1334 int ret = -1;
1335
1336 subsystem = alloca(strlen(filename) + 1);
1337 strcpy(subsystem, filename);
1338 if ((p = index(subsystem, '.')) != NULL)
1339 *p = '\0';
1340
1341 path = lxc_cgroup_get_hierarchy_abs_path(subsystem, name, lxcpath);
1342 if (path) {
1343 ret = do_cgroup_get(path, filename, value, len);
1344 free(path);
9a93d992 1345 }
33ad9f1a 1346 return ret;
9a93d992
SH
1347}
1348
33ad9f1a
CS
1349/*
1350 * lxc_cgroup_path_get: Get the absolute pathname for a cgroup
1351 * file for a running container.
1352 *
1353 * @filename : the file of interest (e.g. "freezer.state") or
1354 * the subsystem name (e.g. "freezer") in which case
1355 * the directory where the cgroup may be modified
1356 * will be returned
1357 * @name : name of container to connect to
1358 * @lxcpath : the lxcpath in which the container is running
8900b9eb 1359 *
33ad9f1a
CS
1360 * This is the exported function, which determines cgpath from the
1361 * lxc-start of the @name container running in @lxcpath.
1362 *
1363 * Returns path on success, NULL on error. The caller must free()
1364 * the returned path.
1365 */
1366char *lxc_cgroup_path_get(const char *filename, const char *name,
1367 const char *lxcpath)
9a93d992 1368{
33ad9f1a 1369 char *subsystem = NULL, *longer_file = NULL, *p, *group, *path;
9a93d992 1370
33ad9f1a
CS
1371 subsystem = alloca(strlen(filename) + 1);
1372 strcpy(subsystem, filename);
1373 if ((p = index(subsystem, '.')) != NULL) {
1374 *p = '\0';
1375 longer_file = alloca(strlen(filename) + 2);
1376 longer_file[0] = '/';
1377 strcpy(longer_file + 1, filename);
b98f7d6e
SH
1378 }
1379
33ad9f1a
CS
1380 group = lxc_cgroup_get_hierarchy_path(subsystem, name, lxcpath);
1381 if (!group)
1382 return NULL;
b98f7d6e 1383
86b3688b 1384 path = lxc_cgroup_find_abs_path(subsystem, group, true, p ? longer_file : NULL);
33ad9f1a
CS
1385 free(group);
1386 return path;
9a93d992
SH
1387}
1388
d4ef7c50 1389int lxc_setup_mount_cgroup(const char *root, struct lxc_cgroup_info *cgroup_info, int type)
aae1f3c4
CS
1390{
1391 size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup");
1392 char *path = NULL;
1393 char **parts = NULL;
1394 char *dirname = NULL;
1395 char *abs_path = NULL;
1396 char *abs_path2 = NULL;
d4ef7c50
SH
1397 struct cgfs_data *cgfs_d;
1398 struct cgroup_process_info *info, *base_info;
aae1f3c4
CS
1399 int r, saved_errno = 0;
1400
d4ef7c50
SH
1401 init_cg_ops();
1402
1403 if (strcmp(active_cg_ops->name, "cgmanager") == 0) {
1404 // todo - offer to bind-mount /sys/fs/cgroup/cgmanager/
1405 return 0;
1406 }
1407
1408 cgfs_d = cgroup_info->data;
1409 base_info = cgfs_d->info;
1410
7997d7da
CS
1411 if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) {
1412 ERROR("could not mount cgroups into container: invalid type specified internally");
1413 errno = EINVAL;
1414 return -1;
1415 }
1416
aae1f3c4
CS
1417 path = calloc(1, bufsz);
1418 if (!path)
1419 return -1;
1420 snprintf(path, bufsz, "%s/sys/fs/cgroup", root);
1421 r = mount("cgroup_root", path, "tmpfs", MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME, "size=10240k,mode=755");
1422 if (r < 0) {
1423 SYSERROR("could not mount tmpfs to /sys/fs/cgroup in the container");
1424 return -1;
1425 }
1426
1427 /* now mount all the hierarchies we care about */
1428 for (info = base_info; info; info = info->next) {
1429 size_t subsystem_count, i;
1430 struct cgroup_mount_point *mp = info->designated_mount_point;
1431 if (!mp)
1432 mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, true);
1433 if (!mp) {
1434 SYSERROR("could not find original mount point for cgroup hierarchy while trying to mount cgroup filesystem");
1435 goto out_error;
1436 }
1437
1438 subsystem_count = lxc_array_len((void **)info->hierarchy->subsystems);
1439 parts = calloc(subsystem_count + 1, sizeof(char *));
1440 if (!parts)
1441 goto out_error;
1442
1443 for (i = 0; i < subsystem_count; i++) {
1444 if (!strncmp(info->hierarchy->subsystems[i], "name=", 5))
1445 parts[i] = info->hierarchy->subsystems[i] + 5;
1446 else
1447 parts[i] = info->hierarchy->subsystems[i];
1448 }
1449 dirname = lxc_string_join(",", (const char **)parts, false);
1450 if (!dirname)
1451 goto out_error;
1452
1453 /* create subsystem directory */
1454 abs_path = lxc_append_paths(path, dirname);
1455 if (!abs_path)
1456 goto out_error;
1457 r = mkdir_p(abs_path, 0755);
1458 if (r < 0 && errno != EEXIST) {
1459 SYSERROR("could not create cgroup subsystem directory /sys/fs/cgroup/%s", dirname);
1460 goto out_error;
1461 }
1462
aae1f3c4
CS
1463 abs_path2 = lxc_append_paths(abs_path, info->cgroup_path);
1464 if (!abs_path2)
1465 goto out_error;
aae1f3c4 1466
7997d7da
CS
1467 if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_RW || type == LXC_AUTO_CGROUP_FULL_MIXED) {
1468 /* bind-mount the cgroup entire filesystem there */
1469 if (strcmp(mp->mount_prefix, "/") != 0) {
1470 /* FIXME: maybe we should just try to remount the entire hierarchy
1471 * with a regular mount command? may that works? */
1472 ERROR("could not automatically mount cgroup-full to /sys/fs/cgroup/%s: host has no mount point for this cgroup filesystem that has access to the root cgroup", dirname);
1473 goto out_error;
1474 }
1475 r = mount(mp->mount_point, abs_path, "none", MS_BIND, 0);
1476 if (r < 0) {
1477 SYSERROR("error bind-mounting %s to %s", mp->mount_point, abs_path);
1478 goto out_error;
1479 }
1480 /* main cgroup path should be read-only */
1481 if (type == LXC_AUTO_CGROUP_FULL_RO || type == LXC_AUTO_CGROUP_FULL_MIXED) {
1482 r = mount(NULL, abs_path, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
1483 if (r < 0) {
1484 SYSERROR("error re-mounting %s readonly", abs_path);
1485 goto out_error;
1486 }
1487 }
1488 /* own cgroup should be read-write */
1489 if (type == LXC_AUTO_CGROUP_FULL_MIXED) {
1490 r = mount(abs_path2, abs_path2, NULL, MS_BIND, NULL);
1491 if (r < 0) {
1492 SYSERROR("error bind-mounting %s onto itself", abs_path2);
1493 goto out_error;
1494 }
1495 r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND, NULL);
1496 if (r < 0) {
1497 SYSERROR("error re-mounting %s readwrite", abs_path2);
1498 goto out_error;
1499 }
1500 }
1501 } else {
1502 /* create path for container's cgroup */
1503 r = mkdir_p(abs_path2, 0755);
1504 if (r < 0 && errno != EEXIST) {
1505 SYSERROR("could not create cgroup directory /sys/fs/cgroup/%s%s", dirname, info->cgroup_path);
1506 goto out_error;
1507 }
aae1f3c4 1508
7997d7da
CS
1509 free(abs_path);
1510 abs_path = NULL;
1511
1512 /* bind-mount container's cgroup to that directory */
1513 abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
1514 if (!abs_path)
1515 goto out_error;
1516 r = mount(abs_path, abs_path2, "none", MS_BIND, 0);
1517 if (r < 0) {
1518 SYSERROR("error bind-mounting %s to %s", abs_path, abs_path2);
1519 goto out_error;
1520 }
1521 if (type == LXC_AUTO_CGROUP_RO) {
1522 r = mount(NULL, abs_path2, NULL, MS_REMOUNT|MS_BIND|MS_RDONLY, NULL);
1523 if (r < 0) {
1524 SYSERROR("error re-mounting %s readonly", abs_path2);
1525 goto out_error;
1526 }
1527 }
aae1f3c4
CS
1528 }
1529
1530 free(abs_path);
1531 free(abs_path2);
1532 abs_path = NULL;
1533 abs_path2 = NULL;
1534
1535 /* add symlinks for every single subsystem */
1536 if (subsystem_count > 1) {
1537 for (i = 0; i < subsystem_count; i++) {
1538 abs_path = lxc_append_paths(path, parts[i]);
1539 if (!abs_path)
1540 goto out_error;
1541 r = symlink(dirname, abs_path);
1542 if (r < 0)
1543 WARN("could not create symlink %s -> %s in /sys/fs/cgroup of container", parts[i], dirname);
1544 free(abs_path);
1545 abs_path = NULL;
1546 }
1547 }
1548 free(dirname);
1549 free(parts);
1550 dirname = NULL;
1551 parts = NULL;
1552 }
1553
1554 /* try to remount the tmpfs readonly, since the container shouldn't
1555 * change anything (this will also make sure that trying to create
1556 * new cgroups outside the allowed area fails with an error instead
1557 * of simply causing this to create directories in the tmpfs itself)
1558 */
7997d7da
CS
1559 if (type != LXC_AUTO_CGROUP_RW && type != LXC_AUTO_CGROUP_FULL_RW)
1560 mount(NULL, path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
aae1f3c4
CS
1561
1562 free(path);
1563
1564 return 0;
1565
1566out_error:
1567 saved_errno = errno;
1568 free(path);
1569 free(dirname);
1570 free(parts);
1571 free(abs_path);
1572 free(abs_path2);
1573 errno = saved_errno;
1574 return -1;
1575}
1576
33ad9f1a
CS
1577int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler)
1578{
d4ef7c50
SH
1579 struct cgfs_data *d = handler->cgroup_info->data;
1580 struct cgroup_process_info *info = d->info;
33ad9f1a
CS
1581 struct cgroup_mount_point *mp = NULL;
1582 char *abs_path = NULL;
1583 int ret;
460a1cf0 1584
33ad9f1a
CS
1585 if (!info) {
1586 errno = ENOENT;
1587 return -1;
b98f7d6e 1588 }
c8f7c563 1589
33ad9f1a 1590 if (info->designated_mount_point) {
8900b9eb 1591 mp = info->designated_mount_point;
33ad9f1a
CS
1592 } else {
1593 mp = lxc_cgroup_find_mount_point(info->hierarchy, info->cgroup_path, false);
1594 if (!mp)
1595 return -1;
c8f7c563
CS
1596 }
1597
33ad9f1a
CS
1598 abs_path = cgroup_to_absolute_path(mp, info->cgroup_path, NULL);
1599 if (!abs_path)
1600 return -1;
1601
1602 ret = cgroup_recursive_task_count(abs_path);
1603 free(abs_path);
1604 return ret;
c8f7c563
CS
1605}
1606
574c4428
QH
1607static struct cgroup_process_info *
1608lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str,
1609 struct cgroup_meta_data *meta)
d08ba6ec 1610{
33ad9f1a
CS
1611 struct cgroup_process_info *result = NULL;
1612 FILE *proc_pid_cgroup = NULL;
1613 char *line = NULL;
1614 size_t sz = 0;
1615 int saved_errno = 0;
1616 struct cgroup_process_info **cptr = &result;
1617 struct cgroup_process_info *entry = NULL;
1618
1619 proc_pid_cgroup = fopen_cloexec(proc_pid_cgroup_str, "r");
1620 if (!proc_pid_cgroup)
b98f7d6e 1621 return NULL;
1ac470c0 1622
33ad9f1a
CS
1623 while (getline(&line, &sz, proc_pid_cgroup) != -1) {
1624 /* file format: hierarchy:subsystems:group */
1625 char *colon1;
1626 char *colon2;
1627 char *endptr;
1628 int hierarchy_number;
1629 struct cgroup_hierarchy *h = NULL;
fd4f5a56 1630
33ad9f1a 1631 if (!line[0])
ae5c8b8e 1632 continue;
b98f7d6e 1633
33ad9f1a
CS
1634 if (line[strlen(line) - 1] == '\n')
1635 line[strlen(line) - 1] = '\0';
1636
1637 colon1 = strchr(line, ':');
1638 if (!colon1)
8900b9eb 1639 continue;
33ad9f1a
CS
1640 *colon1++ = '\0';
1641 colon2 = strchr(colon1, ':');
1642 if (!colon2)
ae5c8b8e 1643 continue;
33ad9f1a 1644 *colon2++ = '\0';
e4659536 1645
33ad9f1a
CS
1646 endptr = NULL;
1647 hierarchy_number = strtoul(line, &endptr, 10);
1648 if (!endptr || *endptr)
9a93d992 1649 continue;
9a93d992 1650
33ad9f1a
CS
1651 if (hierarchy_number > meta->maximum_hierarchy) {
1652 /* we encountered a hierarchy we didn't have before,
1653 * so probably somebody remounted some stuff in the
1654 * mean time...
1655 */
1656 errno = EAGAIN;
1657 goto out_error;
b98f7d6e 1658 }
33ad9f1a
CS
1659
1660 h = meta->hierarchies[hierarchy_number];
1661 if (!h) {
1662 /* we encountered a hierarchy that was thought to be
1663 * dead before, so probably somebody remounted some
1664 * stuff in the mean time...
1665 */
1666 errno = EAGAIN;
1667 goto out_error;
b98f7d6e 1668 }
33ad9f1a
CS
1669
1670 /* we are told that we should ignore this hierarchy */
1671 if (!h->used)
b98f7d6e 1672 continue;
5193cc3d 1673
33ad9f1a
CS
1674 entry = calloc(1, sizeof(struct cgroup_process_info));
1675 if (!entry)
1676 goto out_error;
fd4f5a56 1677
33ad9f1a
CS
1678 entry->meta_ref = lxc_cgroup_get_meta(meta);
1679 entry->hierarchy = h;
1680 entry->cgroup_path = strdup(colon2);
1681 if (!entry->cgroup_path)
1682 goto out_error;
d08ba6ec 1683
33ad9f1a
CS
1684 *cptr = entry;
1685 cptr = &entry->next;
1686 entry = NULL;
b98f7d6e 1687 }
b98f7d6e 1688
33ad9f1a
CS
1689 fclose(proc_pid_cgroup);
1690 free(line);
1691 return result;
1692
1693out_error:
1694 saved_errno = errno;
1695 if (proc_pid_cgroup)
1696 fclose(proc_pid_cgroup);
1697 lxc_cgroup_process_info_free(result);
1698 lxc_cgroup_process_info_free(entry);
1699 free(line);
1700 errno = saved_errno;
ae5c8b8e 1701 return NULL;
36b86299
DL
1702}
1703
574c4428
QH
1704static char **subsystems_from_mount_options(const char *mount_options,
1705 char **kernel_list)
36b86299 1706{
33ad9f1a
CS
1707 char *token, *str, *saveptr = NULL;
1708 char **result = NULL;
1709 size_t result_capacity = 0;
8900b9eb 1710 size_t result_count = 0;
33ad9f1a
CS
1711 int saved_errno;
1712 int r;
ef342abb 1713
33ad9f1a
CS
1714 str = alloca(strlen(mount_options)+1);
1715 strcpy(str, mount_options);
1716 for (; (token = strtok_r(str, ",", &saveptr)); str = NULL) {
1717 /* we have a subsystem if it's either in the list of
1718 * subsystems provided by the kernel OR if it starts
1719 * with name= for named hierarchies
1720 */
1721 if (!strncmp(token, "name=", 5) || lxc_string_in_array(token, (const char **)kernel_list)) {
1722 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 12);
1723 if (r < 0)
1724 goto out_free;
1725 result[result_count + 1] = NULL;
1726 result[result_count] = strdup(token);
1727 if (!result[result_count])
1728 goto out_free;
1729 result_count++;
1730 }
ae5c8b8e 1731 }
f0e64b8b 1732
33ad9f1a
CS
1733 return result;
1734
1735out_free:
1736 saved_errno = errno;
1737 lxc_free_array((void**)result, free);
1738 errno = saved_errno;
1739 return NULL;
b98f7d6e
SH
1740}
1741
574c4428 1742static void lxc_cgroup_mount_point_free(struct cgroup_mount_point *mp)
b98f7d6e 1743{
33ad9f1a
CS
1744 if (!mp)
1745 return;
1746 free(mp->mount_point);
1747 free(mp->mount_prefix);
1748 free(mp);
bcbd102c
SH
1749}
1750
574c4428 1751static void lxc_cgroup_hierarchy_free(struct cgroup_hierarchy *h)
341a9bd8 1752{
33ad9f1a
CS
1753 if (!h)
1754 return;
1755 lxc_free_array((void **)h->subsystems, free);
8bfcb981 1756 free(h->all_mount_points);
33ad9f1a
CS
1757 free(h);
1758}
341a9bd8 1759
574c4428 1760static bool is_valid_cgroup(const char *name)
33ad9f1a
CS
1761{
1762 const char *p;
1763 for (p = name; *p; p++) {
28bb9321
QH
1764 /* Use the ASCII printable characters range(32 - 127)
1765 * is reasonable, we kick out 32(SPACE) because it'll
1766 * break legacy lxc-ls
1767 */
1768 if (*p <= 32 || *p >= 127 || *p == '/')
33ad9f1a 1769 return false;
341a9bd8 1770 }
33ad9f1a
CS
1771 return strcmp(name, ".") != 0 && strcmp(name, "..") != 0;
1772}
341a9bd8 1773
574c4428
QH
1774static int create_or_remove_cgroup(bool do_remove,
1775 struct cgroup_mount_point *mp, const char *path, int recurse)
33ad9f1a
CS
1776{
1777 int r, saved_errno = 0;
1778 char *buf = cgroup_to_absolute_path(mp, path, NULL);
1779 if (!buf)
1780 return -1;
341a9bd8 1781
33ad9f1a 1782 /* create or remove directory */
603c64c2
SH
1783 if (do_remove) {
1784 if (recurse)
1785 r = cgroup_rmdir(buf);
1786 else
1787 r = rmdir(buf);
1788 } else
1789 r = mkdir(buf, 0777);
33ad9f1a
CS
1790 saved_errno = errno;
1791 free(buf);
1792 errno = saved_errno;
1793 return r;
341a9bd8 1794}
bcbd102c 1795
574c4428 1796static int create_cgroup(struct cgroup_mount_point *mp, const char *path)
a6ddef61 1797{
603c64c2 1798 return create_or_remove_cgroup(false, mp, path, false);
a6ddef61
MN
1799}
1800
574c4428
QH
1801static int remove_cgroup(struct cgroup_mount_point *mp,
1802 const char *path, bool recurse)
576f946d 1803{
603c64c2 1804 return create_or_remove_cgroup(true, mp, path, recurse);
33ad9f1a 1805}
576f946d 1806
574c4428
QH
1807static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp,
1808 const char *path, const char *suffix)
33ad9f1a
CS
1809{
1810 /* first we have to make sure we subtract the mount point's prefix */
1811 char *prefix = mp->mount_prefix;
1812 char *buf;
1813 ssize_t len, rv;
1814
1815 /* we want to make sure only absolute paths to cgroups are passed to us */
1816 if (path[0] != '/') {
1817 errno = EINVAL;
1818 return NULL;
1819 }
b98f7d6e 1820
33ad9f1a
CS
1821 if (prefix && !strcmp(prefix, "/"))
1822 prefix = NULL;
b98f7d6e 1823
33ad9f1a
CS
1824 /* prefix doesn't match */
1825 if (prefix && strncmp(prefix, path, strlen(prefix)) != 0) {
1826 errno = EINVAL;
1827 return NULL;
1828 }
1829 /* if prefix is /foo and path is /foobar */
1830 if (prefix && path[strlen(prefix)] != '/' && path[strlen(prefix)] != '\0') {
1831 errno = EINVAL;
1832 return NULL;
1833 }
b98f7d6e 1834
33ad9f1a
CS
1835 /* remove prefix from path */
1836 path += prefix ? strlen(prefix) : 0;
b98f7d6e 1837
33ad9f1a
CS
1838 len = strlen(mp->mount_point) + strlen(path) + (suffix ? strlen(suffix) : 0);
1839 buf = calloc(len + 1, 1);
50266dc6
DE
1840 if (!buf)
1841 return NULL;
33ad9f1a 1842 rv = snprintf(buf, len + 1, "%s%s%s", mp->mount_point, path, suffix ? suffix : "");
8900b9eb 1843 if (rv > len) {
33ad9f1a
CS
1844 free(buf);
1845 errno = ENOMEM;
8900b9eb 1846 return NULL;
8b92dc3a 1847 }
576f946d 1848
33ad9f1a 1849 return buf;
e0f888d9 1850}
283678ed 1851
574c4428
QH
1852static struct cgroup_process_info *
1853find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem)
283678ed 1854{
33ad9f1a
CS
1855 struct cgroup_process_info *info_ptr;
1856 for (info_ptr = info; info_ptr; info_ptr = info_ptr->next) {
1857 struct cgroup_hierarchy *h = info_ptr->hierarchy;
1858 if (lxc_string_in_array(subsystem, (const char **)h->subsystems))
1859 return info_ptr;
b98f7d6e 1860 }
33ad9f1a
CS
1861 errno = ENOENT;
1862 return NULL;
1863}
283678ed 1864
574c4428
QH
1865static int do_cgroup_get(const char *cgroup_path, const char *sub_filename,
1866 char *value, size_t len)
33ad9f1a
CS
1867{
1868 const char *parts[3] = {
1869 cgroup_path,
1870 sub_filename,
1871 NULL
1872 };
1873 char *filename;
1874 int ret, saved_errno;
1875
1876 filename = lxc_string_join("/", parts, false);
1877 if (!filename)
1878 return -1;
1879
1880 ret = lxc_read_from_file(filename, value, len);
1881 saved_errno = errno;
1882 free(filename);
1883 errno = saved_errno;
1884 return ret;
283678ed 1885}
b113383b 1886
574c4428
QH
1887static int do_cgroup_set(const char *cgroup_path, const char *sub_filename,
1888 const char *value)
b113383b 1889{
33ad9f1a
CS
1890 const char *parts[3] = {
1891 cgroup_path,
1892 sub_filename,
1893 NULL
1894 };
1895 char *filename;
1896 int ret, saved_errno;
b113383b 1897
33ad9f1a
CS
1898 filename = lxc_string_join("/", parts, false);
1899 if (!filename)
1900 return -1;
b113383b 1901
33ad9f1a
CS
1902 ret = lxc_write_to_file(filename, value, strlen(value), false);
1903 saved_errno = errno;
1904 free(filename);
1905 errno = saved_errno;
1906 return ret;
b98f7d6e
SH
1907}
1908
9daf6f5d 1909static int do_setup_cgroup_limits(struct lxc_handler *h,
574c4428 1910 struct lxc_list *cgroup_settings, bool do_devices)
b98f7d6e
SH
1911{
1912 struct lxc_list *iterator;
1913 struct lxc_cgroup *cg;
1914 int ret = -1;
1915
33ad9f1a 1916 if (lxc_list_empty(cgroup_settings))
b98f7d6e
SH
1917 return 0;
1918
33ad9f1a 1919 lxc_list_for_each(iterator, cgroup_settings) {
b98f7d6e
SH
1920 cg = iterator->elem;
1921
33ad9f1a 1922 if (do_devices == !strncmp("devices", cg->subsystem, 7)) {
b98f7d6e 1923 if (strcmp(cg->subsystem, "devices.deny") == 0 &&
33ad9f1a 1924 cgroup_devices_has_allow_or_deny(h, cg->value, false))
b98f7d6e
SH
1925 continue;
1926 if (strcmp(cg->subsystem, "devices.allow") == 0 &&
33ad9f1a 1927 cgroup_devices_has_allow_or_deny(h, cg->value, true))
b98f7d6e 1928 continue;
33ad9f1a 1929 if (lxc_cgroup_set_handler(cg->subsystem, cg->value, h)) {
b98f7d6e
SH
1930 ERROR("Error setting %s to %s for %s\n",
1931 cg->subsystem, cg->value, h->name);
1932 goto out;
1933 }
b113383b 1934 }
b98f7d6e
SH
1935
1936 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
b113383b
SH
1937 }
1938
b98f7d6e
SH
1939 ret = 0;
1940 INFO("cgroup has been setup");
1941out:
b113383b
SH
1942 return ret;
1943}
b98f7d6e 1944
574c4428
QH
1945static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h,
1946 char *v, bool for_allow)
33ad9f1a
CS
1947{
1948 char *path;
1949 FILE *devices_list;
8900b9eb 1950 char *line = NULL;
33ad9f1a
CS
1951 size_t sz = 0;
1952 bool ret = !for_allow;
1953 const char *parts[3] = {
1954 NULL,
1955 "devices.list",
1956 NULL
1957 };
1958
1959 // XXX FIXME if users could use something other than 'lxc.devices.deny = a'.
1960 // not sure they ever do, but they *could*
1961 // right now, I'm assuming they do NOT
1962 if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0)
1963 return false;
1964
1965 parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_handler("devices", h);
1966 if (!parts[0])
1967 return false;
1968 path = lxc_string_join("/", parts, false);
1969 if (!path) {
1970 free((void *)parts[0]);
1971 return false;
1972 }
1973
1974 devices_list = fopen_cloexec(path, "r");
1975 if (!devices_list) {
1976 free(path);
1977 return false;
1978 }
1979
1980 while (getline(&line, &sz, devices_list) != -1) {
1981 size_t len = strlen(line);
1982 if (len > 0 && line[len-1] == '\n')
1983 line[len-1] = '\0';
1984 if (strcmp(line, "a *:* rwm") == 0) {
1985 ret = for_allow;
1986 goto out;
1987 } else if (for_allow && strcmp(line, v) == 0) {
1988 ret = true;
8900b9eb 1989 goto out;
33ad9f1a
CS
1990 }
1991 }
1992
1993out:
1994 fclose(devices_list);
1995 free(line);
1996 free(path);
1997 return ret;
1998}
1999
574c4428 2000static int cgroup_recursive_task_count(const char *cgroup_path)
b98f7d6e 2001{
33ad9f1a
CS
2002 DIR *d;
2003 struct dirent *dent_buf;
2004 struct dirent *dent;
8900b9eb 2005 ssize_t name_max;
33ad9f1a
CS
2006 int n = 0, r;
2007
2008 /* see man readdir_r(3) */
2009 name_max = pathconf(cgroup_path, _PC_NAME_MAX);
2010 if (name_max <= 0)
2011 name_max = 255;
2012 dent_buf = malloc(offsetof(struct dirent, d_name) + name_max + 1);
2013 if (!dent_buf)
2014 return -1;
2015
2016 d = opendir(cgroup_path);
034ef75d
SH
2017 if (!d) {
2018 free(dent_buf);
33ad9f1a 2019 return 0;
034ef75d 2020 }
33ad9f1a
CS
2021
2022 while (readdir_r(d, dent_buf, &dent) == 0 && dent) {
2023 const char *parts[3] = {
2024 cgroup_path,
2025 dent->d_name,
2026 NULL
2027 };
2028 char *sub_path;
2029 struct stat st;
2030
2031 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
2032 continue;
2033 sub_path = lxc_string_join("/", parts, false);
2034 if (!sub_path) {
2035 closedir(d);
2036 free(dent_buf);
2037 return -1;
2038 }
2039 r = stat(sub_path, &st);
2040 if (r < 0) {
2041 closedir(d);
2042 free(dent_buf);
2043 free(sub_path);
2044 return -1;
2045 }
2046 if (S_ISDIR(st.st_mode)) {
2047 r = cgroup_recursive_task_count(sub_path);
2048 if (r >= 0)
2049 n += r;
2050 } else if (!strcmp(dent->d_name, "tasks")) {
2051 r = count_lines(sub_path);
2052 if (r >= 0)
2053 n += r;
2054 }
2055 free(sub_path);
2056 }
2057 closedir(d);
2058 free(dent_buf);
2059
2060 return n;
2061}
2062
574c4428 2063static int count_lines(const char *fn)
33ad9f1a
CS
2064{
2065 FILE *f;
2066 char *line = NULL;
2067 size_t sz = 0;
2068 int n = 0;
2069
2070 f = fopen_cloexec(fn, "r");
2071 if (!f)
2072 return -1;
2073
2074 while (getline(&line, &sz, f) != -1) {
2075 n++;
2076 }
2077 free(line);
2078 fclose(f);
2079 return n;
b98f7d6e
SH
2080}
2081
574c4428
QH
2082static int handle_cgroup_settings(struct cgroup_mount_point *mp,
2083 char *cgroup_path)
b98f7d6e 2084{
33ad9f1a 2085 int r, saved_errno = 0;
7e7243e1 2086 char buf[2];
1ea59ad2
SH
2087
2088 /* If this is the memory cgroup, we want to enforce hierarchy.
2089 * But don't fail if for some reason we can't.
2090 */
2091 if (lxc_string_in_array("memory", (const char **)mp->hierarchy->subsystems)) {
2092 char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/memory.use_hierarchy");
2093 if (cc_path) {
7e7243e1
SH
2094 r = lxc_read_from_file(cc_path, buf, 1);
2095 if (r < 1 || buf[0] != '1') {
2096 r = lxc_write_to_file(cc_path, "1", 1, false);
2097 if (r < 0)
2098 SYSERROR("failed to set memory.use_hiararchy to 1; continuing");
2099 }
1ea59ad2
SH
2100 free(cc_path);
2101 }
2102 }
2103
33ad9f1a
CS
2104 /* if this is a cpuset hierarchy, we have to set cgroup.clone_children in
2105 * the base cgroup, otherwise containers will start with an empty cpuset.mems
2106 * and cpuset.cpus and then
2107 */
2108 if (lxc_string_in_array("cpuset", (const char **)mp->hierarchy->subsystems)) {
2109 char *cc_path = cgroup_to_absolute_path(mp, cgroup_path, "/cgroup.clone_children");
0a4d9378
SH
2110 struct stat sb;
2111
33ad9f1a
CS
2112 if (!cc_path)
2113 return -1;
0a4d9378
SH
2114 if (stat(cc_path, &sb) != 0 && errno == ENOENT)
2115 return 0;
7e7243e1
SH
2116 r = lxc_read_from_file(cc_path, buf, 1);
2117 if (r == 1 && buf[0] == '1') {
2118 free(cc_path);
2119 return 0;
2120 }
33ad9f1a
CS
2121 r = lxc_write_to_file(cc_path, "1", 1, false);
2122 saved_errno = errno;
2123 free(cc_path);
2124 errno = saved_errno;
2125 return r < 0 ? -1 : 0;
2126 }
2127 return 0;
b98f7d6e 2128}
484ed030
SH
2129
2130extern void lxc_monitor_send_state(const char *name, lxc_state_t state,
2131 const char *lxcpath);
d4ef7c50 2132int do_unfreeze(int freeze, const char *name, const char *lxcpath)
484ed030 2133{
d4ef7c50
SH
2134 char v[100];
2135 const char *state = freeze ? "FROZEN" : "THAWED";
484ed030 2136
d4ef7c50
SH
2137 if (lxc_cgroup_set("freezer.state", state, name, lxcpath) < 0) {
2138 ERROR("Failed to freeze %s:%s", lxcpath, name);
484ed030
SH
2139 return -1;
2140 }
d4ef7c50
SH
2141 while (1) {
2142 if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) {
2143 ERROR("Failed to get new freezer state for %s:%s", lxcpath, name);
2144 return -1;
2145 }
2146 if (v[strlen(v)-1] == '\n')
2147 v[strlen(v)-1] = '\0';
2148 if (strncmp(v, state, strlen(state)) == 0) {
2149 if (name)
2150 lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath);
2151 return 0;
2152 }
2153 sleep(1);
484ed030 2154 }
d4ef7c50 2155}
484ed030 2156
d4ef7c50
SH
2157int freeze_unfreeze(const char *name, int freeze, const char *lxcpath)
2158{
2159 return do_unfreeze(freeze, name, lxcpath);
2160}
484ed030 2161
d4ef7c50
SH
2162lxc_state_t freezer_state(const char *name, const char *lxcpath)
2163{
2164 char v[100];
e8d07ef2 2165 if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0)
d4ef7c50 2166 return -1;
484ed030 2167
d4ef7c50
SH
2168 if (v[strlen(v)-1] == '\n')
2169 v[strlen(v)-1] = '\0';
2170 return lxc_str2state(v);
2171}
484ed030 2172
d4ef7c50
SH
2173static void cgfs_destroy(struct lxc_handler *handler)
2174{
2175 struct cgfs_data *d = handler->cgroup_info->data;
2176 if (!d)
2177 return;
2178 if (d->info)
2179 lxc_cgroup_process_info_free_and_remove(d->info);
2180 if (d->meta)
2181 lxc_cgroup_put_meta(d->meta);
2182 free(d);
2183 handler->cgroup_info->data = NULL;
2184}
484ed030 2185
d4ef7c50
SH
2186static inline bool cgfs_init(struct lxc_handler *handler)
2187{
2188 struct cgfs_data *d = malloc(sizeof(*d));
2189 if (!d)
2190 return false;
2191 d->info = NULL;
2192 d->meta = lxc_cgroup_load_meta();
484ed030 2193
d4ef7c50
SH
2194 if (!d->meta) {
2195 ERROR("cgroupfs failed to detect cgroup metadata");
378a5729 2196 free(d);
d4ef7c50
SH
2197 return false;
2198 }
2199 handler->cgroup_info->data = d;
2200 return true;
2201}
484ed030 2202
d4ef7c50
SH
2203static inline bool cgfs_create(struct lxc_handler *handler)
2204{
2205 struct cgfs_data *d = handler->cgroup_info->data;
2206 struct cgroup_process_info *i;
2207 struct cgroup_meta_data *md = d->meta;
2208 i = lxc_cgroupfs_create(handler->name, handler->cgroup_info->cgroup_pattern, md, NULL);
2209 if (!i)
2210 return false;
2211 d->info = i;
2212 return true;
2213}
484ed030 2214
d4ef7c50
SH
2215static inline bool cgfs_enter(struct lxc_handler *handler)
2216{
2217 struct cgfs_data *d = handler->cgroup_info->data;
2218 struct cgroup_process_info *i = d->info;
2219 int ret;
2220
2221 ret = lxc_cgroupfs_enter(i, handler->pid, false);
484ed030 2222
d4ef7c50
SH
2223 return ret == 0;
2224}
2225
2226static inline bool cgfs_create_legacy(struct lxc_handler *handler)
2227{
2228 struct cgfs_data *d = handler->cgroup_info->data;
2229 struct cgroup_process_info *i = d->info;
2230 if (lxc_cgroup_create_legacy(i, handler->name, handler->pid) < 0) {
2231 ERROR("failed to create legacy ns cgroups for '%s'", handler->name);
2232 return false;
484ed030 2233 }
d4ef7c50
SH
2234 return true;
2235}
484ed030 2236
d4ef7c50
SH
2237static char *cgfs_get_cgroup(struct lxc_handler *handler, const char *subsystem)
2238{
2239 return lxc_cgroup_get_hierarchy_path_handler(subsystem, handler);
484ed030
SH
2240}
2241
0086f499
SH
2242static int cgfs_unfreeze_fromhandler(struct lxc_handler *handler)
2243{
2244 char *cgabspath, *cgrelpath;
2245 int ret;
2246
2247 cgrelpath = lxc_cgroup_get_hierarchy_path_handler("freezer", handler);
2248 cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL);
2249 if (!cgabspath)
2250 return -1;
2251
2252 ret = do_cgroup_set(cgabspath, "freezer.state", "THAWED");
2253 free(cgabspath);
2254 return ret;
2255}
2256
9daf6f5d
SH
2257bool cgroupfs_setup_limits(struct lxc_handler *h, bool with_devices)
2258{
2259 return do_setup_cgroup_limits(h, &h->conf->cgroup, with_devices) == 0;
2260}
2261
d4ef7c50
SH
2262static struct cgroup_ops cgfs_ops = {
2263 .destroy = cgfs_destroy,
2264 .init = cgfs_init,
2265 .create = cgfs_create,
2266 .enter = cgfs_enter,
2267 .create_legacy = cgfs_create_legacy,
2268 .get_cgroup = cgfs_get_cgroup,
2269 .get = lxc_cgroupfs_get,
2270 .set = lxc_cgroupfs_set,
0086f499 2271 .unfreeze_fromhandler = cgfs_unfreeze_fromhandler,
9daf6f5d 2272 .setup_limits = cgroupfs_setup_limits,
d4ef7c50
SH
2273 .name = "cgroupfs",
2274};
2275static void init_cg_ops(void)
484ed030 2276{
d4ef7c50
SH
2277 if (!use_cgmanager)
2278 return;
2279 if (cgmanager_initialized)
2280 return;
2281 if (!lxc_init_cgmanager()) {
2282 ERROR("Could not contact cgroup manager, falling back to cgroupfs");
2283 active_cg_ops = &cgfs_ops;
2284 }
2285}
484ed030 2286
d4ef7c50
SH
2287/*
2288 * These are the backend-independent cgroup handlers for container
2289 * start and stop
2290 */
484ed030 2291
d4ef7c50
SH
2292/* Free all cgroup info held by the handler */
2293void cgroup_destroy(struct lxc_handler *handler)
2294{
2295 if (!handler->cgroup_info)
2296 return;
2297 if (active_cg_ops)
2298 active_cg_ops->destroy(handler);
484ed030
SH
2299}
2300
d4ef7c50
SH
2301/*
2302 * Allocate a lxc_cgroup_info for the active cgroup
2303 * backend, and assign it to the handler
2304 */
2305bool cgroup_init(struct lxc_handler *handler)
484ed030 2306{
d4ef7c50
SH
2307 init_cg_ops();
2308 handler->cgroup_info = malloc(sizeof(struct lxc_cgroup_info));
2309 if (!handler->cgroup_info)
2310 return false;
2311 memset(handler->cgroup_info, 0, sizeof(struct lxc_cgroup_info));
2312 /* if we are running as root, use system cgroup pattern, otherwise
2313 * just create a cgroup under the current one. But also fall back to
2314 * that if for some reason reading the configuration fails and no
2315 * default value is available
2316 */
2317 if (geteuid() == 0)
2318 handler->cgroup_info->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
2319 if (!handler->cgroup_info->cgroup_pattern)
2320 handler->cgroup_info->cgroup_pattern = "%n";
484ed030 2321
d4ef7c50
SH
2322 return active_cg_ops->init(handler);
2323}
484ed030 2324
d4ef7c50
SH
2325/* Create the container cgroups for all requested controllers */
2326bool cgroup_create(struct lxc_handler *handler)
2327{
2328 return active_cg_ops->create(handler);
2329}
484ed030 2330
d4ef7c50
SH
2331/*
2332 * Enter the container init into its new cgroups for all
2333 * requested controllers */
2334bool cgroup_enter(struct lxc_handler *handler)
2335{
2336 return active_cg_ops->enter(handler);
2337}
484ed030 2338
d4ef7c50
SH
2339bool cgroup_create_legacy(struct lxc_handler *handler)
2340{
2341 if (active_cg_ops->create_legacy)
2342 return active_cg_ops->create_legacy(handler);
2343 return true;
2344}
484ed030 2345
d4ef7c50
SH
2346char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem)
2347{
2348 return active_cg_ops->get_cgroup(handler, subsystem);
484ed030
SH
2349}
2350
d4ef7c50
SH
2351int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath)
2352{
2353 init_cg_ops();
2354 return active_cg_ops->set(filename, value, name, lxcpath);
2355}
2356
2357int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
2358{
2359 init_cg_ops();
2360 return active_cg_ops->get(filename, value, len, name, lxcpath);
2361}
0086f499
SH
2362
2363int lxc_unfreeze_fromhandler(struct lxc_handler *handler)
2364{
2365 return active_cg_ops->unfreeze_fromhandler(handler);
2366}
9daf6f5d
SH
2367
2368bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices)
2369{
2370 return active_cg_ops->setup_limits(handler, with_devices);
2371}