diff options
author | Balbir Singh <balbir@linux.vnet.ibm.com> | 2008-03-19 14:53:07 +0000 |
---|---|---|
committer | Balbir Singh <balbir@linux.vnet.ibm.com> | 2008-03-19 14:53:07 +0000 |
commit | d55f73ddbb0dc099b1471f3493e505142ce94a97 (patch) | |
tree | 7d4bcc778ed6f35fe043962b29873cff9ef9e24c | |
download | libcg-d55f73ddbb0dc099b1471f3493e505142ce94a97.tar.gz libcg-d55f73ddbb0dc099b1471f3493e505142ce94a97.tar.xz libcg-d55f73ddbb0dc099b1471f3493e505142ce94a97.zip |
First initial revision. Look for TODOs and BUGs
git-svn-id: https://libcg.svn.sourceforge.net/svnroot/libcg/src@1 4f4bb910-9a46-0410-90c8-c897d4f1cd53
-rw-r--r-- | Makefile | 31 | ||||
-rw-r--r-- | config.c | 704 | ||||
-rw-r--r-- | file-ops.c | 176 | ||||
-rw-r--r-- | lex.l | 33 | ||||
-rw-r--r-- | libcg.h | 121 | ||||
-rw-r--r-- | parse.y | 257 | ||||
-rw-r--r-- | samples/wlm.conf | 35 | ||||
-rw-r--r-- | samples/wlm.conf.2 | 40 | ||||
-rw-r--r-- | samples/wlm.conf.3 | 65 |
9 files changed, 1462 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a8ad7a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +# +# Copyright IBM Corporation. 2007 +# +# Authors: 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. +# +YACC_DEBUG=-t +DEBUG=-DDEBUG +INC=-I. +CFLAGS=-g -O2 -Wextra $(DEBUG) $(INC) +LIBS= + +all: cgconfig + +cgconfig: config.c y.tab.c lex.yy.c libcg.h file-ops.c + $(CC) $(CFLAGS) -o $@ y.tab.c lex.yy.c config.c file-ops.c $(LIBS) + +y.tab.c: parse.y lex.yy.c + byacc -v -d parse.y + +lex.yy.c: lex.l + flex lex.l + +clean: + \rm -f y.tab.c y.tab.h lex.yy.c y.output cgconfig diff --git a/config.c b/config.c new file mode 100644 index 0000000..7b4b0f7 --- /dev/null +++ b/config.c @@ -0,0 +1,704 @@ +/* + * 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 + * 2. Add namespace support + * 3. Add support for parsing cgroup filesystem and creating a + * config out of it. + * + * Code initiated and designed by Balbir Singh. All faults are most likely + * his mistake. + */ + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <grp.h> +#include <libcg.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 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", + NULL, +}; + +/* + * File traversal routines require the maximum number of open file + * descriptors to be specified + */ +const int cg_max_openfd = 20; + +/* + * Insert the group into the list of group names we maintain. This helps + * us cleanup nicely + */ +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; + + if (!group_list) { + group_list = tmp; + return 1; + } + curr = group_list; + while (curr->next) + curr = curr->next; + + curr->next = tmp; + return 1; +} + +/* + * Cleanup the group list. We walk the group list and free the entries in the + * hash tables and controller specific entries. + */ +int cg_cleanup_group_list(void) +{ + 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); + } + + return 1; +} + +/* + * Find and walk the mount_table structures to find the specified controller + * name. This routine is *NOT* thread safe. + */ +struct mount_table *cg_find_mount_info(const char *controller_name) +{ + struct mount_table *curr = mount_table; + char *str; + + while (curr) { + str = curr->options; + if (!str) + return NULL; + + str = strtok(curr->options, ","); + do { + if (!strncmp(str, controller_name, strlen(str))) + return curr; + str = strtok(NULL, ","); + } while(str); + curr = curr->next; + } + return NULL; +} + +int cg_cpu_controller_settings(struct cg_group *cg_group, + const char *group_path) +{ + int ret = 1; + char *shares_file; + + shares_file = malloc(strlen(group_path) + strlen("/cpu.shares") + 1); + if (!shares_file) + return 0; + + strncpy(shares_file, group_path, strlen(group_path)); + shares_file = strncat(shares_file, "/cpu.shares", + strlen("/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; +} + +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 + assert(0); + return ret; +} + +int cg_create_group(struct cg_group *cg_group) +{ + int i, ret; + struct mount_table *mount_info; + char *group_path, *tasks_file, *shares_file; + + dbg("found group %s\n", cg_group->name); + + for (i = 0; cg_controller_names[i]; i++) { + + /* + * Find the mount point related information + */ + mount_info = cg_find_mount_info(cg_controller_names[i]); + dbg("mount_info for controller %s:%s\n", + mount_info->mount_point, cg_controller_names[i]); + if (!mount_info) + return 0; + + /* + * TODO: Namespace support is most likely going to be + * plugged in here + */ + + /* + * Find the path to the group directory + */ + group_path = cg_build_group_path(cg_group, mount_info); + if (!group_path) + 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; + + /* + * Find the tasks file, should probably be encapsulated + * like we encapsulate cg_build_group_path + */ + tasks_file = malloc(strlen(group_path) + strlen("/tasks") + 1); + if (!tasks_file) + goto cleanup_dir; + strncpy(tasks_file, group_path, strlen(group_path)); + 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); + free(tasks_file); + free(group_path); + } + return 1; +cleanup_perm: + rmdir(group_path); +cleanup_dir: + free(group_path); +cleanup_group: + return 0; +} + +/* + * Go ahead and create the groups in the filesystem. This routine will need + * to be revisited everytime new controllers are added. + */ +int cg_create_groups(void) +{ + 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; + } + + return ret; +} + +/* + * 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; + } + + return 1; +err: + free(group_path); + 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 + */ +struct cg_group *cg_get_current_group(void) +{ + if (!current_group) + current_group = calloc(1, sizeof(*current_group)); + + return current_group; +} + +/* + * 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; +} + +int cg_insert_group(const char *group_name) +{ + struct cg_group *cg_group = cg_get_current_group(); + ENTRY item, *found_item; + int ret; + + 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; + } + 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(); + return 1; +err: + cg_cleanup_group_list(); + 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. + */ +int cg_parse_controller_options(char *controller, char *name_value) +{ + struct cg_group *cg_group = cg_get_current_group(); + char *name, *value; + + if (!cg_group) + 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; + } + dbg("cpu name %s value %s\n", name, value); + } else { + return 0; + } + free(controller); + free(name_value); + return 1; +} + +/* + * Convert the uid/gid field and supplied value to appropriate task + * permissions. This routine is *NOT* thread safe. + */ +int cg_group_task_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->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; +} + +/* + * 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 + */ +int cg_init_group_and_mount_info(void) +{ + int ret; + + group_list = NULL; + mount_table = NULL; + current_group = NULL; + + ret = hcreate_r(MAX_GROUP_ELEMENTS, &group_hash); + if (!ret) + return 0; + return 1; +} + +/* + * 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. + */ +void cg_destroy_group_and_mount_info(void) +{ + hdestroy_r(&group_hash); + group_list = NULL; + mount_table = NULL; + current_group = NULL; +} + +/* + * Insert a name, mount_point pair into the mount_table data structure + * TODO: Validate names and mount points + */ +int cg_insert_into_mount_table(const char *name, const char *mount_point) +{ + 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; + } + + return 1; +} + +void cg_cleanup_mount_table(void) +{ + struct mount_table *curr = mount_table, *tmp; + + while (curr) { + tmp = curr; + curr = curr->next; + tmp->next = NULL; + + /* + * Some of this data might have been allocated by the lexer + * while parsing tokens + */ + free(tmp->mount_point); + free(tmp->options); + + free(tmp); + } +} + +int cg_load_config(const char *pathname) +{ + yyin = fopen(pathname, "rw"); + if (!yyin) { + dbg("Failed to open file %s\n", pathname); + return 0; + } + + if (!cg_init_group_and_mount_info()) + return 0; + + if (yyparse() != 0) { + dbg("Failed to parse file %s\n", pathname); + return 0; + } + + if (!cg_mount_controllers()) + goto err_mnt; + if (!cg_create_groups()) + goto err_grp; + + fclose(yyin); + return 1; +err_grp: + cg_destroy_groups(); + cg_cleanup_group_list(); +err_mnt: + cg_unmount_controllers(); + cg_cleanup_mount_table(); + 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; + } + } + + cg_destroy_group_and_mount_info(); +} diff --git a/file-ops.c b/file-ops.c new file mode 100644 index 0000000..a627286 --- /dev/null +++ b/file-ops.c @@ -0,0 +1,176 @@ +/* + * 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 <libcg.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(const 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, const char *group_path) +{ + int ret; + ret = mkdir(group_path, 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_IROTH | S_IXOTH); + 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; +} + @@ -0,0 +1,33 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Authors: 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. + */ + +%{ +#include "y.tab.h" +int line_no = 1; + +%} + +%% +\n {line_no++;} +[ \t] {/* DO NOTHING */} +^#.*[ \t]* {/* Comments */} +^\*.*[ \t]* {/* Comments */} +"mount" {return MOUNT;} +"task" {return TASK;} +"admin" {return ADMIN;} +"perm" {return PERM;} +"group" {return GROUP;} +[a-zA-Z0-9_\-\/\.]+ {yylval.name = strdup(yytext); return ID;} +. {return yytext[0];} +%% + @@ -0,0 +1,121 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Author: 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. + * + */ + +#ifndef _LIBCG_H +#define _LIBCG_H + +#include <grp.h> +#include <sys/stat.h> + +#define _GNU_SOURCE +#define __USE_GNU + +/* Maximum number of mount points/controllers */ +#define MAX_MNT_ELEMENTS 8 +/* Estimated number of groups created */ +#define MAX_GROUP_ELEMENTS 128 + +int verbose; + +#ifdef DEBUG +#define dbg(x...) if (verbose) { \ + printf(x); \ + } +#else +#define dbg(x...) do { } while(0) +#endif + +/* + * NOTE: Wide characters are not supported at the moment. Wide character support + * 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 + */ +/* XX: Why a recursive structure? */ +struct list_of_names { + char *name; + struct list_of_names *next; +}; + +enum cg_msg_type { + CG_MSG_LOAD_FILE, + CG_MSG_UNLOAD_FILE, + CG_MSG_ERR, + CG_MSG_DONE, +}; + +#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, const 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); +#endif /* _LIBCG_H */ @@ -0,0 +1,257 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Authors: 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. + * + * NOTE: The grammar has been modified, not to be the most efficient, but + * to allow easy updation of internal data structures. + */ +%{ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <libcg.h> + +int yylex(void); +extern int line_no; +extern char *yytext; + +void yyerror(char *s) +{ + fprintf(stderr, "error at line number %d at %c:%s", line_no, *yytext, + s); +} + +int yywrap(void) +{ + return 1; +} + +%} + +%token ID MOUNT GROUP PERM TASK ADMIN + +%union { + char *name; + char chr; + int val; +} +%type <name> ID namevalue_conf +%type <val> mountvalue_conf mount task_namevalue_conf admin_namevalue_conf +%type <val> admin_conf task_conf task_or_admin group_conf group start +%start start +%% + +start : start group + { + $$ = $1; + } + | start mount + { + $$ = $1; + } + | + { + $$ = 1; + } + ; + +group : GROUP ID '{' group_conf '}' + { + $$ = $4; + if ($$) + cg_insert_group($2); + else { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +group_conf + : ID '{' namevalue_conf '}' + { + $$ = cg_parse_controller_options($1, $3); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | group_conf ID '{' namevalue_conf '}' + { + $$ = cg_parse_controller_options($2, $4); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | PERM '{' task_or_admin '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +namevalue_conf + : ID '=' ID ';' + { + $1 = realloc($1, strlen($1) + strlen($3) + 2); + $1 = strncat($1, " ", strlen(" ")); + $$ = strncat($1, $3, strlen($3)); + free($3); + } + | namevalue_conf ID '=' ID ';' + { + $2 = realloc($2, strlen($2) + strlen($4) + 2); + $2 = strncat($2, " ", strlen(" ")); + $$ = strncat($2, $4, strlen($4)); + free($4); + } + ; + +task_namevalue_conf + : ID '=' ID ';' + { + $$ = cg_group_task_perm($1, $3); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | task_namevalue_conf ID '=' ID ';' + { + $$ = $1 && cg_group_task_perm($2, $4); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +admin_namevalue_conf + : ID '=' ID ';' + { + $$ = cg_group_admin_perm($1, $3); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | admin_namevalue_conf ID '=' ID ';' + { + $$ = $1 && cg_group_admin_perm($2, $4); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +task_or_admin + : TASK '{' task_namevalue_conf '}' admin_conf + { + $$ = $3 && $5; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | ADMIN '{' admin_namevalue_conf '}' task_conf + { + $$ = $3 && $5; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +admin_conf: ADMIN '{' admin_namevalue_conf '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +task_conf: TASK '{' task_namevalue_conf '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +mountvalue_conf + : ID '=' ID ';' + { + if (!cg_insert_into_mount_table($1, $3)) { + cg_cleanup_mount_table(); + $$ = 0; + return $$; + } + $$ = 1; + } + | mountvalue_conf ID '=' ID ';' + { + if (!cg_insert_into_mount_table($2, $4)) { + cg_cleanup_mount_table(); + $$ = 0; + return $$; + } + $$ = 1; + } + ; + +mount : MOUNT '{' mountvalue_conf '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + + +%% diff --git a/samples/wlm.conf b/samples/wlm.conf new file mode 100644 index 0000000..dc905ad --- /dev/null +++ b/samples/wlm.conf @@ -0,0 +1,35 @@ +# +# Copyright IBM Corporation. 2007 +# +# Authors: 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. +# +# controller file +# + +group ca1 { + perm { + task { + uid = balbir; + gid = cgroup; + } + admin { + uid = root; + gid = cgroup; + } + } + + cpu { + cpu.shares = 500; + } +} + +mount { + cpu = /container; +} diff --git a/samples/wlm.conf.2 b/samples/wlm.conf.2 new file mode 100644 index 0000000..e4739e0 --- /dev/null +++ b/samples/wlm.conf.2 @@ -0,0 +1,40 @@ +# +# Copyright IBM Corporation. 2007 +# +# Authors: 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. +# +# controller file +# + +group ca1 { + perm { + task { + uid = bulls; + gid = bears; + } + admin { + uid = root; + gid = root; + } + } + + cpu { + cpu.shares = 500; + } + + memory { + memory.limit_in_bytes = 200M; + } +} + +mount { + cpu = /container; + memory = /memory; +} diff --git a/samples/wlm.conf.3 b/samples/wlm.conf.3 new file mode 100644 index 0000000..6738624 --- /dev/null +++ b/samples/wlm.conf.3 @@ -0,0 +1,65 @@ +# +# Copyright IBM Corporation. 2007 +# +# Authors: 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. +# +# controller file +# + +group ca1 { + perm { + task { + uid = root; + gid = kvm; + } + admin { + uid = root; + gid = root; + } + } + + cpu { + cpu.shares = 500; + } + + memory { + memory.limit_in_bytes = 200M; + } +} + +group default { + perm { + task { + uid = root; + gid = root; + } + admin { + uid = root; + gid = root; + } + } + + cpu { + cpu.shares = 500; + } + + memory { + memory.limit_in_bytes = 200M; + } +} + +mount { + cpu = /container; + memory = /memory; +} + +mount { + cpu_acct = /acct; +} |