libcgroup/libcgroup-0.41/src/config.c
2014-02-05 16:04:04 +01:00

1688 lines
39 KiB
C

/*
* Copyright IBM Corporation. 2007
*
* Authors: Balbir Singh <balbir@linux.vnet.ibm.com>
* Dhaval Giani <dhaval@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* TODOs:
* 1. Implement our own hashing scheme
*
* Code initiated and designed by Balbir Singh. All faults are most likely
* his mistake.
*
* Cleanup and changes to use the "official" structures and functions made
* by Dhaval Giani. All faults will still be Balbir's mistake :)
*/
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <grp.h>
#include <libcgroup.h>
#include <libcgroup-internal.h>
#include <limits.h>
#include <pwd.h>
#include <pthread.h>
#include <search.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
unsigned int MAX_CGROUPS = 64; /* NOTE: This value changes dynamically */
unsigned int MAX_TEMPLATES = 64;
/* NOTE: This value changes dynamically */
enum tc_switch_t {
CGROUP,
TEMPLATE,
};
extern FILE *yyin;
extern int yyparse(void);
static struct cgroup default_group;
static int default_group_set = 0;
/*
* The basic global data structures.
*
* config_mount_table -> Where what controller is mounted
* table_index -> Where in the table are we.
* config_table_lock -> Serializing access to config_mount_table.
* cgroup_table -> Which cgroups have to be created.
* cgroup_table_index -> Where in the cgroup_table we are.
*/
static struct cg_mount_table_s config_mount_table[CG_CONTROLLER_MAX];
static struct cg_mount_table_s config_namespace_table[CG_CONTROLLER_MAX];
static int config_table_index;
static int namespace_table_index;
static pthread_rwlock_t config_table_lock = PTHREAD_RWLOCK_INITIALIZER;
static pthread_rwlock_t namespace_table_lock = PTHREAD_RWLOCK_INITIALIZER;
static struct cgroup *config_cgroup_table;
static int cgroup_table_index;
/*
* template structures filled by cgroup_parse_config when the configuration
* file is parsing (analogous to config_cgroup_table and cgroup_table_index
* for cgroups)
*/
static struct cgroup *config_template_table;
static int config_template_table_index;
/*
* template structures used for templates cache, config_template_table and
* cgroup_template_table_index are rewritten in each cgroup_parse_config
* thus not only if we want to reload template cache
*/
static struct cgroup *template_table;
static int template_table_index;
/*
* Needed for the type while mounting cgroupfs.
*/
#define CGROUP_FILESYSTEM "cgroup"
/*
* NOTE: All these functions return 1 on success
* and not 0 as is the library convention
*/
/*
* This call just sets the name of the cgroup. It will
* always be called in the end, because the parser will
* work bottom up. It works for cgroup and templates tables
* based on flag variable:
* CGROUP ... cgroup
* TEMPLATE ... template
*/
int config_insert_cgroup(char *cg_name, int flag)
{
struct cgroup *config_cgroup;
struct cgroup *config_table;
unsigned int *max;
int *table_index;
switch (flag) {
case CGROUP:
table_index = &cgroup_table_index;
config_table = config_cgroup_table;
max = &MAX_CGROUPS;
break;
case TEMPLATE:
table_index = &config_template_table_index;
config_table = config_template_table;
max = &MAX_TEMPLATES;
break;
default:
return 0;
}
if (*table_index >= *max - 1) {
struct cgroup *newblk;
unsigned int oldlen;
if (*max >= INT_MAX) {
last_errno = ENOMEM;
return 0;
}
oldlen = *max;
*max *= 2;
newblk = realloc(config_table, (*max * sizeof(struct cgroup)));
if (!newblk) {
last_errno = ENOMEM;
return 0;
}
memset(newblk + oldlen, 0, (*max - oldlen) *
sizeof(struct cgroup));
init_cgroup_table(newblk + oldlen, *max - oldlen);
config_table = newblk;
switch (flag) {
case CGROUP:
config_cgroup_table = config_table;
break;
case TEMPLATE:
config_template_table = config_table;
break;
default:
return 0;
}
cgroup_dbg("maximum %d\n", *max);
cgroup_dbg("reallocated config_table to %p\n",
config_table);
}
config_cgroup = &config_table[*table_index];
strncpy(config_cgroup->name, cg_name, FILENAME_MAX);
/*
* Since this will be the last part to be parsed.
*/
*table_index = *table_index + 1;
free(cg_name);
return 1;
}
/*
* This call just sets the name of the cgroup. It will
* always be called in the end, because the parser will
* work bottom up.
*/
int cgroup_config_insert_cgroup(char *cg_name)
{
int ret;
ret = config_insert_cgroup(cg_name, CGROUP);
return ret;
}
/*
* This call just sets the name of the template. It will
* always be called in the end, because the parser will
* work bottom up.
*/
int template_config_insert_cgroup(char *cg_name)
{
int ret;
ret = config_insert_cgroup(cg_name, TEMPLATE);
return ret;
}
/*
* This function sets the various controller's control
* files. It will always append values for config_cgroup/template_table_index
* entry in the config_cgroup/template_table. The index is incremented in
* cgroup/template_config_insert_cgroup.
* flag variable switch between cgroup/templates variables:
* CGROUP ... cgroup
* TEMPLATE ... template
*/
int config_parse_controller_options(char *controller,
struct cgroup_dictionary *values, int flag)
{
const char *name, *value;
struct cgroup_controller *cgc;
int error;
struct cgroup *config_cgroup;
void *iter = NULL;
int *table_index;
switch (flag) {
case CGROUP:
table_index = &cgroup_table_index;
config_cgroup =
&config_cgroup_table[*table_index];
break;
case TEMPLATE:
table_index = &config_template_table_index;
config_cgroup =
&config_template_table[*table_index];
break;
default:
return 0;
}
cgroup_dbg("Adding controller %s\n", controller);
cgc = cgroup_add_controller(config_cgroup, controller);
if (!cgc)
goto parse_error;
/*
* Did we just specify the controller to create the correct
* set of directories, without setting any values?
*/
if (!values)
goto done;
error = cgroup_dictionary_iterator_begin(values, &iter, &name, &value);
while (error == 0) {
cgroup_dbg("[1] name value pair being processed is %s=%s\n",
name, value);
if (!name)
goto parse_error;
error = cgroup_add_value_string(cgc, name, value);
if (error)
goto parse_error;
error = cgroup_dictionary_iterator_next(&iter, &name, &value);
}
cgroup_dictionary_iterator_end(&iter);
iter = NULL;
if (error != ECGEOF)
goto parse_error;
done:
free(controller);
return 1;
parse_error:
free(controller);
cgroup_dictionary_iterator_end(&iter);
cgroup_delete_cgroup(config_cgroup, 1);
*table_index = *table_index - 1;
return 0;
}
/* This function sets the various controller's control
* files. It will always append values for cgroup_table_index
* entry in the cgroup_table. The index is incremented in
* cgroup_config_insert_cgroup
*/
int cgroup_config_parse_controller_options(char *controller,
struct cgroup_dictionary *values)
{
int ret;
ret = config_parse_controller_options(controller, values, CGROUP);
return ret;
}
/* This function sets the various controller's control
* files. It will always append values for config_template_table_index
* entry in the config_template_table. The index is incremented in
* template_config_insert_cgroup
*/
int template_config_parse_controller_options(char *controller,
struct cgroup_dictionary *values)
{
int ret;
ret = config_parse_controller_options(controller, values, TEMPLATE);
return ret;
}
/*
* Sets the tasks file's uid and gid for cgroup and templates tables
* based on flag variable:
* CGROUP ... cgroup
* TEMPLATE ... template
*/
int config_group_task_perm(char *perm_type, char *value, int flag)
{
struct passwd *pw, *pw_buffer;
struct group *group, *group_buffer;
long val = atoi(value);
char buffer[CGROUP_BUFFER_LEN];
struct cgroup *config_cgroup;
int table_index, ret;
switch (flag) {
case CGROUP:
table_index = cgroup_table_index;
config_cgroup =
&config_cgroup_table[table_index];
break;
case TEMPLATE:
table_index = config_template_table_index;
config_cgroup =
&config_template_table[table_index];
break;
default:
return 0;
}
if (!strcmp(perm_type, "uid")) {
if (!val) {
pw = (struct passwd *) malloc(sizeof(struct passwd));
if (!pw)
goto group_task_error;
getpwnam_r(value, pw, buffer, CGROUP_BUFFER_LEN,
&pw_buffer);
if (pw_buffer == NULL) {
free(pw);
goto group_task_error;
}
val = pw->pw_uid;
free(pw);
}
config_cgroup->tasks_uid = val;
}
if (!strcmp(perm_type, "gid")) {
if (!val) {
group = (struct group *) malloc(sizeof(struct group));
if (!group)
goto group_task_error;
ret = getgrnam_r(value, group, buffer,
CGROUP_BUFFER_LEN, &group_buffer);
if (ret != 0 || group_buffer == NULL) {
free(group);
goto group_task_error;
}
val = group->gr_gid;
free(group);
}
config_cgroup->tasks_gid = val;
}
if (!strcmp(perm_type, "fperm")) {
char *endptr;
val = strtol(value, &endptr, 8);
if (*endptr)
goto group_task_error;
config_cgroup->task_fperm = val;
}
free(perm_type);
free(value);
return 1;
group_task_error:
free(perm_type);
free(value);
cgroup_delete_cgroup(config_cgroup, 1);
table_index--;
return 0;
}
/*
* Sets the tasks file's uid and gid
*/
int cgroup_config_group_task_perm(char *perm_type, char *value)
{
int ret;
ret = config_group_task_perm(perm_type, value, CGROUP);
return ret;
}
/*
* Sets the tasks file's uid and gid for templates
*/
int template_config_group_task_perm(char *perm_type, char *value)
{
int ret;
ret = config_group_task_perm(perm_type, value, TEMPLATE);
return ret;
}
/*
* Sets the admin file's uid and gid for cgroup and templates tables
* based on flag variable:
* CGROUP ... cgroup
* TEMPLATE ... templates
*/
int config_group_admin_perm(char *perm_type, char *value, int flag)
{
struct passwd *pw, *pw_buffer;
struct group *group, *group_buffer;
struct cgroup *config_cgroup;
long val = atoi(value);
char buffer[CGROUP_BUFFER_LEN];
int table_index, ret;
switch (flag) {
case CGROUP:
table_index = cgroup_table_index;
config_cgroup = &config_cgroup_table[table_index];
break;
case TEMPLATE:
table_index = config_template_table_index;
config_cgroup = &config_template_table[table_index];
break;
default:
return 0;
}
if (!strcmp(perm_type, "uid")) {
if (!val) {
pw = (struct passwd *) malloc(sizeof(struct passwd));
if (!pw)
goto admin_error;
getpwnam_r(value, pw, buffer, CGROUP_BUFFER_LEN,
&pw_buffer);
if (pw_buffer == NULL) {
free(pw);
goto admin_error;
}
val = pw->pw_uid;
free(pw);
}
config_cgroup->control_uid = val;
}
if (!strcmp(perm_type, "gid")) {
if (!val) {
group = (struct group *) malloc(sizeof(struct group));
if (!group)
goto admin_error;
ret = getgrnam_r(value, group, buffer,
CGROUP_BUFFER_LEN, &group_buffer);
if (ret != 0 || group_buffer == NULL) {
free(group);
goto admin_error;
}
val = group->gr_gid;
free(group);
}
config_cgroup->control_gid = val;
}
if (!strcmp(perm_type, "fperm")) {
char *endptr;
val = strtol(value, &endptr, 8);
if (*endptr)
goto admin_error;
config_cgroup->control_fperm = val;
}
if (!strcmp(perm_type, "dperm")) {
char *endptr;
val = strtol(value, &endptr, 8);
if (*endptr)
goto admin_error;
config_cgroup->control_dperm = val;
}
free(perm_type);
free(value);
return 1;
admin_error:
free(perm_type);
free(value);
cgroup_delete_cgroup(config_cgroup, 1);
table_index--;
return 0;
}
/*
* Set the control file's uid and gid
*/
int cgroup_config_group_admin_perm(char *perm_type, char *value)
{
int ret;
ret = config_group_admin_perm(perm_type, value, CGROUP);
return ret;
}
/*
* Set the control file's uid and gid for templates
*/
int template_config_group_admin_perm(char *perm_type, char *value)
{
int ret;
ret = config_group_admin_perm(perm_type, value, TEMPLATE);
return ret;
}
/*
* The moment we have found the controller's information
* insert it into the config_mount_table.
*/
int cgroup_config_insert_into_mount_table(char *name, char *mount_point)
{
int i;
if (config_table_index >= CG_CONTROLLER_MAX)
return 0;
pthread_rwlock_wrlock(&config_table_lock);
/*
* Merge controller names with the same mount point
*/
for (i = 0; i < config_table_index; i++) {
if (strcmp(config_mount_table[i].mount.path,
mount_point) == 0) {
char *cname = config_mount_table[i].name;
strncat(cname, ",", FILENAME_MAX - strlen(cname) - 1);
strncat(cname, name,
FILENAME_MAX - strlen(cname) - 1);
goto done;
}
}
strcpy(config_mount_table[config_table_index].name, name);
strcpy(config_mount_table[config_table_index].mount.path, mount_point);
config_mount_table[config_table_index].mount.next = NULL;
config_table_index++;
done:
pthread_rwlock_unlock(&config_table_lock);
free(name);
free(mount_point);
return 1;
}
/*
* Cleanup all the data from the config_mount_table
*/
void cgroup_config_cleanup_mount_table(void)
{
memset(&config_mount_table, 0,
sizeof(struct cg_mount_table_s) * CG_CONTROLLER_MAX);
}
/*
* The moment we have found the controller's information
* insert it into the config_mount_table.
*/
int cgroup_config_insert_into_namespace_table(char *name, char *nspath)
{
if (namespace_table_index >= CG_CONTROLLER_MAX)
return 0;
pthread_rwlock_wrlock(&namespace_table_lock);
strcpy(config_namespace_table[namespace_table_index].name, name);
strcpy(config_namespace_table[namespace_table_index].mount.path,
nspath);
config_namespace_table[namespace_table_index].mount.next = NULL;
namespace_table_index++;
pthread_rwlock_unlock(&namespace_table_lock);
free(name);
free(nspath);
return 1;
}
/*
* Cleanup all the data from the config_mount_table
*/
void cgroup_config_cleanup_namespace_table(void)
{
memset(&config_namespace_table, 0,
sizeof(struct cg_mount_table_s) * CG_CONTROLLER_MAX);
}
/**
* Add necessary options for mount. Currently only 'none' option is added
* for mounts with only 'name=xxx' and without real controller.
*/
static int cgroup_config_ajdust_mount_options(struct cg_mount_table_s *mount)
{
char *save = NULL;
char *opts = strdup(mount->name);
char *token;
int name_only = 1;
if (opts == NULL)
return ECGFAIL;
token = strtok_r(opts, ",", &save);
while (token != NULL) {
if (strncmp(token, "name=", 5) != 0) {
name_only = 0;
break;
}
token = strtok_r(NULL, ",", &save);
}
free(opts);
if (name_only) {
strncat(mount->name, ",", FILENAME_MAX - strlen(mount->name)-1);
strncat(mount->name, "none",
FILENAME_MAX - strlen(mount->name) - 1);
}
return 0;
}
/*
* Start mounting the mount table.
*/
static int cgroup_config_mount_fs(void)
{
int ret;
struct stat buff;
int i;
int error;
for (i = 0; i < config_table_index; i++) {
struct cg_mount_table_s *curr = &(config_mount_table[i]);
ret = stat(curr->mount.path, &buff);
if (ret < 0 && errno != ENOENT) {
cgroup_err("Error: cannot access %s: %s\n",
curr->mount.path, strerror(errno));
last_errno = errno;
error = ECGOTHER;
goto out_err;
}
if (errno == ENOENT) {
ret = cg_mkdir_p(curr->mount.path);
if (ret) {
cgroup_err("Error: cannot create directory %s\n",
curr->mount.path);
error = ret;
goto out_err;
}
} else if (!S_ISDIR(buff.st_mode)) {
cgroup_err("Error: %s already exists but it is not a directory\n",
curr->mount.path);
errno = ENOTDIR;
last_errno = errno;
error = ECGOTHER;
goto out_err;
}
error = cgroup_config_ajdust_mount_options(curr);
if (error)
goto out_err;
ret = mount(CGROUP_FILESYSTEM, curr->mount.path,
CGROUP_FILESYSTEM, 0, curr->name);
if (ret < 0) {
cgroup_err("Error: cannot mount %s to %s: %s\n",
curr->name, curr->mount.path,
strerror(errno));
error = ECGMOUNTFAIL;
goto out_err;
}
}
return 0;
out_err:
/*
* If we come here, we have failed. Since we have touched only
* mountpoints prior to i, we shall operate on only them now.
*/
config_table_index = i;
return error;
}
/*
* Actually create the groups once the parsing has been finished.
*/
static int cgroup_config_create_groups(void)
{
int error = 0;
int i;
for (i = 0; i < cgroup_table_index; i++) {
struct cgroup *cgroup = &config_cgroup_table[i];
error = cgroup_create_cgroup(cgroup, 0);
cgroup_dbg("creating group %s, error %d\n", cgroup->name,
error);
if (error)
return error;
}
return error;
}
/*
* Destroy the cgroups
*/
static int cgroup_config_destroy_groups(void)
{
int error = 0, ret = 0;
int i;
for (i = 0; i < cgroup_table_index; i++) {
struct cgroup *cgroup = &config_cgroup_table[i];
error = cgroup_delete_cgroup_ext(cgroup,
CGFLAG_DELETE_RECURSIVE
| CGFLAG_DELETE_IGNORE_MIGRATION);
if (error) {
/* store the error, but continue deleting the rest */
ret = error;
}
}
return ret;
}
/*
* Unmount the controllers
*/
static int cgroup_config_unmount_controllers(void)
{
int i;
int error;
for (i = 0; i < config_table_index; i++) {
/*
* We ignore failures and ensure that all mounted
* containers are unmounted
*/
error = umount(config_mount_table[i].mount.path);
if (error < 0)
cgroup_dbg("Unmount failed\n");
error = rmdir(config_mount_table[i].mount.path);
if (error < 0)
cgroup_dbg("rmdir failed\n");
}
return 0;
}
static int config_validate_namespaces(void)
{
int i;
char *namespace = NULL;
char *mount_path = NULL;
int j, subsys_count;
int error = 0;
pthread_rwlock_wrlock(&cg_mount_table_lock);
for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
/*
* If we get the path in the first run, then we
* are good, else we will need to go for two
* loops. This should be optimized in the future
*/
mount_path = cg_mount_table[i].mount.path;
if (!mount_path) {
last_errno = errno;
error = ECGOTHER;
goto out_error;
}
/*
* Setup the namespace for the subsystems having the same
* mount point.
*/
if (!cg_namespace_table[i]) {
namespace = NULL;
} else {
namespace = cg_namespace_table[i];
if (!namespace) {
last_errno = errno;
error = ECGOTHER;
goto out_error;
}
}
/*
* We want to handle all the subsytems that are mounted
* together. So initialize j to start from the next point in
* the mount table.
*/
j = i + 1;
/*
* Search through the mount table to locate which subsystems
* are mounted together.
*/
while (!strncmp(cg_mount_table[j].mount.path, mount_path,
FILENAME_MAX)) {
if (!namespace && cg_namespace_table[j]) {
/* In case namespace is not setup, set it up */
namespace = cg_namespace_table[j];
if (!namespace) {
last_errno = errno;
error = ECGOTHER;
goto out_error;
}
}
j++;
}
subsys_count = j;
/*
* If there is no namespace, then continue on :)
*/
if (!namespace) {
i = subsys_count - 1;
continue;
}
/*
* Validate/setup the namespace
* If no namespace is specified, copy the namespace we have
* stored. If a namespace is specified, confirm if it is
* the same as we have stored. If not, we fail.
*/
for (j = i; j < subsys_count; j++) {
if (!cg_namespace_table[j]) {
cg_namespace_table[j] = strdup(namespace);
if (!cg_namespace_table[j]) {
last_errno = errno;
error = ECGOTHER;
goto out_error;
}
} else if (strcmp(namespace, cg_namespace_table[j])) {
error = ECGNAMESPACEPATHS;
goto out_error;
}
}
/* i++ in the for loop will increment it */
i = subsys_count - 1;
}
out_error:
pthread_rwlock_unlock(&cg_mount_table_lock);
return error;
}
/*
* Should always be called after cgroup_init() has been called
*
* NOT to be called outside the library. Is handled internally
* when we are looking to load namespace configurations.
*
* This function will order the namespace table in the same
* fashion as how the mou table is setup.
*
* Also it will setup namespaces for all the controllers mounted.
* In case a controller does not have a namespace assigned to it, it
* will set it to null.
*/
static int config_order_namespace_table(void)
{
int i = 0;
int error = 0;
pthread_rwlock_wrlock(&cg_mount_table_lock);
/*
* Set everything to NULL
*/
for (i = 0; i < CG_CONTROLLER_MAX; i++)
cg_namespace_table[i] = NULL;
memset(cg_namespace_table, 0,
CG_CONTROLLER_MAX * sizeof(cg_namespace_table[0]));
/*
* Now fill up the namespace table looking at the table we have
* otherwise.
*/
for (i = 0; i < namespace_table_index; i++) {
int j;
int flag = 0;
for (j = 0; cg_mount_table[j].name[0] != '\0'; j++) {
if (strncmp(config_namespace_table[i].name,
cg_mount_table[j].name, FILENAME_MAX) == 0) {
flag = 1;
if (cg_namespace_table[j]) {
error = ECGNAMESPACEPATHS;
goto error_out;
}
cg_namespace_table[j] = strdup(
config_namespace_table[i].mount.path);
if (!cg_namespace_table[j]) {
last_errno = errno;
error = ECGOTHER;
goto error_out;
}
}
}
if (!flag)
return ECGNAMESPACECONTROLLER;
}
error_out:
pthread_rwlock_unlock(&cg_mount_table_lock);
return error;
}
/**
* Free all memory allocated during cgroup_parse_config(), namely
* config_cgroup_table and config_template_table.
*/
static void cgroup_free_config(void)
{
int i;
if (config_cgroup_table) {
for (i = 0; i < cgroup_table_index; i++)
cgroup_free_controllers(
&config_cgroup_table[i]);
free(config_cgroup_table);
config_cgroup_table = NULL;
}
config_table_index = 0;
if (config_template_table) {
for (i = 0; i < config_template_table_index; i++)
cgroup_free_controllers(
&config_template_table[i]);
free(config_template_table);
config_template_table = NULL;
}
config_template_table_index = 0;
}
/**
* Applies default permissions/uid/gid to all groups in config file.
*/
static void cgroup_config_apply_default()
{
int i;
if (config_cgroup_table) {
for (i = 0; i < cgroup_table_index; i++) {
struct cgroup *c = &config_cgroup_table[i];
if (c->control_dperm == NO_PERMS)
c->control_dperm = default_group.control_dperm;
if (c->control_fperm == NO_PERMS)
c->control_fperm = default_group.control_fperm;
if (c->control_gid == NO_UID_GID)
c->control_gid = default_group.control_gid;
if (c->control_uid == NO_UID_GID)
c->control_uid = default_group.control_uid;
if (c->task_fperm == NO_PERMS)
c->task_fperm = default_group.task_fperm;
if (c->tasks_gid == NO_UID_GID)
c->tasks_gid = default_group.tasks_gid;
if (c->tasks_uid == NO_UID_GID)
c->tasks_uid = default_group.tasks_uid;
}
}
}
static int cgroup_parse_config(const char *pathname)
{
int ret;
yyin = fopen(pathname, "re");
if (!yyin) {
cgroup_err("Error: failed to open file %s\n", pathname);
last_errno = errno;
return ECGOTHER;
}
config_cgroup_table = calloc(MAX_CGROUPS, sizeof(struct cgroup));
if (!config_cgroup_table) {
ret = ECGFAIL;
goto err;
}
config_template_table = calloc(MAX_TEMPLATES, sizeof(struct cgroup));
if (!config_template_table) {
ret = ECGFAIL;
goto err;
}
/* Clear all internal variables so this function can be called twice. */
init_cgroup_table(config_cgroup_table, MAX_CGROUPS);
init_cgroup_table(config_template_table, MAX_TEMPLATES);
memset(config_namespace_table, 0, sizeof(config_namespace_table));
memset(config_mount_table, 0, sizeof(config_mount_table));
config_table_index = 0;
namespace_table_index = 0;
cgroup_table_index = 0;
config_template_table_index = 0;
if (!default_group_set) {
/* init the default cgroup */
init_cgroup_table(&default_group, 1);
}
/*
* Parser calls longjmp() on really fatal error (like out-of-memory).
*/
ret = setjmp(parser_error_env);
if (!ret)
ret = yyparse();
if (ret) {
/*
* Either yyparse failed or longjmp() was called.
*/
cgroup_err("Error: failed to parse file %s\n", pathname);
ret = ECGCONFIGPARSEFAIL;
goto err;
}
err:
if (yyin)
fclose(yyin);
if (ret) {
cgroup_free_config();
}
return ret;
}
int _cgroup_config_compare_groups(const void *p1, const void *p2)
{
const struct cgroup *g1 = p1;
const struct cgroup *g2 = p2;
return strcmp(g1->name, g2->name);
}
static void cgroup_config_sort_groups()
{
qsort(config_cgroup_table, cgroup_table_index, sizeof(struct cgroup),
_cgroup_config_compare_groups);
}
/*
* The main function which does all the setup of the data structures
* and finally creates the cgroups
*/
int cgroup_config_load_config(const char *pathname)
{
int error;
int namespace_enabled = 0;
int mount_enabled = 0;
int ret;
ret = cgroup_parse_config(pathname);
if (ret != 0)
return ret;
namespace_enabled = (config_namespace_table[0].name[0] != '\0');
mount_enabled = (config_mount_table[0].name[0] != '\0');
/*
* The configuration should have namespace or mount, not both.
*/
if (namespace_enabled && mount_enabled) {
free(config_cgroup_table);
return ECGMOUNTNAMESPACE;
}
error = cgroup_config_mount_fs();
if (error)
goto err_mnt;
error = cgroup_init();
if (error == ECGROUPNOTMOUNTED && cgroup_table_index == 0
&& config_template_table_index == 0) {
/*
* The config file seems to be empty.
*/
error = 0;
goto err_mnt;
}
if (error)
goto err_mnt;
/*
* The very first thing is to sort the namespace table. If we fail
* we unmount everything and get out.
*/
error = config_order_namespace_table();
if (error)
goto err_mnt;
error = config_validate_namespaces();
if (error)
goto err_mnt;
cgroup_config_apply_default();
error = cgroup_config_create_groups();
cgroup_dbg("creating all cgroups now, error=%d\n", error);
if (error)
goto err_grp;
cgroup_free_config();
return 0;
err_grp:
cgroup_config_destroy_groups();
err_mnt:
cgroup_config_unmount_controllers();
cgroup_free_config();
return error;
}
/* unmounts given mount, but only if it is empty */
static int cgroup_config_try_unmount(struct cg_mount_table_s *mount_info)
{
char *controller, *controller_list;
struct cg_mount_point *mount = &(mount_info->mount);
void *handle = NULL;
int ret, lvl;
struct cgroup_file_info info;
char *saveptr = NULL;
/* parse the first controller name from list of controllers */
controller_list = strdup(mount_info->name);
if (!controller_list) {
last_errno = errno;
return ECGOTHER;
}
controller = strtok_r(controller_list, ",", &saveptr);
if (!controller) {
free(controller_list);
return ECGINVAL;
}
/* check if the hierarchy is empty */
ret = cgroup_walk_tree_begin(controller, "/", 0, &handle, &info, &lvl);
free(controller_list);
if (ret == ECGCONTROLLEREXISTS)
return 0;
if (ret)
return ret;
/* skip the first found directory, it's '/' */
ret = cgroup_walk_tree_next(0, &handle, &info, lvl);
/* find any other subdirectory */
while (ret == 0) {
if (info.type == CGROUP_FILE_TYPE_DIR)
break;
ret = cgroup_walk_tree_next(0, &handle, &info, lvl);
}
cgroup_walk_tree_end(&handle);
if (ret == 0) {
cgroup_dbg("won't unmount %s: hieararchy is not empty\n",
mount_info->name);
return 0; /* the hieararchy is not empty */
}
if (ret != ECGEOF)
return ret;
/*
* ret must be ECGEOF now = there is only root group in the hierarchy
* -> unmount all mount points.
*/
ret = 0;
while (mount) {
int err;
cgroup_dbg("unmounting %s at %s\n", mount_info->name,
mount->path);
err = umount(mount->path);
if (err && !ret) {
ret = ECGOTHER;
last_errno = errno;
}
mount = mount->next;
}
return ret;
}
int cgroup_config_unload_config(const char *pathname, int flags)
{
int ret, i, error;
int namespace_enabled = 0;
int mount_enabled = 0;
cgroup_dbg("cgroup_config_unload_config: parsing %s\n", pathname);
ret = cgroup_parse_config(pathname);
if (ret)
goto err;
namespace_enabled = (config_namespace_table[0].name[0] != '\0');
mount_enabled = (config_mount_table[0].name[0] != '\0');
/*
* The configuration should have namespace or mount, not both.
*/
if (namespace_enabled && mount_enabled) {
free(config_cgroup_table);
return ECGMOUNTNAMESPACE;
}
ret = config_order_namespace_table();
if (ret)
goto err;
ret = config_validate_namespaces();
if (ret)
goto err;
/*
* Delete the groups in reverse order, i.e. subgroups first, then
* parents.
*/
cgroup_config_sort_groups();
for (i = cgroup_table_index-1; i >= 0; i--) {
struct cgroup *cgroup = &config_cgroup_table[i];
cgroup_dbg("removing %s\n", pathname);
error = cgroup_delete_cgroup_ext(cgroup, flags);
if (error && !ret) {
/* store the error, but continue deleting the rest */
ret = error;
}
}
/* Delete templates */
for (i = 0; i < config_template_table_index; i++) {
struct cgroup *cgroup = &config_template_table[i];
cgroup_dbg("removing %s\n", pathname);
error = cgroup_delete_cgroup_ext(cgroup, flags);
if (error && !ret) {
/* store the error, but continue deleting the rest */
ret = error;
}
}
config_template_table_index = 0;
if (mount_enabled) {
for (i = 0; i < config_table_index; i++) {
struct cg_mount_table_s *m = &(config_mount_table[i]);
cgroup_dbg("unmounting %s\n", m->name);
error = cgroup_config_try_unmount(m);
if (error && !ret)
ret = error;
}
}
err:
cgroup_free_config();
return ret;
}
static int cgroup_config_unload_controller(const struct cgroup_mount_point *mount_info)
{
int ret, error;
struct cgroup *cgroup = NULL;
struct cgroup_controller *cgc = NULL;
char path[FILENAME_MAX];
void *handle;
cgroup = cgroup_new_cgroup(".");
if (cgroup == NULL)
return ECGFAIL;
cgc = cgroup_add_controller(cgroup, mount_info->name);
if (cgc == NULL) {
ret = ECGFAIL;
goto out_error;
}
ret = cgroup_delete_cgroup_ext(cgroup, CGFLAG_DELETE_RECURSIVE);
if (ret != 0)
goto out_error;
/* unmount everything */
ret = cgroup_get_subsys_mount_point_begin(mount_info->name, &handle,
path);
while (ret == 0) {
error = umount(path);
if (error) {
cgroup_warn("Warning: cannot unmount controller %s on %s: %s\n",
mount_info->name, path,
strerror(errno));
last_errno = errno;
ret = ECGOTHER;
goto out_error;
}
ret = cgroup_get_subsys_mount_point_next(&handle, path);
}
cgroup_get_subsys_mount_point_end(&handle);
if (ret == ECGEOF)
ret = 0;
out_error:
if (cgroup)
cgroup_free(&cgroup);
return ret;
}
int cgroup_unload_cgroups(void)
{
int error = 0;
void *ctrl_handle;
int ret = 0;
char *curr_path = NULL;
struct cgroup_mount_point info;
error = cgroup_init();
if (error) {
ret = error;
goto out_error;
}
error = cgroup_get_controller_begin(&ctrl_handle, &info);
while (error == 0) {
if (!curr_path || strcmp(info.path, curr_path) != 0) {
if (curr_path)
free(curr_path);
curr_path = strdup(info.path);
if (!curr_path)
goto out_errno;
error = cgroup_config_unload_controller(&info);
if (error) {
/* remember the error and continue unloading
* the rest */
cgroup_warn("Warning: cannot clear controller %s\n",
info.name);
ret = error;
error = 0;
}
}
error = cgroup_get_controller_next(&ctrl_handle, &info);
}
if (error == ECGEOF)
error = 0;
if (error)
ret = error;
out_error:
if (curr_path)
free(curr_path);
cgroup_get_controller_end(&ctrl_handle);
return ret;
out_errno:
last_errno = errno;
cgroup_get_controller_end(&ctrl_handle);
return ECGOTHER;
}
/**
* Defines the default group. The parser puts content of 'default { }' to
* topmost group in config_cgroup_table. This function copies the permissions
* from it to our default cgroup.
*/
int cgroup_config_define_default(void)
{
struct cgroup *config_cgroup =
&config_cgroup_table[cgroup_table_index];
init_cgroup_table(&default_group, 1);
if (config_cgroup->control_dperm != NO_PERMS)
default_group.control_dperm = config_cgroup->control_dperm;
if (config_cgroup->control_fperm != NO_PERMS)
default_group.control_fperm = config_cgroup->control_fperm;
if (config_cgroup->control_gid != NO_UID_GID)
default_group.control_gid = config_cgroup->control_gid;
if (config_cgroup->control_uid != NO_UID_GID)
default_group.control_uid = config_cgroup->control_uid;
if (config_cgroup->task_fperm != NO_PERMS)
default_group.task_fperm = config_cgroup->task_fperm;
if (config_cgroup->tasks_gid != NO_UID_GID)
default_group.tasks_gid = config_cgroup->tasks_gid;
if (config_cgroup->tasks_uid != NO_UID_GID)
default_group.tasks_uid = config_cgroup->tasks_uid;
/*
* Reset all changes made by 'default { }' to the topmost group so it
* can be used by following 'group { }'.
*/
init_cgroup_table(config_cgroup, 1);
return 0;
}
int cgroup_config_set_default(struct cgroup *new_default)
{
if (!new_default)
return ECGINVAL;
init_cgroup_table(&default_group, 1);
default_group.control_dperm = new_default->control_dperm;
default_group.control_fperm = new_default->control_fperm;
default_group.control_gid = new_default->control_gid;
default_group.control_uid = new_default->control_uid;
default_group.task_fperm = new_default->task_fperm;
default_group.tasks_gid = new_default->tasks_gid;
default_group.tasks_uid = new_default->tasks_uid;
default_group_set = 1;
return 0;
}
/**
* Reloads the templates list, using the given configuration file.
* @return 0 on success, > 0 on failure
*/
int cgroup_reload_cached_templates(char *pathname)
{
int i;
int ret = 0;
if (template_table) {
/* template structures have to be free */
for (i = 0; i < template_table_index; i++)
cgroup_free_controllers(&template_table[i]);
free(template_table);
template_table = NULL;
}
template_table_index = 0;
if ((config_template_table_index != 0) || (config_table_index != 0)) {
/* config template structures have to be free as well*/
cgroup_free_config();
}
/* reloading data to config template structures */
cgroup_dbg("Reloading cached templates from %s.\n", pathname);
ret = cgroup_parse_config(pathname);
if (ret) {
cgroup_dbg("Could not reload template cache, error was: %d\n",
ret);
return ret;
}
/* copy data to templates cache structures */
template_table_index = config_template_table_index;
template_table = calloc(template_table_index, sizeof(struct cgroup));
if (template_table == NULL) {
ret = ECGOTHER;
return ret;
}
for (i = 0; i < template_table_index; i++) {
cgroup_copy_cgroup(&template_table[i],
&config_template_table[i]);
strcpy((template_table[i]).name,
(config_template_table[i]).name);
template_table[i].tasks_uid =
config_template_table[i].tasks_uid;
template_table[i].tasks_gid =
config_template_table[i].tasks_gid;
template_table[i].task_fperm =
config_template_table[i].task_fperm;
template_table[i].control_uid =
config_template_table[i].control_uid;
template_table[i].control_gid =
config_template_table[i].control_gid;
template_table[i].control_fperm =
config_template_table[i].control_fperm;
template_table[i].control_dperm =
config_template_table[i].control_dperm;
}
return ret;
}
/**
* Initializes the templates cache.
* @return 0 on success, > 0 on error
*/
int cgroup_init_templates_cache(char *pathname)
{
int ret = 0;
int i;
if (template_table) {
/* template structures have to be free */
for (i = 0; i < template_table_index; i++)
cgroup_free_controllers(&template_table[i]);
free(template_table);
template_table = NULL;
}
template_table_index = 0;
if ((config_template_table_index != 0) || (config_table_index != 0)) {
/* config structures have to be clean */
cgroup_free_config();
}
cgroup_dbg("Loading cached templates from %s.\n", pathname);
/* Attempt to read the configuration file and cache the rules. */
ret = cgroup_parse_config(pathname);
if (ret) {
cgroup_dbg("Could not initialize rule cache, error was: %d\n",
ret);
return ret;
}
/* copy template data to templates cache structures */
template_table_index = config_template_table_index;
template_table = calloc(template_table_index, sizeof(struct cgroup));
if (template_table == NULL) {
ret = ECGOTHER;
return ret;
}
for (i = 0; i < template_table_index; i++) {
cgroup_copy_cgroup(&template_table[i],
&config_template_table[i]);
strcpy((template_table[i]).name,
(config_template_table[i]).name);
template_table[i].tasks_uid =
config_template_table[i].tasks_uid;
template_table[i].tasks_gid =
config_template_table[i].tasks_gid;
template_table[i].task_fperm =
config_template_table[i].task_fperm;
template_table[i].control_uid =
config_template_table[i].control_uid;
template_table[i].control_gid =
config_template_table[i].control_gid;
template_table[i].control_fperm =
config_template_table[i].control_fperm;
template_table[i].control_dperm =
config_template_table[i].control_dperm;
}
return ret;
}
/*
* Create a given cgroup, based on template configuration if it is present
* if the template is not present cgroup is creted using cgroup_create_cgroup
*/
int cgroup_config_create_template_group(struct cgroup *cgroup,
char *template_name, int flags)
{
int ret = 0;
int i, j, k;
struct cgroup *t_cgroup;
char buffer[FILENAME_MAX];
struct cgroup *aux_cgroup;
struct cgroup_controller *cgc;
int found;
/*
* If the user did not ask for cached rules, we must parse the
* configuration file and prepare template structures now. We
* use CGCONFIG_CONF_FILE by default
*/
if (!(flags & CGFLAG_USE_TEMPLATE_CACHE)) {
if (template_table_index == 0)
/* the rules cache is empty */
ret = cgroup_init_templates_cache(CGCONFIG_CONF_FILE);
else
/* cache is not empty */
ret = cgroup_reload_cached_templates(
CGCONFIG_CONF_FILE);
if (ret != 0) {
cgroup_dbg("Failed initialize templates cache.\n");
return ret;
}
}
for (i = 0; cgroup->controller[i] != NULL; i++) {
/* for each controller we have to add to cgroup structure
* either template cgroup or empty controller */
found = 0;
/* look for relevant template - test name x controller pair */
for (j = 0; j < template_table_index; j++) {
t_cgroup = &template_table[j];
if (strcmp(t_cgroup->name, template_name) != 0) {
/* template name does not match skip template */
continue;
}
/* template name match */
for (k = 0; t_cgroup->controller[k] != NULL; k++) {
if (strcmp((cgroup->controller[i])->name,
(t_cgroup->controller[k])->name) != 0) {
/* controller name does not match */
continue;
}
/* name and controller match template found */
/* variables substituted in template */
strncpy(buffer, t_cgroup->name,
FILENAME_MAX-1);
strncpy(t_cgroup->name, cgroup->name,
FILENAME_MAX-1);
ret = cgroup_create_cgroup(t_cgroup, flags);
strncpy(t_cgroup->name, buffer,
FILENAME_MAX-1);
if (ret) {
cgroup_dbg("creating group %s, error %d\n",
cgroup->name, ret);
goto end;
} else {
/* go to new controller */
j = template_table_index;
found = 1;
continue;
}
}
}
if (found == 1)
continue;
/* no template is present for given name x controller pair
* add controller to result cgroup */
aux_cgroup = cgroup_new_cgroup(cgroup->name);
if (aux_cgroup) {
ret = ECGINVAL;
fprintf(stderr, "cgroup %s can't be created\n",
cgroup->name);
goto end;
}
cgc = cgroup_add_controller(aux_cgroup,
(cgroup->controller[i])->name);
if (cgc == NULL) {
ret = ECGINVAL;
fprintf(stderr, "cgroup %s can't be created\n",
cgroup->name);
goto end;
}
ret = cgroup_create_cgroup(aux_cgroup, flags);
if (ret) {
ret = ECGINVAL;
fprintf(stderr, "cgroup %s can't be created\n",
cgroup->name);
goto end;
}
}
end:
return ret;
}