1688 lines
39 KiB
C
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;
|
|
}
|