cgroups: handle hybrid cgroup layouts

parent 04ad7ffe
...@@ -19,6 +19,7 @@ noinst_HEADERS = \ ...@@ -19,6 +19,7 @@ noinst_HEADERS = \
bdev/lxczfs.h \ bdev/lxczfs.h \
bdev/storage_utils.h \ bdev/storage_utils.h \
cgroups/cgroup.h \ cgroups/cgroup.h \
cgroups/cgroup_utils.h \
caps.h \ caps.h \
conf.h \ conf.h \
confile.h \ confile.h \
...@@ -90,6 +91,7 @@ liblxc_la_SOURCES = \ ...@@ -90,6 +91,7 @@ liblxc_la_SOURCES = \
bdev/storage_utils.c bdev/storage_utils.h \ bdev/storage_utils.c bdev/storage_utils.h \
cgroups/cgfs.c \ cgroups/cgfs.c \
cgroups/cgfsng.c \ cgroups/cgfsng.c \
cgroups/cgroup_utils.c cgroups/cgroup_utils.h \
cgroups/cgroup.c cgroups/cgroup.h \ cgroups/cgroup.c cgroups/cgroup.h \
commands.c commands.h \ commands.c commands.h \
commands_utils.c commands_utils.h \ commands_utils.c commands_utils.h \
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include "bdev.h" #include "bdev.h"
#include "cgroup.h" #include "cgroup.h"
#include "cgroup_utils.h"
#include "commands.h" #include "commands.h"
#include "log.h" #include "log.h"
#include "utils.h" #include "utils.h"
...@@ -72,6 +73,7 @@ struct hierarchy { ...@@ -72,6 +73,7 @@ struct hierarchy {
char *mountpoint; char *mountpoint;
char *base_cgroup; char *base_cgroup;
char *fullcgpath; char *fullcgpath;
bool is_cgroup_v2;
}; };
/* /*
...@@ -600,7 +602,8 @@ static bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname) ...@@ -600,7 +602,8 @@ static bool handle_cpuset_hierarchy(struct hierarchy *h, char *cgname)
} }
clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL); clonechildrenpath = must_make_path(cgpath, "cgroup.clone_children", NULL);
if (!file_exists(clonechildrenpath)) { /* unified hierarchy doesn't have clone_children */ /* unified hierarchy doesn't have clone_children */
if (!file_exists(clonechildrenpath)) {
free(clonechildrenpath); free(clonechildrenpath);
free(cgpath); free(cgpath);
return true; return true;
...@@ -742,10 +745,14 @@ static bool is_lxcfs(const char *line) ...@@ -742,10 +745,14 @@ static bool is_lxcfs(const char *line)
*/ */
static char **get_controllers(char **klist, char **nlist, char *line) static char **get_controllers(char **klist, char **nlist, char *line)
{ {
// the fourth field is /sys/fs/cgroup/comma-delimited-controller-list /* the fourth field is /sys/fs/cgroup/comma-delimited-controller-list */
int i; int i;
char *p = line, *p2, *tok, *saveptr = NULL; char *p = line, *p2, *tok, *saveptr = NULL;
char **aret = NULL; char **aret = NULL;
bool is_cgroup_v2;
/* handle cgroup v2 */
is_cgroup_v2 = is_cgroupfs_v2(line);
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
p = strchr(p, ' '); p = strchr(p, ' ');
...@@ -768,6 +775,13 @@ static char **get_controllers(char **klist, char **nlist, char *line) ...@@ -768,6 +775,13 @@ static char **get_controllers(char **klist, char **nlist, char *line)
return NULL; return NULL;
} }
*p2 = '\0'; *p2 = '\0';
/* cgroup v2 does not have separate mountpoints for controllers */
if (is_cgroup_v2) {
must_append_controller(klist, nlist, &aret, "cgroup2");
return aret;
}
for (tok = strtok_r(p, ",", &saveptr); tok; for (tok = strtok_r(p, ",", &saveptr); tok;
tok = strtok_r(NULL, ",", &saveptr)) { tok = strtok_r(NULL, ",", &saveptr)) {
must_append_controller(klist, nlist, &aret, tok); must_append_controller(klist, nlist, &aret, tok);
...@@ -776,15 +790,6 @@ static char **get_controllers(char **klist, char **nlist, char *line) ...@@ -776,15 +790,6 @@ static char **get_controllers(char **klist, char **nlist, char *line)
return aret; return aret;
} }
/* return true if the fstype is cgroup */
static bool is_cgroupfs(char *line)
{
char *p = strstr(line, " - ");
if (!p)
return false;
return strncmp(p, " - cgroup ", 10) == 0;
}
/* Add a controller to our list of hierarchies */ /* Add a controller to our list of hierarchies */
static void add_controller(char **clist, char *mountpoint, char *base_cgroup) static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
{ {
...@@ -797,6 +802,12 @@ static void add_controller(char **clist, char *mountpoint, char *base_cgroup) ...@@ -797,6 +802,12 @@ static void add_controller(char **clist, char *mountpoint, char *base_cgroup)
new->base_cgroup = base_cgroup; new->base_cgroup = base_cgroup;
new->fullcgpath = NULL; new->fullcgpath = NULL;
/* record if this is the cgroup v2 hierarchy */
if (!strcmp(base_cgroup, "cgroup2"))
new->is_cgroup_v2 = true;
else
new->is_cgroup_v2 = false;
newentry = append_null_to_list((void ***)&hierarchies); newentry = append_null_to_list((void ***)&hierarchies);
hierarchies[newentry] = new; hierarchies[newentry] = new;
} }
...@@ -878,13 +889,21 @@ static bool controller_in_clist(char *cgline, char *c) ...@@ -878,13 +889,21 @@ static bool controller_in_clist(char *cgline, char *c)
static char *get_current_cgroup(char *basecginfo, char *controller) static char *get_current_cgroup(char *basecginfo, char *controller)
{ {
char *p = basecginfo; char *p = basecginfo;
bool is_cgroup_v2;
bool is_cgroup_v2_base_cgroup;
is_cgroup_v2 = !strcmp(controller, "cgroup2");
while (true) {
is_cgroup_v2_base_cgroup = false;
/* cgroup v2 entry in "/proc/<pid>/cgroup": "0::/some/path" */
if (is_cgroup_v2 && (*p == '0'))
is_cgroup_v2_base_cgroup = true;
while (1) {
p = strchr(p, ':'); p = strchr(p, ':');
if (!p) if (!p)
return NULL; return NULL;
p++; p++;
if (controller_in_clist(p, controller)) { if (is_cgroup_v2_base_cgroup || controller_in_clist(p, controller)) {
p = strchr(p, ':'); p = strchr(p, ':');
if (!p) if (!p)
return NULL; return NULL;
...@@ -899,20 +918,6 @@ static char *get_current_cgroup(char *basecginfo, char *controller) ...@@ -899,20 +918,6 @@ static char *get_current_cgroup(char *basecginfo, char *controller)
} }
} }
/*
* Given a hierarchy @mountpoint and base @path, verify that we can create
* directories underneath it.
*/
static bool test_writeable(char *mountpoint, char *path)
{
char *fullpath = must_make_path(mountpoint, path, NULL);
int ret;
ret = access(fullpath, W_OK);
free(fullpath);
return ret == 0;
}
static void must_append_string(char ***list, char *entry) static void must_append_string(char ***list, char *entry)
{ {
int newentry = append_null_to_list((void ***)list); int newentry = append_null_to_list((void ***)list);
...@@ -941,16 +946,17 @@ static void get_existing_subsystems(char ***klist, char ***nlist) ...@@ -941,16 +946,17 @@ static void get_existing_subsystems(char ***klist, char ***nlist)
continue; continue;
*p2 = '\0'; *p2 = '\0';
/* If we have a mixture between cgroup v1 and cgroup v2 /* If the kernel has cgroup v2 support, then /proc/self/cgroup
* hierarchies, then /proc/self/cgroup contains entries of the * contains an entry of the form:
* form:
* *
* 0::/some/path * 0::/some/path
* *
* We need to skip those. * In this case we use "cgroup2" as controller name.
*/ */
if ((p2 - p) == 0) if ((p2 - p) == 0) {
must_append_string(klist, "cgroup2");
continue; continue;
}
for (tok = strtok_r(p, ",", &saveptr); tok; for (tok = strtok_r(p, ",", &saveptr); tok;
tok = strtok_r(NULL, ",", &saveptr)) { tok = strtok_r(NULL, ",", &saveptr)) {
...@@ -1058,8 +1064,10 @@ static bool parse_hierarchies(void) ...@@ -1058,8 +1064,10 @@ static bool parse_hierarchies(void)
while (getline(&line, &len, f) != -1) { while (getline(&line, &len, f) != -1) {
char **controller_list = NULL; char **controller_list = NULL;
char *mountpoint, *base_cgroup; char *mountpoint, *base_cgroup;
bool is_cgroup_v2, writeable;
if (!is_lxcfs(line) && !is_cgroupfs(line)) is_cgroup_v2 = is_cgroupfs_v2(line);
if (!is_lxcfs(line) && !is_cgroupfs_v1(line) && !is_cgroup_v2)
continue; continue;
controller_list = get_controllers(klist, nlist, line); controller_list = get_controllers(klist, nlist, line);
...@@ -1085,9 +1093,14 @@ static bool parse_hierarchies(void) ...@@ -1085,9 +1093,14 @@ static bool parse_hierarchies(void)
free(mountpoint); free(mountpoint);
continue; continue;
} }
trim(base_cgroup); trim(base_cgroup);
prune_init_scope(base_cgroup); prune_init_scope(base_cgroup);
if (!test_writeable(mountpoint, base_cgroup)) { if (is_cgroup_v2)
writeable = test_writeable_v2(mountpoint, base_cgroup);
else
writeable = test_writeable_v1(mountpoint, base_cgroup);
if (!writeable) {
free_string_list(controller_list); free_string_list(controller_list);
free(mountpoint); free(mountpoint);
free(base_cgroup); free(base_cgroup);
......
/*
* lxc: linux Container library
*
* Copyright © 2017 Canonical Ltd.
*
* Authors:
* Serge Hallyn <serge.hallyn@ubuntu.com>
* Christian Brauner <christian.brauner@ubuntu.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "cgroup_utils.h"
#include "utils.h"
bool is_cgroupfs_v1(char *line)
{
char *p = strstr(line, " - ");
if (!p)
return false;
return strncmp(p, " - cgroup ", 10) == 0;
}
bool is_cgroupfs_v2(char *line)
{
char *p = strstr(line, " - ");
if (!p)
return false;
return strncmp(p, " - cgroup2 ", 11) == 0;
}
bool test_writeable_v1(char *mountpoint, char *path)
{
char *fullpath = must_make_path(mountpoint, path, NULL);
int ret;
ret = access(fullpath, W_OK);
free(fullpath);
return ret == 0;
}
bool test_writeable_v2(char *mountpoint, char *path)
{
/* In order to move ourselves into an appropriate sub-cgroup we need to
* have write access to the parent cgroup's "cgroup.procs" file, i.e. we
* need to have write access to the our current cgroups's "cgroup.procs"
* file.
*/
int ret;
char *cgroup_path, *cgroup_procs_file;
cgroup_path = must_make_path(mountpoint, path, NULL);
cgroup_procs_file = must_make_path(cgroup_path, "cgroup.procs", NULL);
ret = access(cgroup_path, W_OK);
free(cgroup_path);
if (ret < 0) {
free(cgroup_procs_file);
return false;
}
ret = access(cgroup_procs_file, W_OK);
free(cgroup_procs_file);
return ret == 0;
}
/*
* lxc: linux Container library
*
* Copyright © 2017 Canonical Ltd.
*
* Authors:
* Serge Hallyn <serge.hallyn@ubuntu.com>
* Christian Brauner <christian.brauner@ubuntu.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_CGROUP_UTILS_H
#define __LXC_CGROUP_UTILS_H
#include <stdbool.h>
#include <stdio.h>
/* Check if given entry from /proc/<pid>/mountinfo is a cgroupfs v1 mount. */
extern bool is_cgroupfs_v1(char *line);
/* Check if given entry from /proc/<pid>/mountinfo is a cgroupfs v2 mount. */
extern bool is_cgroupfs_v2(char *line);
/* Given a v1 hierarchy @mountpoint and base @path, verify that we can create
* directories underneath it.
*/
extern bool test_writeable_v1(char *mountpoint, char *path);
/* Given a v2 hierarchy @mountpoint and base @path, verify that we can create
* directories underneath it and that we have write access to the cgroup's
* "cgroup.procs" file.
*/
extern bool test_writeable_v2(char *mountpoint, char *path);
#endif /* __LXC_CGROUP_UTILS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment