diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | Makefile.in | 9 | ||||
-rw-r--r-- | api.c | 90 | ||||
-rw-r--r-- | cgconfig.c | 55 | ||||
-rw-r--r-- | config.c | 854 | ||||
-rw-r--r-- | file-ops.c | 176 | ||||
-rw-r--r-- | libcgroup-internal.h | 15 | ||||
-rw-r--r-- | libcgroup.h | 80 | ||||
-rw-r--r-- | parse.y | 27 | ||||
-rw-r--r-- | samples/wlm.conf.3 | 9 |
10 files changed, 480 insertions, 844 deletions
@@ -31,9 +31,8 @@ VERSION=1 all: libcgroup.so cgconfigparser cgexec cgclassify cgrulesengd -cgconfigparser: libcgroup.so config.c y.tab.c lex.yy.c libcgroup.h file-ops.c - $(CC) $(CFLAGS) -o $@ y.tab.c lex.yy.c config.c file-ops.c \ - $(LDFLAGS) $(LIBS) +cgconfigparser: libcgroup.so cgconfig.c libcgroup.h + $(CC) $(CFLAGS) -Wall -o $@ cgconfig.c $(LDFLAGS) $(LIBS) cgexec: libcgroup.so cgexec.c libcgroup.h $(CC) $(CFLAGS) -Wall -o $@ cgexec.c $(LDFLAGS) $(LIBS) @@ -51,9 +50,9 @@ y.tab.c: parse.y lex.yy.c lex.yy.c: lex.l $(LEX) lex.l -libcgroup.so: api.c libcgroup.h wrapper.c +libcgroup.so: api.c libcgroup.h wrapper.c config.c lex.yy.c y.tab.c $(CC) $(CFLAGS) -shared -fPIC -Wl,--soname,$@.$(VERSION) -o $@ api.c \ - wrapper.c + wrapper.c lex.yy.c y.tab.c config.c ln -sf $@ $@.$(VERSION) test: diff --git a/Makefile.in b/Makefile.in index aa68f4e..74601cc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -31,9 +31,8 @@ VERSION=1 all: libcgroup.so cgconfigparser cgexec cgclassify cgrulesengd -cgconfigparser: libcgroup.so config.c y.tab.c lex.yy.c libcgroup.h file-ops.c - $(CC) $(CFLAGS) -o $@ y.tab.c lex.yy.c config.c file-ops.c \ - $(LDFLAGS) $(LIBS) +cgconfigparser: libcgroup.so cgconfig.c libcgroup.h + $(CC) $(CFLAGS) -Wall -o $@ cgconfig.c $(LDFLAGS) $(LIBS) cgexec: libcgroup.so cgexec.c libcgroup.h $(CC) $(CFLAGS) -Wall -o $@ cgexec.c $(LDFLAGS) $(LIBS) @@ -51,9 +50,9 @@ y.tab.c: parse.y lex.yy.c lex.yy.c: lex.l $(LEX) lex.l -libcgroup.so: api.c libcgroup.h wrapper.c +libcgroup.so: api.c libcgroup.h wrapper.c config.c lex.yy.c y.tab.c $(CC) $(CFLAGS) -shared -fPIC -Wl,--soname,$@.$(VERSION) -o $@ api.c \ - wrapper.c + wrapper.c lex.yy.c y.tab.c config.c ln -sf $@ $@.$(VERSION) test: @@ -658,7 +658,7 @@ static char *cg_build_path_locked(char *name, char *path, char *type) return NULL; } -static char *cg_build_path(char *name, char *path, char *type) +char *cg_build_path(char *name, char *path, char *type) { pthread_rwlock_rdlock(&cg_mount_table_lock); path = cg_build_path_locked(name, path, type); @@ -790,6 +790,76 @@ int cgroup_attach_task(struct cgroup *cgroup) return error; } +/** + * cg_mkdir_p, emulate the mkdir -p command (recursively creating paths) + * @path: path to create + */ +static int cg_mkdir_p(const char *path) +{ + char *real_path, *wd; + int i = 0, j = 0; + char pos, *str; + int ret = 0; + char cwd[FILENAME_MAX], *buf; + + buf = getcwd(cwd, FILENAME_MAX); + if (!buf) + return ECGOTHER; + + real_path = strdup(path); + if (!real_path) + return ECGOTHER; + + do { + while (real_path[j] != '\0' && real_path[j] != '/') + j++; + while (real_path[j] != '\0' && real_path[j] == '/') + j++; + if (i == j) + continue; + pos = real_path[j]; + real_path[j] = '\0'; /* Temporarily overwrite "/" */ + str = &real_path[i]; + ret = mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + wd = strdup(str); + if (!wd) { + ret = ECGOTHER; + break; + } + real_path[j] = pos; + if (ret) { + switch (errno) { + case EEXIST: + ret = 0; /* Not fatal really */ + break; + case EPERM: + ret = ECGROUPNOTOWNER; + free(wd); + goto done; + default: + ret = ECGROUPNOTALLOWED; + free(wd); + goto done; + } + } + i = j; + ret = chdir(wd); + if (ret) { + printf("could not chdir to child directory (%s)\n", wd); + break; + } + free(wd); + } while (real_path[i]); + + ret = chdir(buf); + if (ret) + printf("could not go back to old directory (%s)\n", cwd); + +done: + free(real_path); + return ret; +} + /* * create_control_group() * This is the basic function used to create the control group. This function @@ -801,21 +871,7 @@ static int cg_create_control_group(char *path) int error; if (!cg_test_mounted_fs()) return ECGROUPNOTMOUNTED; - error = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - if (error) { - switch(errno) { - case EEXIST: - /* - * If the directory already exists, it really should - * not be an error - */ - return 0; - case EPERM: - return ECGROUPNOTOWNER; - default: - return ECGROUPNOTALLOWED; - } - } + error = cg_mkdir_p(path); return error; } @@ -1065,7 +1121,7 @@ int cgroup_create_cgroup(struct cgroup *cgroup, int ignore_ownership) strcat(path, "/tasks"); error = chown(path, cgroup->tasks_uid, cgroup->tasks_gid); - if (!error) { + if (error) { error = ECGFAIL; goto err; } diff --git a/cgconfig.c b/cgconfig.c new file mode 100644 index 0000000..b98769f --- /dev/null +++ b/cgconfig.c @@ -0,0 +1,55 @@ + +/* + * Copyright IBM Corporation. 2007 + * + * Authors: Dhaval Giani <dhaval@linux.vnet.ibm.com> + * Balbir Singh <balbir@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. + * + * Code initiated and designed by Dhaval Giani. All faults are most likely + * his mistake. + */ + +#include <libcgroup.h> +#include <libcgroup-internal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char *argv[]) +{ + int c; + char filename[PATH_MAX]; + int ret; + + if (argc < 2) { + fprintf(stderr, "usage is %s <option> <config file>\n", + argv[0]); + exit(2); + } + + while ((c = getopt(argc, argv, "l:")) > 0) { + switch (c) { + case 'l': + strncpy(filename, optarg, PATH_MAX); + ret = cgroup_config_load_config(filename); + if (ret) { + printf("Loading configuration file %s " + "failed, ret = %d\n", filename, ret); + exit(3); + } + break; + default: + fprintf(stderr, "Invalid command line option\n"); + break; + } + } + return 0; +} @@ -20,6 +20,9 @@ * * 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> @@ -27,8 +30,10 @@ #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> @@ -39,672 +44,417 @@ #include <sys/stat.h> #include <sys/types.h> +#define MAX_CGROUPS 1024 + extern FILE *yyin; extern int yyparse(void); -extern int yydebug; -extern int line_no; -extern int verbose; - -struct hsearch_data group_hash; -struct list_of_names *group_list; -struct mount_table *mount_table; - -const char library_ver[] = "0.01"; -const char cg_filesystem[] = "cgroup"; - -struct cg_group *current_group; - -const char *cg_controller_names[] = { - "cpu", - "cpuacct", - "memory", - "cpuset", - NULL, -}; /* - * File traversal routines require the maximum number of open file - * descriptors to be specified + * 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. */ -const int cg_max_openfd = 20; +static struct cg_mount_table_s config_mount_table[CG_CONTROLLER_MAX]; +static int config_table_index; +static pthread_rwlock_t config_table_lock = PTHREAD_RWLOCK_INITIALIZER; +static struct cgroup config_cgroup_table[MAX_CGROUPS]; +int cgroup_table_index; /* - * Insert the group into the list of group names we maintain. This helps - * us cleanup nicely + * Needed for the type while mounting cgroupfs. */ -int cg_insert_into_group_list(const char *name) -{ - struct list_of_names *tmp, *curr; - - tmp = malloc(sizeof(*tmp)); - if (!tmp) - return 0; - tmp->next = NULL; - tmp->name = (char *)name; +static const char cgroup_filesystem[] = "cgroup"; - if (!group_list) { - group_list = tmp; - return 1; - } - curr = group_list; - while (curr->next) - curr = curr->next; - - curr->next = tmp; - return 1; -} +/* + * NOTE: All these functions return 1 on success + * and not 0 as is the library convention + */ /* - * Cleanup the group list. We walk the group list and free the entries in the - * hash tables and controller specific entries. + * 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 cg_cleanup_group_list(void) +int cgroup_config_insert_cgroup(char *cg_name) { - struct list_of_names *curr = group_list, *tmp; - ENTRY item, *found_item; - int ret; - struct cg_group *cg_group; - - while (curr) { - tmp = curr; - curr = curr->next; - item.key = tmp->name; - ret = hsearch_r(item, FIND, &found_item, &group_hash); - if (!ret) { - printf("Most likely a bug in the code\n"); - continue; - } - /* - * Free the name and it's value - */ - free(tmp->name); - cg_group = (struct cg_group *)found_item->data; - /* - * Controller specific cleanup - */ - if (cg_group->cpu_config.shares) - free(cg_group->cpu_config.shares); - - free(found_item->data); - } + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + strncpy(config_cgroup->name, cg_name, FILENAME_MAX); + /* + * Since this will be the last part to be parsed. + */ + cgroup_table_index++; + free(cg_name); return 1; } /* - * Find and walk the mount_table structures to find the specified controller - * name. This routine is *NOT* thread safe. + * 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 */ -struct mount_table *cg_find_mount_info(const char *controller_name) +int cgroup_config_parse_controller_options(char *controller, char *name_value) { - struct mount_table *curr = mount_table; - char *str; - - while (curr) { - dbg("options %s mntpt %s next %p\n", curr->options, - curr->mount_point, curr->next); - dbg("passed controller name %s\n", controller_name); - str = curr->options; - if (!str) - return NULL; - - str = strtok(curr->options, ","); - do { - if (!strcmp(str, controller_name)) - return curr; - str = strtok(NULL, ","); - } while(str); - curr = curr->next; - } - return NULL; -} + char *buffer; + char *name, *value; + struct cgroup_controller *cgc; + int error; + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; -int cg_cpu_controller_settings(struct cg_group *cg_group, - const char *group_path) -{ - int ret = 1; - char *shares_file; + cgc = cgroup_add_controller(config_cgroup, controller); - shares_file = malloc(strlen(group_path) + strlen("/cpu.shares") + 1); - if (!shares_file) - return 0; + if (!cgc) + goto parse_error; - strcpy(shares_file, group_path); - shares_file = strcat(shares_file, "/cpu.shares"); - dbg("shares file is %s\n", shares_file); - if (cg_group->cpu_config.shares) { - FILE *fd = fopen(shares_file, "rw+"); - if (!fd) - goto cleanup_shares; - /* - * Assume that one write will do it for us - */ - fwrite(cg_group->cpu_config.shares, - strlen(cg_group->cpu_config.shares), 1, fd); - fclose(fd); - } -cleanup_shares: - free(shares_file); - return ret; -} + /* + * Did we just specify the controller to create the correct + * set of directories, without setting any values? + */ + if (!name_value) + goto done; -int cg_controller_handle_option(struct cg_group *cg_group, - const char *cg_controller_name, - const char *group_path) -{ - int ret = 0; - if (!strncmp(cg_controller_name, "cpu", strlen("cpu"))) { - ret = cg_cpu_controller_settings(cg_group, group_path); - } else - printf("config for %s pending\n", cg_controller_name); - return ret; -} + name = strtok_r(name_value, " ", &buffer); -int cg_create_group(struct cg_group *cg_group) -{ - int i, ret; - struct mount_table *mount_info; - char *group_path[2], *tasks_file, *shares_file; + if (!name) + goto parse_error; - dbg("found group %s\n", cg_group->name); - group_path[1] = NULL; + value = strtok_r(NULL, " ", &buffer); - for (i = 0; cg_controller_names[i]; i++) { + if (!value) + goto parse_error; - /* - * Find the mount point related information - */ - mount_info = cg_find_mount_info(cg_controller_names[i]); - if (!mount_info) - continue; - dbg("mount_info for controller %s:%s\n", - mount_info->mount_point, cg_controller_names[i]); - /* - * TODO: Namespace support is most likely going to be - * plugged in here - */ + error = cgroup_add_value_string(cgc, name, value); - /* - * Find the path to the group directory - */ - group_path[0] = cg_build_group_path(cg_group, mount_info); - if (!group_path[0]) - goto cleanup_group; - /* - * Create the specified directory. Errors are ignored. - * If the directory already exists, we are most likely - * OK - */ - ret = cg_make_directory(cg_group, group_path); - if (!ret && (errno != EEXIST)) - goto cleanup_dir; + if (error) + goto parse_error; - /* - * Find the tasks file, should probably be encapsulated - * like we encapsulate cg_build_group_path - */ - tasks_file = malloc(strlen(group_path[0]) + strlen("/tasks") + 1); - if (!tasks_file) - goto cleanup_dir; - strcpy(tasks_file, group_path[0]); - tasks_file = strncat(tasks_file, "/tasks", strlen("/tasks")); - dbg("tasks file is %s\n", tasks_file); - - /* - * Assign task file ownership - */ - ret = chown(tasks_file, cg_group->tasks_uid, - cg_group->tasks_gid); - if (ret < 0) - goto cleanup_perm; - - /* - * Controller specific work, errors are not fatal. - */ - cg_controller_handle_option(cg_group, cg_controller_names[i], - group_path[0]); - free(tasks_file); - free(group_path[0]); - } +done: + free(controller); + free(name_value); return 1; -cleanup_perm: - rmdir(group_path[0]); -cleanup_dir: - free(group_path[0]); -cleanup_group: + +parse_error: + free(controller); + free(name_value); + cgroup_delete_cgroup(config_cgroup, 1); + cgroup_table_index--; return 0; } /* - * Go ahead and create the groups in the filesystem. This routine will need - * to be revisited everytime new controllers are added. + * Sets the tasks file's uid and gid */ -int cg_create_groups(void) +int cgroup_config_group_task_perm(char *perm_type, char *value) { - struct list_of_names *curr = group_list, *tmp; - ENTRY item, *found_item; - struct cg_group *cg_group; - int ret = 1; - - while (curr) { - tmp = curr; - curr = curr->next; - item.key = tmp->name; - ret = hsearch_r(item, FIND, &found_item, &group_hash); - if (!ret) - return 0; - cg_group = (struct cg_group *)found_item->data; - ret = cg_create_group(cg_group); - if (!ret) - break; + struct passwd *pw, *pw_buffer; + struct group *group, *group_buffer; + int error; + long val = atoi(value); + char buffer[CGROUP_BUFFER_LEN]; + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + + if (!strcmp(perm_type, "uid")) { + if (!val) { + pw = (struct passwd *) malloc(sizeof(struct passwd)); + + if (!pw) + goto group_task_error; + + 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; } - return ret; -} + if (!strcmp(perm_type, "gid")) { + if (!val) { + group = (struct group *) malloc(sizeof(struct group)); -/* - * Go ahead and create the groups in the filesystem. This routine will need - * to be revisited everytime new controllers are added. - */ -int cg_destroy_groups(void) -{ - struct list_of_names *curr = group_list, *tmp; - ENTRY item, *found_item; - struct cg_group *cg_group; - int ret; - struct mount_table *mount_info; - char *group_path; - - while (curr) { - tmp = curr; - curr = curr->next; - item.key = tmp->name; - ret = hsearch_r(item, FIND, &found_item, &group_hash); - if (!ret) - return 0; - cg_group = (struct cg_group *)found_item->data; - mount_info = cg_find_mount_info("cpu"); - dbg("mount_info for cpu %s\n", mount_info->mount_point); - if (!mount_info) - return 0; - group_path = malloc(strlen(mount_info->mount_point) + - strlen(cg_group->name) + 2); - if (!group_path) - return 0; - strncpy(group_path, mount_info->mount_point, - strlen(mount_info->mount_point) + 1); - dbg("group_path is %s\n", group_path); - strncat(group_path, "/", strlen("/")); - strncat(group_path, cg_group->name, strlen(cg_group->name)); - dbg("group_path is %s\n", group_path); - /* - * We might strategically add migrate tasks here, so that - * rmdir is successful. TODO: Later - */ - ret = rmdir(group_path); - if (ret < 0) - goto err; + if (!group) + goto group_task_error; + + error = getgrnam_r(value, group, buffer, + CGROUP_BUFFER_LEN, &group_buffer); + + if (group_buffer == NULL) { + free(group); + goto group_task_error; + } + + val = group->gr_gid; + free(group); + } + config_cgroup->tasks_gid = val; } return 1; -err: - free(group_path); + +group_task_error: + free(perm_type); + free(value); + cgroup_delete_cgroup(config_cgroup, 1); + cgroup_table_index--; return 0; } + /* - * The cg_get_current_group routine is used by the parser to figure out - * the current group that is being built and fill it in with the information - * as it parses through the configuration file + * Set the control file's uid/gid */ -struct cg_group *cg_get_current_group(void) +int cgroup_config_group_admin_perm(char *perm_type, char *value) { - if (!current_group) - current_group = calloc(1, sizeof(*current_group)); + struct passwd *pw, *pw_buffer; + struct group *group, *group_buffer; + int error; + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + long val = atoi(value); + char buffer[CGROUP_BUFFER_LEN]; - return current_group; -} + if (!strcmp(perm_type, "uid")) { + if (!val) { + pw = (struct passwd *) malloc(sizeof(struct passwd)); -/* - * This routine should be invoked when the current group being parsed is - * completely parsed - */ -void cg_put_current_group(void) -{ - /* - * NOTE: we do not free the group, the group is installed into the - * hash table, the cleanup routine will do the freeing of the group - */ - current_group = NULL; -} + if (!pw) + goto admin_error; -int cg_insert_group(const char *group_name) -{ - struct cg_group *cg_group = cg_get_current_group(); - ENTRY item, *found_item; - int ret; + error = getpwnam_r(value, pw, buffer, CGROUP_BUFFER_LEN, + &pw_buffer); + if (error) { + free(pw); + goto admin_error; + } - if (!cg_group) - return 0; - /* - * Dont' copy the name over, just point to it - */ - cg_group->name = (char *)group_name; - item.key = (char *)group_name; - item.data = cg_group; - dbg("Inserting %s into hash table\n", group_name); - ret = hsearch_r(item, ENTER, &found_item, &group_hash); - if (!ret) { - if (found_item) - errno = EEXIST; - errno = ENOMEM; - goto err; + val = pw->pw_uid; + free(pw); + } + config_cgroup->control_uid = val; } - ret = cg_insert_into_group_list(group_name); - if (!ret) - goto err; - dbg("Inserted %s into hash and list\n", group_name); - cg_put_current_group(); + + if (!strcmp(perm_type, "gid")) { + if (!val) { + group = (struct group *) malloc(sizeof(struct group)); + + if (!group) + goto admin_error; + + error = getgrnam_r(value, group, buffer, + CGROUP_BUFFER_LEN, &group_buffer); + + if (error) { + free(group); + goto admin_error; + } + + val = group->gr_gid; + free(group); + } + config_cgroup->control_gid = val; + } + return 1; -err: - cg_cleanup_group_list(); + +admin_error: + free(perm_type); + free(value); + cgroup_delete_cgroup(config_cgroup, 1); + cgroup_table_index--; return 0; } /* - * Because of the way parsing works (bottom-up, shift-reduce), we don't - * know the name of the controller yet. Compilers build an AST and use - * a symbol table to deal with this problem. This code does simple things - * like concatinating strings and passing them upwards. This routine is - * *NOT* thread safe. - * - * This code will need modification everytime new controller support is - * added. + * The moment we have found the controller's information + * insert it into the config_mount_table. */ -int cg_parse_controller_options(char *controller, char *name_value) +int cgroup_config_insert_into_mount_table(char *name, char *mount_point) { - struct cg_group *cg_group = cg_get_current_group(); - char *name, *value; + int i; - if (!cg_group) + if (config_table_index >= CG_CONTROLLER_MAX) return 0; - if (!strncmp(controller, "cpu", strlen("cpu"))) { - name = strtok(name_value, " "); - value = strtok(NULL, " "); - if (!strncmp(name, "cpu.shares", strlen("cpu.shares"))) - cg_group->cpu_config.shares = strdup(value); - else { - free(controller); - free(name_value); - 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].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; } - dbg("cpu name %s value %s\n", name, value); - } else { - return 0; } - free(controller); - free(name_value); + + strcpy(config_mount_table[config_table_index].name, name); + strcpy(config_mount_table[config_table_index].path, mount_point); + config_table_index++; +done: + pthread_rwlock_unlock(&config_table_lock); + free(name); + free(mount_point); return 1; } /* - * Convert the uid/gid field and supplied value to appropriate task - * permissions. This routine is *NOT* thread safe. + * Cleanup all the data from the config_mount_table */ -int cg_group_task_perm(char *perm_type, char *value) +void cgroup_config_cleanup_mount_table(void) { - struct cg_group *cg_group = cg_get_current_group(); - struct passwd *pw; - struct group *group; - long val = atoi(value); - if (!strncmp(perm_type, "uid", strlen("uid"))) { - if (!val) { /* We got the identifier as a name */ - pw = getpwnam(value); - if (!pw) { - free(perm_type); - free(value); - return 0; - } else { - cg_group->tasks_uid = pw->pw_uid; - } - } else { - cg_group->tasks_uid = val; - } - dbg("TASKS %s: %d\n", perm_type, cg_group->tasks_uid); - } - if (!strncmp(perm_type, "gid", strlen("gid"))) { - if (!val) { /* We got the identifier as a name */ - group = getgrnam(value); - if (!group) { - free(perm_type); - free(value); - return 0; - } else { - cg_group->tasks_gid = group->gr_gid; - } - } else { - cg_group->tasks_gid = val; - } - dbg("TASKS %s: %d\n", perm_type, cg_group->tasks_gid); - } - free(perm_type); - free(value); - return 1; -} - -int cg_group_admin_perm(char *perm_type, char *value) -{ - struct cg_group *cg_group = cg_get_current_group(); - struct passwd *pw; - struct group *group; - long val = atoi(value); - if (!strncmp(perm_type, "uid", strlen("uid"))) { - if (!val) { /* We got the identifier as a name */ - pw = getpwnam(value); - if (!pw) { - free(perm_type); - free(value); - return 0; - } else { - cg_group->admin_uid = pw->pw_uid; - } - } else { - cg_group->admin_uid = val; - } - dbg("ADMIN %s: %d\n", perm_type, cg_group->admin_uid); - } - if (!strncmp(perm_type, "gid", strlen("gid"))) { - if (!val) { /* We got the identifier as a name */ - group = getgrnam(value); - if (!group) { - free(perm_type); - free(value); - return 0; - } else { - cg_group->admin_gid = group->gr_gid; - } - } else { - cg_group->admin_gid = val; - } - dbg("ADMIN %s: %d\n", perm_type, - cg_group->admin_gid); - } - free(perm_type); - free(value); - return 1; + memset(&config_mount_table, 0, + sizeof(struct cg_mount_table_s) * CG_CONTROLLER_MAX); } /* - * We maintain a hash table. The group hash table maintains the parameters for - * each group, including the parameters for each controller - * - * TODO: Make the initialization a run time configuration parameter + * Start mounting the mount table. */ -int cg_init_group_and_mount_info(void) +int cgroup_config_mount_fs() { int ret; + struct stat buff; + int i; - group_list = NULL; - mount_table = NULL; - current_group = NULL; + for (i = 0; i < config_table_index; i++) { + struct cg_mount_table_s *curr = &(config_mount_table[i]); - ret = hcreate_r(MAX_GROUP_ELEMENTS, &group_hash); - if (!ret) - return 0; - return 1; + ret = stat(curr->path, &buff); + + if (ret < 0 && errno != ENOENT) + return ECGOTHER; + + if (errno == ENOENT) { + ret = mkdir(curr->path, + S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (ret < 0) + return ECGOTHER; + } else if (!S_ISDIR(buff.st_mode)) { + errno = ENOTDIR; + return ECGOTHER; + } + + ret = mount(cgroup_filesystem, curr->path, cgroup_filesystem, + 0, curr->name); + + if (ret < 0) + return ECGMOUNTFAIL; + } + return 0; } /* - * This routine should be called only once all elements of the hash table have - * been freed. Otherwise, we'll end up with a memory leak. + * Actually create the groups once the parsing has been finished. */ -void cg_destroy_group_and_mount_info(void) +int cgroup_config_create_groups() { - hdestroy_r(&group_hash); - group_list = NULL; - mount_table = NULL; - current_group = NULL; + 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); + dbg("creating group %s, error %d\n", cgroup->name, error); + if (error) + return error; + } + return error; } /* - * Insert a name, mount_point pair into the mount_table data structure - * TODO: Validate names and mount points + * Destroy the cgroups */ -int cg_insert_into_mount_table(const char *name, const char *mount_point) +int cgroup_config_destroy_groups(void) { - struct mount_table *tmp, *prev = mount_table; - - while (prev && prev->next != NULL && - (strncmp(mount_point, prev->mount_point, strlen(mount_point)))) - prev = prev->next; - - if (prev && - !(strncmp(mount_point, prev->mount_point, strlen(mount_point)))) { - prev->options = realloc(prev->options, strlen(prev->options) - + strlen(name) + 2); - if (!prev->options) - return 0; - strncat(prev->options, ",", strlen(",")); - strncat(prev->options, name, strlen(name)); - dbg("options %s: mount_point %s\n", prev->options, - prev->mount_point); - return 1; - } - - tmp = malloc(sizeof(*tmp)); - if (!tmp) - return 0; - - tmp->next = NULL; - tmp->mount_point = (char *)mount_point; - tmp->options = (char *)name; - dbg("options %s: mount_point %s\n", tmp->options, tmp->mount_point); - - if (!prev) { - mount_table = tmp; - return 1; - } else { - prev->next = tmp; + int error = 0; + int i; + + for (i = 0; i < cgroup_table_index; i++) { + struct cgroup *cgroup = &config_cgroup_table[i]; + error = cgroup_delete_cgroup(cgroup, 0); + if (error) + return error; } - - return 1; + return error; } -void cg_cleanup_mount_table(void) +/* + * Unmount the controllers + */ +int cgroup_config_unmount_controllers(void) { - struct mount_table *curr = mount_table, *tmp; - - while (curr) { - tmp = curr; - curr = curr->next; - tmp->next = NULL; + int i; + int error; + for (i = 0; i < config_table_index; i++) { /* - * Some of this data might have been allocated by the lexer - * while parsing tokens + * We ignore failures and ensure that all mounted + * containers are unmounted */ - free(tmp->mount_point); - free(tmp->options); - - free(tmp); + error = umount(config_mount_table[i].path); + if (error < 0) + dbg("Unmount failed\n"); + error = rmdir(config_mount_table[i].path); + if (error < 0) + dbg("rmdir failed\n"); } + + return 0; } -int cg_load_config(const char *pathname) +/* + * 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) { - yyin = fopen(pathname, "rw"); + int error; + yyin = fopen(pathname, "r"); + if (!yyin) { dbg("Failed to open file %s\n", pathname); - return 0; + return ECGOTHER; } - if (!cg_init_group_and_mount_info()) - return 0; - if (yyparse() != 0) { dbg("Failed to parse file %s\n", pathname); - return 0; + return ECGROUPPARSEFAIL; } - if (!cg_mount_controllers()) + error = cgroup_config_mount_fs(); + if (error) + goto err_mnt; + + error = cgroup_init(); + if (error) goto err_mnt; - if (!cg_create_groups()) + + error = cgroup_config_create_groups(); + dbg("creating all cgroups now, error=%d\n", error); + if (error) goto err_grp; fclose(yyin); - return 1; + return 0; err_grp: - dbg("Creating groups failed\n"); - cg_destroy_groups(); - cg_cleanup_group_list(); + cgroup_config_destroy_groups(); err_mnt: - dbg("Mounting controllers failed\n"); - cg_unmount_controllers(); - cg_cleanup_mount_table(); + cgroup_config_unmount_controllers(); fclose(yyin); - return 0; -} - -void cg_unload_current_config(void) -{ - cg_destroy_groups(); - cg_cleanup_group_list(); - cg_unmount_controllers(); - cg_cleanup_mount_table(); - cg_destroy_group_and_mount_info(); -} - -int main(int argc, char *argv[]) -{ - int c; - char filename[PATH_MAX]; - int ret; - - if (argc < 2) { - fprintf(stderr, "usage is %s <option> <config file>\n", - argv[0]); - exit(2); - } - - while ((c = getopt(argc, argv, "l:ur:")) > 0) { - switch (c) { - case 'u': - cg_unload_current_config(); - break; - case 'r': - cg_unload_current_config(); - /* FALLTHROUGH */ - case 'l': - strncpy(filename, optarg, PATH_MAX); - ret = cg_load_config(filename); - if (!ret) - exit(3); - break; - default: - fprintf(stderr, "Invalid command line option\n"); - break; - } - } - + return error; } diff --git a/file-ops.c b/file-ops.c deleted file mode 100644 index ffba8e8..0000000 --- a/file-ops.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 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. - * - */ - -#include <dirent.h> -#include <errno.h> -#include <fts.h> -#include <grp.h> -#include <libcgroup.h> -#include <limits.h> -#include <pwd.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> - -extern const char cg_filesystem[]; -extern struct mount_table *mount_table; - -int cg_chown_file(FTS *fts, FTSENT *ent, uid_t owner, gid_t group) -{ - int ret = 1; - const char *filename = fts->fts_path; - dbg("seeing file %s\n", filename); - switch (ent->fts_info) { - case FTS_ERR: - errno = ent->fts_errno; - break; - case FTS_D: - case FTS_DC: - case FTS_NSOK: - case FTS_NS: - case FTS_DNR: - case FTS_DP: - case FTS_F: - case FTS_DEFAULT: - ret = chown(filename, owner, group); - break; - } - return ret; -} - -/* - * TODO: Need to decide a better place to put this function. - */ -int cg_chown_recursive(char *path[], uid_t owner, gid_t group) -{ - int ret = 1; - FTS *fts = fts_open((char **)path, FTS_PHYSICAL | FTS_NOCHDIR | - FTS_NOSTAT, NULL); - while (1) { - FTSENT *ent; - ent = fts_read(fts); - if (!ent) { - dbg("fts_read failed\n"); - break; - } - cg_chown_file(fts, ent, owner, group); - } - fts_close(fts); - return ret; -} - -char *cg_build_group_path(struct cg_group *cg_group, - struct mount_table *mount_info) -{ - char *group_path; - - group_path = malloc(strlen(mount_info->mount_point) + - strlen(cg_group->name) + 2); - if (!group_path) - return NULL; - strncpy(group_path, mount_info->mount_point, - strlen(mount_info->mount_point) + 1); - dbg("group path is %s\n", group_path); - strncat(group_path, "/", strlen("/")); - strncat(group_path, cg_group->name, strlen(cg_group->name)); - dbg("group path is %s\n", group_path); - return group_path; -} - -int cg_make_directory(struct cg_group *cg_group, char *group_path[]) -{ - int ret; - ret = mkdir(group_path[0], S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - if (ret < 0) - return 0; - /* - * Recursively change all files under the directory - */ - ret = cg_chown_recursive(group_path, cg_group->admin_uid, - cg_group->admin_gid); - return ret; -} - -/* - * After having parsed the configuration file, walk through the mount_table - * and mount the specified controllers. This routine might create directories - * specified as mount_point. - */ -int cg_mount_controllers(void) -{ - int ret; - struct mount_table *curr = mount_table; - struct stat buf; - - while (curr) { - ret = stat(curr->mount_point, &buf); - if (ret < 0 && errno != ENOENT) - return 0; - - /* - * Check if path needs to be created before mounting - */ - if (errno == ENOENT) { - ret = mkdir(curr->mount_point, S_IRWXU | S_IRWXG | - S_IRWXO | S_ISVTX); - if (ret < 0) - return 0; - } else if (!S_ISDIR(buf.st_mode)) { - errno = ENOTDIR; - return 0; - } - - ret = mount(cg_filesystem, curr->mount_point, - cg_filesystem, 0, curr->options); - - if (ret < 0) - return 0; - curr = curr->next; - } - return 1; -} - -/* - * Called during end of WLM configuration to unmount all controllers or - * on failure, to cleanup mounted controllers - */ -int cg_unmount_controllers(void) -{ - struct mount_table *curr = mount_table; - int ret; - - while (curr) { - /* - * We ignore failures and ensure that all mounted - * containers are unmounted - */ - ret = umount(curr->mount_point); - if (ret < 0) - printf("Unmount failed\n"); - ret = rmdir(curr->mount_point); - if (ret < 0) - printf("rmdir failed\n"); - curr = curr->next; - } - return 1; -} - diff --git a/libcgroup-internal.h b/libcgroup-internal.h index c2f2ce3..af5d396 100644 --- a/libcgroup-internal.h +++ b/libcgroup-internal.h @@ -24,6 +24,8 @@ __BEGIN_DECLS #define CGRULES_CONF_FILE "/etc/cgrules.conf" #define CGRULES_MAX_FIELDS_PER_LINE 3 +#define CGROUP_BUFFER_LEN (5 * FILENAME_MAX) + struct control_value { char name[FILENAME_MAX]; char value[CG_VALUE_MAX]; @@ -49,6 +51,7 @@ struct cgroup { struct cg_mount_table_s { char name[FILENAME_MAX]; char path[FILENAME_MAX]; + int index; }; struct cgroup_rules_data { @@ -78,6 +81,18 @@ struct cgroup_rule_list { }; +/* Internal API */ +char *cg_build_path(char *name, char *path, char *type); + +/* + * config related API + */ +int cgroup_config_insert_cgroup(char *cg_name); +int cgroup_config_parse_controller_options(char *controller, char *name_value); +int cgroup_config_group_task_perm(char *perm_type, char *value); +int cgroup_config_group_admin_perm(char *perm_type, char *value); +int cgroup_config_insert_into_mount_table(char *name, char *mount_point); +void cgroup_config_cleanup_mount_table(void); __END_DECLS #endif diff --git a/libcgroup.h b/libcgroup.h index 97c663d..9325846 100644 --- a/libcgroup.h +++ b/libcgroup.h @@ -54,47 +54,6 @@ __BEGIN_DECLS * would require us to use a scanner/parser that can parse beyond ASCII */ -/* - * These data structures are heavily controller dependent, which means - * any changes (additions/removal) of configuration items would have to - * be reflected in this library. We might implement a plugin - * infrastructure, so that we can deal with such changes with ease. - */ - -struct cpu_controller { - /*TODO: Add the cpu.usage file here, also need to automate this.*/ - char *shares; /* Having strings helps us write them to files */ - /* - * XX: No it does not make a difference. It requires a fprintf anyway - * so it needs the qualifier. - */ -}; - -struct cg_group { - char *name; - uid_t tasks_uid; - gid_t tasks_gid; - uid_t admin_uid; - gid_t admin_gid; - struct cpu_controller cpu_config; -}; - -/* - * A singly linked list suffices since we don't expect too many mount points - */ -struct mount_table { - char *options; /* Name(s) of the controller */ - char *mount_point; /* The place where the controller is mounted */ - struct mount_table *next; -}; - -/* - * Maintain a list of all group names. These will be used during cleanup - */ -struct list_of_names { - char *name; - struct list_of_names *next; -}; /* Maximum length of a line in the daemon config file */ #define CGROUP_RULE_MAXLINE (FILENAME_MAX + LOGIN_NAME_MAX + \ @@ -109,13 +68,6 @@ enum cgflags { CGFLAG_USECACHE = 0x01, }; -enum cg_msg_type { - CG_MSG_LOAD_FILE, - CG_MSG_UNLOAD_FILE, - CG_MSG_ERR, - CG_MSG_DONE, -}; - enum cgroup_errors { ECGROUPNOTCOMPILED=50000, ECGROUPNOTMOUNTED, @@ -141,35 +93,9 @@ enum cgroup_errors { ECGCONTROLLERNOTEQUAL, ECGROUPPARSEFAIL, /* Failed to parse rules configuration file. */ ECGROUPNORULES, /* Rules list does not exist. */ + ECGMOUNTFAIL, }; -#define CG_MAX_MSG_SIZE 256 -#define CG_SERVER_MSG_PATH "/tmp/control_group" -#define CG_BACKLOG 5 - -/* Message's exchanged between server and client */ -struct cg_msg { - enum cg_msg_type type; - char buf[CG_MAX_MSG_SIZE]; -}; - -/* Function Prototypes start here */ -int cg_init_group_and_mount_info(void); -int cg_insert_into_mount_table(const char *name, const char *mount_point); -void cg_cleanup_mount_table(void); -int cg_group_admin_perm(char *perm_type, char *value); -int cg_group_task_perm(char *perm_type, char *value); -int cg_parse_controller_options(char *controller, char *name_value); -int cg_insert_group(const char *group_name); -int chown_recursive(const char* path, uid_t owner, gid_t group); -int cg_make_directory(struct cg_group *cg_group, char *group_path[]); -char *cg_build_group_path(struct cg_group *cg_group, - struct mount_table *mount_info); -int cg_mount_controllers(void); -int cg_unmount_controllers(void); -int cg_load_config(const char *pathname); -void cg_unload_current_config(void); - #define CG_NV_MAX 100 #define CG_CONTROLLER_MAX 100 #define CG_VALUE_MAX 100 @@ -300,6 +226,10 @@ int cgroup_get_value_bool(struct cgroup_controller *controller, const char *name, bool *value); int cgroup_set_value_bool(struct cgroup_controller *controller, const char *name, bool value); +/* + * Config related stuff + */ +int cgroup_config_load_config(const char *pathname); __END_DECLS @@ -18,6 +18,7 @@ #include <stdio.h> #include <string.h> #include <libcgroup.h> +#include <libcgroup-internal.h> int yylex(void); extern int line_no; @@ -67,7 +68,7 @@ group : GROUP ID '{' group_conf '}' { $$ = $4; if ($$) - cg_insert_group($2); + cgroup_config_insert_cgroup($2); else { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -80,7 +81,7 @@ group : GROUP ID '{' group_conf '}' group_conf : ID '{' namevalue_conf '}' { - $$ = cg_parse_controller_options($1, $3); + $$ = cgroup_config_parse_controller_options($1, $3); if (!$$) { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -90,7 +91,7 @@ group_conf } | group_conf ID '{' namevalue_conf '}' { - $$ = cg_parse_controller_options($2, $4); + $$ = cgroup_config_parse_controller_options($2, $4); if (!$$) { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -125,12 +126,16 @@ namevalue_conf $$ = strncat($2, $4, strlen($4)); free($4); } + | + { + $$ = NULL; + } ; task_namevalue_conf : ID '=' ID ';' { - $$ = cg_group_task_perm($1, $3); + $$ = cgroup_config_group_task_perm($1, $3); if (!$$) { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -140,7 +145,7 @@ task_namevalue_conf } | task_namevalue_conf ID '=' ID ';' { - $$ = $1 && cg_group_task_perm($2, $4); + $$ = $1 && cgroup_config_group_task_perm($2, $4); if (!$$) { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -153,7 +158,7 @@ task_namevalue_conf admin_namevalue_conf : ID '=' ID ';' { - $$ = cg_group_admin_perm($1, $3); + $$ = cgroup_config_group_admin_perm($1, $3); if (!$$) { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -163,7 +168,7 @@ admin_namevalue_conf } | admin_namevalue_conf ID '=' ID ';' { - $$ = $1 && cg_group_admin_perm($2, $4); + $$ = $1 && cgroup_config_group_admin_perm($2, $4); if (!$$) { fprintf(stderr, "parsing failed at line number %d\n", line_no); @@ -223,8 +228,8 @@ task_conf: TASK '{' task_namevalue_conf '}' mountvalue_conf : ID '=' ID ';' { - if (!cg_insert_into_mount_table($1, $3)) { - cg_cleanup_mount_table(); + if (!cgroup_config_insert_into_mount_table($1, $3)) { + cgroup_config_cleanup_mount_table(); $$ = 0; return $$; } @@ -232,8 +237,8 @@ mountvalue_conf } | mountvalue_conf ID '=' ID ';' { - if (!cg_insert_into_mount_table($2, $4)) { - cg_cleanup_mount_table(); + if (!cgroup_config_insert_into_mount_table($2, $4)) { + cgroup_config_cleanup_mount_table(); $$ = 0; return $$; } diff --git a/samples/wlm.conf.3 b/samples/wlm.conf.3 index 6738624..a0e9a1b 100644 --- a/samples/wlm.conf.3 +++ b/samples/wlm.conf.3 @@ -32,6 +32,7 @@ group ca1 { memory { memory.limit_in_bytes = 200M; } + } group default { @@ -53,13 +54,15 @@ group default { memory { memory.limit_in_bytes = 200M; } + + cpuacct {} } mount { - cpu = /container; - memory = /memory; + cpu = /cgroup/cpu; + memory = /cgroup/mem; } mount { - cpu_acct = /acct; + cpuacct = /cgroup/cpu; } |