summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBalbir Singh <balbir@linux.vnet.ibm.com>2008-03-19 14:53:07 +0000
committerBalbir Singh <balbir@linux.vnet.ibm.com>2008-03-19 14:53:07 +0000
commitd55f73ddbb0dc099b1471f3493e505142ce94a97 (patch)
tree7d4bcc778ed6f35fe043962b29873cff9ef9e24c
downloadlibcg-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--Makefile31
-rw-r--r--config.c704
-rw-r--r--file-ops.c176
-rw-r--r--lex.l33
-rw-r--r--libcg.h121
-rw-r--r--parse.y257
-rw-r--r--samples/wlm.conf35
-rw-r--r--samples/wlm.conf.240
-rw-r--r--samples/wlm.conf.365
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;
+}
+
diff --git a/lex.l b/lex.l
new file mode 100644
index 0000000..f9cc1ae
--- /dev/null
+++ b/lex.l
@@ -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];}
+%%
+
diff --git a/libcg.h b/libcg.h
new file mode 100644
index 0000000..8fcda81
--- /dev/null
+++ b/libcg.h
@@ -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 */
diff --git a/parse.y b/parse.y
new file mode 100644
index 0000000..fe7d5fe
--- /dev/null
+++ b/parse.y
@@ -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;
+}