]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
First initial revision. Look for TODOs and BUGs
authorBalbir Singh <balbir@linux.vnet.ibm.com>
Wed, 19 Mar 2008 14:53:07 +0000 (14:53 +0000)
committerBalbir Singh <balbir@linux.vnet.ibm.com>
Wed, 19 Mar 2008 14:53:07 +0000 (14:53 +0000)
git-svn-id: https://libcg.svn.sourceforge.net/svnroot/libcg/src@1 4f4bb910-9a46-0410-90c8-c897d4f1cd53

Makefile [new file with mode: 0644]
config.c [new file with mode: 0644]
file-ops.c [new file with mode: 0644]
lex.l [new file with mode: 0644]
libcg.h [new file with mode: 0644]
parse.y [new file with mode: 0644]
samples/wlm.conf [new file with mode: 0644]
samples/wlm.conf.2 [new file with mode: 0644]
samples/wlm.conf.3 [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
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 (file)
index 0000000..a627286
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..dc905ad
--- /dev/null
@@ -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 (file)
index 0000000..e4739e0
--- /dev/null
@@ -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 (file)
index 0000000..6738624
--- /dev/null
@@ -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;
+}