diff options
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | api.c | 337 | ||||
-rw-r--r-- | libcg.h | 42 | ||||
-rw-r--r-- | scripts/README | 4 | ||||
-rw-r--r-- | scripts/doc/howto.txt | 76 | ||||
-rw-r--r-- | scripts/etc/wlm.conf | 16 | ||||
-rw-r--r-- | scripts/etc/wlm/cpu.conf | 14 | ||||
-rwxr-xr-x | scripts/init.d/wlm | 256 | ||||
-rw-r--r-- | tests/Makefile | 9 | ||||
-rw-r--r-- | tests/libcg_ba.cpp | 125 |
10 files changed, 756 insertions, 136 deletions
@@ -13,14 +13,15 @@ YACC_DEBUG=-t DEBUG=-DDEBUG INC=-I. -CFLAGS=-g -O2 -Wextra $(DEBUG) $(INC) +CFLAGS=-g -O2 -Wextra $(INC) LIBS= -lcg LDFLAGS= -L . +INSTALLPREFIX= all: cgconfig libcg.so cgconfig: libcg.so config.c y.tab.c lex.yy.c libcg.h file-ops.c - $(CXX) $(CFLAGS) -o $@ y.tab.c lex.yy.c config.c file-ops.c $(LDFLAGS) $(LIBS) + $(CC) $(CFLAGS) -o $@ y.tab.c lex.yy.c config.c file-ops.c $(LDFLAGS) $(LIBS) y.tab.c: parse.y lex.yy.c byacc -v -d parse.y @@ -31,5 +32,13 @@ lex.yy.c: lex.l libcg.so: api.c libcg.h $(CXX) $(CFLAGS) -shared -fPIC -o $@ api.c +install: libcg.so + \cp libcg.h $(INSTALLPREFIX)/usr/include + \cp libcg.so $(INSTALLPREFIX)/usr/lib + +uninstall: libcg.so + \rm $(INSTALLPREFIX)/usr/include/libcg.h + \rm $(INSTALLPREFIX)/usr/lib/libcg.so + clean: \rm -f y.tab.c y.tab.h lex.yy.c y.output cgconfig libcg.so @@ -2,6 +2,7 @@ * Copyright IBM Corporation. 2007 * * Author: Dhaval Giani <dhaval@linux.vnet.ibm.com> + * 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 @@ -31,24 +32,113 @@ #include <sys/stat.h> #include <sys/syscall.h> #include <unistd.h> +#include <fts.h> +/* + * Remember to bump this up for major API changes. + */ +const static char cg_version[] = "0.01"; + +/* + * Only one mount point is currently supported. This will be enhanced to + * support several hierarchies in the future + */ static char MOUNT_POINT[FILENAME_MAX]; +static int cg_chown_file(FTS *fts, FTSENT *ent, uid_t owner, gid_t group) +{ + int ret = 0; + 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. + */ +static int cg_chown_recursive(char **path, uid_t owner, gid_t group) +{ + int ret = 0; + dbg("path is %s\n", *path); + FTS *fts = fts_open(path, FTS_PHYSICAL | FTS_NOCHDIR | + FTS_NOSTAT, NULL); + while (1) { + FTSENT *ent; + ent = fts_read(fts); + if (!ent) { + dbg("fts_read failed\n"); + break; + } + ret = cg_chown_file(fts, ent, owner, group); + } + fts_close(fts); + return ret; +} + +/** + * cg_init(), initializes the MOUNT_POINT. + * This code is not currently thread safe (hint: getmntent is not thread safe). + * This API is likely to change in the future to push state back to the caller + * to achieve thread safety. The code currently supports just one mount point. + * Complain if the cgroup filesystem controllers are bound to different mount + * points. + */ int cg_init() { FILE *proc_mount; - struct mntent *ent; + struct mntent *ent, *found_ent = NULL; + int found_mnt = 0; + int ret = 0; proc_mount = fopen("/proc/mounts", "r"); - ent = getmntent(proc_mount); + if (proc_mount == NULL) { + return EIO; + } - while (strcmp(ent->mnt_fsname,"cgroup") != 0) { - ent = getmntent(proc_mount); - if (ent == NULL) - return ECGROUPNOTMOUNTED; + while ((ent = getmntent(proc_mount)) != NULL) { + if (!strncmp(ent->mnt_fsname,"cgroup", strlen("cgroup"))) { + found_ent = ent; + found_mnt++; + dbg("Found cgroup option %s, count %d\n", + found_ent->mnt_opts, found_mnt); + } } - strcpy(MOUNT_POINT, ent->mnt_dir); - return 0; + + /* + * Currently we require that all controllers be bound together + */ + if (!found_mnt) + ret = ECGROUPNOTMOUNTED; + else if (found_mnt > 1) + ret = ECGROUPMULTIMOUNTED; + else { + /* + * NOTE: FILENAME_MAX ensures that we don't need to worry + * about crossing MOUNT_POINT size. For the paranoid, yes + * this is a potential security hole - Balbir + * Dhaval - fix these things. + */ + strcpy(MOUNT_POINT, found_ent->mnt_dir); + strcat(MOUNT_POINT, "/"); + } + + fclose(proc_mount); + return ret; } static int cg_test_mounted_fs() @@ -67,6 +157,7 @@ static int cg_test_mounted_fs() if (ent == NULL) return 0; } + fclose(proc_mount); return 1; } @@ -75,21 +166,33 @@ static inline pid_t cg_gettid() return syscall(__NR_gettid); } -/* +static char* cg_build_path(char *name, char *path) +{ + strcpy(path, MOUNT_POINT); + strcat(path, name); + strcat(path, "/"); + return path; +} + +/** cg_attach_task_pid is used to assign tasks to a cgroup. + * struct cgroup *cgroup: The cgroup to assign the thread to. + * pid_t tid: The thread to be assigned to the cgroup. + * + * returns 0 on success. + * returns ECGROUPNOTOWNER if the caller does not have access to the cgroup. + * returns ECGROUPNOTALLOWED for other causes of failure. */ -int cg_attach_task_pid(char *cgroup, pid_t tid) +int cg_attach_task_pid(struct cgroup *cgroup, pid_t tid) { char path[FILENAME_MAX]; FILE *tasks; - if (cgroup == NULL) { - cgroup = (char *) malloc(sizeof(char)); - cgroup = "\0"; + if (cgroup == NULL) + strcpy(path, MOUNT_POINT); + else { + cg_build_path(cgroup->name, path); } - strcpy(path, MOUNT_POINT); - strcat(path, "/"); - strcat(path, cgroup); strcat(path, "/tasks"); tasks = fopen(path, "w"); @@ -102,17 +205,18 @@ int cg_attach_task_pid(char *cgroup, pid_t tid) } } fprintf(tasks, "%d", tid); + fclose(tasks); return 0; } -/* - * Used to attach the task to a control group. +/** cg_attach_task is used to attach the current thread to a cgroup. + * struct cgroup *cgroup: The cgroup to assign the current thread to. * - * WARNING: Will change to use struct cgroup when it is implemented. + * See cg_attach_task_pid for return values. */ -int cg_attach_task(char *cgroup) +int cg_attach_task(struct cgroup *cgroup) { pid_t tid = cg_gettid(); int error; @@ -133,8 +237,8 @@ static int cg_create_control_group(char *path) int error; if (!cg_test_mounted_fs()) return ECGROUPNOTMOUNTED; - error = mkdir(path, 0700); - if (!error) { + error = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (error) { switch(errno) { case EPERM: return ECGROUPNOTOWNER; @@ -184,54 +288,48 @@ static int cg_set_control_value(char *path, char *val) if (errno == ENOENT) return ECGROUPSUBSYSNOTMOUNTED; } + fclose(control_file); return ECGROUPNOTALLOWED; } + return errno; } fprintf(control_file, "%s", val); + fclose(control_file); return 0; } -/* - * WARNING: This API is not final. It WILL change format to use - * struct cgroup. This API will then become internal and be called something - * else. - * - * I am still not happy with how the data structure is looking at the moment, - * plus there are a couple of additional details to be worked out. Please - * do not rely on this API. - * - * Be prepared to change the implementation later once it shifts to - * struct cgroup in the real alpha release. +/** cg_modify_cgroup modifies the cgroup control files. + * struct cgroup *cgroup: The name will be the cgroup to be modified. + * The values will be the values to be modified, those not mentioned + * in the structure will not be modified. * - * The final version is expected to be + * The uids cannot be modified yet. * - * int modify_cgroup(struct cgroup *original, struct cgroup *final); + * returns 0 on success. * - * where original is the cgroup which is to be modified and final is how it - * should look. - * - * Also this version is still at one level since we do not have - * multi-hierarchy support in kernel. The real alpha release should have this - * issue sorted out as well. */ -int cg_modify_cgroup(char *cgroup, struct control_value *values[], int n) +int cg_modify_cgroup(struct cgroup *cgroup) { char path[FILENAME_MAX], base[FILENAME_MAX]; int i; int error; - strcpy(base, MOUNT_POINT); - strcat(base, "/"); - strcat(base, cgroup); - strcat(base, "/"); - - for (i = 0; i < n; i++, strcpy(path, base)) { - strcat(path, values[i]->name); - error = cg_set_control_value(path, values[i]->value); - if (error) - goto err; + cg_build_path(cgroup->name, base); + + for (i = 0; i < CG_CONTROLLER_MAX && cgroup->controller[i]; + i++, strcpy(path, base)) { + int j; + for(j = 0; j < CG_NV_MAX && + cgroup->controller[i]->values[j]; + j++, strcpy(path, base)) { + strcat(path, cgroup->controller[i]->values[j]->name); + error = cg_set_control_value(path, + cgroup->controller[i]->values[j]->value); + if (error) + goto err; + } } return 0; err: @@ -239,109 +337,106 @@ err: } -/* - * WARNING: This API is not final. It WILL change format to use - * struct cgroup. This API will then become internal and be called something - * else. - * - * I am still not happy with how the data structure is looking at the moment, - * plus there are a couple of additional details to be worked out. Please - * do not rely on this API. +/** create_cgroup creates a new control group. + * struct cgroup *cgroup: The control group to be created * - * Be prepared to change the implementation later once it shifts to - * struct cgroup in the real alpha release. - * - * The final version is expected to be - * - * int create_cgroup(struct cgroup *group); - * - * where group is the group to be created - * - * Also this version is still at one level since we do not have - * multi-hierarchy support in kernel. The real alpha release should have this - * issue sorted out as well. + * returns 0 on success. We recommend calling cg_delete_cgroup + * if this routine fails. That should do the cleanup operation. */ -int cg_create_cgroup(char *cgroup, struct control_value *values[], int n) +int cg_create_cgroup(struct cgroup *cgroup, int ignore_ownership) { - char path[FILENAME_MAX], base[FILENAME_MAX]; + char *fts_path[2], base[FILENAME_MAX], *path; int i; int error; - if (MOUNT_POINT == NULL) - return ECGROUPNOTMOUNTED; - - strcpy(path, MOUNT_POINT); - strcat(path, "/"); - strcat(path, cgroup); + fts_path[0] = (char *)malloc(FILENAME_MAX); + if (!fts_path[0]) + return ENOMEM; + fts_path[1] = NULL; + path = fts_path[0]; + cg_build_path(cgroup->name, path); error = cg_create_control_group(path); - strcat(path, "/"); + if (error) + goto err; + strcpy(base, path); - for (i = 0; i < n; i++, strcpy(path, base)) { - strcat(path, values[i]->name); - error = cg_set_control_value(path, values[i]->value); - if (!error) - return error; + if (!ignore_ownership) + error = cg_chown_recursive(fts_path, cgroup->control_uid, + cgroup->control_gid); + + if (error) + goto err; + + for (i = 0; i < CG_CONTROLLER_MAX && cgroup->controller[i]; + i++, strcpy(path, base)) { + int j; + for(j = 0; j < CG_NV_MAX && cgroup->controller[i]->values[j]; + j++, strcpy(path, base)) { + strcat(path, cgroup->controller[i]->values[j]->name); + error = cg_set_control_value(path, + cgroup->controller[i]->values[j]->value); + + /* + * Should we undo, what we've done in the loops above? + */ + if (error) + goto err; + } + } + + if (!ignore_ownership) { + strcpy(path, base); + strcat(path, "/tasks"); + chown(path, cgroup->tasks_uid, cgroup->tasks_gid); } +err: + free(path); return error; } -/* - * WARNING: This API is not final. It WILL change format to use - * struct cgroup. This API will then become internal and be called something - * else. - * - * I am still not happy with how the data structure is looking at the moment, - * plus there are a couple of additional details to be worked out. Please - * do not rely on this API. - * - * Be prepared to change the implementation later once it shifts to - * struct cgroup in the real alpha release. - * - * The final version is expected to be - * - * int delete_cgroup(struct cgroup *group); - * - * where group is the group to be deleted. +/** cg_delete cgroup deletes a control group. + * struct cgroup *cgroup takes the group which is to be deleted. * - * Also this version is still at one level since we do not have - * multi-hierarchy support in kernel. The real alpha release should have this - * issue sorted out as well. + * returns 0 on success. */ -int cg_delete_cgroup(char *cgroup) +int cg_delete_cgroup(struct cgroup *cgroup, int ignore_migration) { FILE *delete_tasks, *base_tasks; int tids; char path[FILENAME_MAX]; - int error; + int error = ECGROUPNOTALLOWED; strcpy(path, MOUNT_POINT); - strcat(path,"/tasks"); + strcat(path,"tasks"); base_tasks = fopen(path, "w"); + if (!base_tasks) + goto base_open_err; - strcpy(path, MOUNT_POINT); - strcat(path, "/"); - strcat(path, cgroup); - strcat(path,"/tasks"); + cg_build_path(cgroup->name, path); + strcat(path,"tasks"); delete_tasks = fopen(path, "r"); + if (!delete_tasks) + goto del_open_err; while (!feof(delete_tasks)) { fscanf(delete_tasks, "%d", &tids); fprintf(base_tasks, "%d", tids); } - strcpy(path, MOUNT_POINT); - strcat(path, "/"); - strcat(path, cgroup); - + cg_build_path(cgroup->name, path); error = rmdir(path); - if (!error) { - return ECGROUPNOTALLOWED; - } - + fclose(delete_tasks); +del_open_err: + fclose(base_tasks); +base_open_err: + if (ignore_migration) { + cg_build_path(cgroup->name, path); + error = rmdir(path); + } return error; } @@ -21,8 +21,10 @@ __BEGIN_DECLS #include <grp.h> +#include <linux/types.h> #include <stdio.h> #include <sys/stat.h> +#include <unistd.h> #ifndef _GNU_SOURCE #define _GNU_SOURCE @@ -37,14 +39,10 @@ __BEGIN_DECLS /* Estimated number of groups created */ #define MAX_GROUP_ELEMENTS 128 -int verbose; - #ifdef DEBUG -#define dbg(x...) if (verbose) { \ - printf(x); \ - } +#define dbg(x...) printf(x) #else -#define dbg(x...) do { } while(0) +#define dbg(x...) do {} while(0) #endif /* @@ -89,7 +87,6 @@ struct mount_table { /* * 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; @@ -109,7 +106,8 @@ enum cg_errors { ECGROUPNOTCREATED, ECGROUPSUBSYSNOTMOUNTED, ECGROUPNOTOWNER, - ECGROUPNOTALLOWED, // This is the stock error. Default error. + ECGROUPMULTIMOUNTED,/* Controllers bound to different mount points */ + ECGROUPNOTALLOWED, /* This is the stock error. Default error. */ }; #define CG_MAX_MSG_SIZE 256 @@ -139,17 +137,35 @@ int cg_unmount_controllers(void); int cg_load_config(const char *pathname); void cg_unload_current_config(void); +#define CG_NV_MAX 100 +#define CG_CONTROLLER_MAX 100 +#define CG_VALUE_MAX 100 /* Functions and structures that can be used by the application*/ struct control_value { char name[FILENAME_MAX]; - char *value; + char value[CG_VALUE_MAX]; +}; + +struct controller { + char name[FILENAME_MAX]; + struct control_value *values[CG_NV_MAX]; +}; + +struct cgroup { + char name[FILENAME_MAX]; + struct controller *controller[CG_CONTROLLER_MAX]; + uid_t tasks_uid; + gid_t tasks_gid; + uid_t control_uid; + gid_t control_gid; }; int cg_init(void); -int cg_attach_task(char *cgroup); -int cg_modify_cgroup(char *cgroup, struct control_value *values[], int n); -int cg_create_cgroup(char *cgroup, struct control_value *values[], int n); -int cg_delete_cgroup(char *cgroup); +int cg_attach_task(struct cgroup *cgroup); +int cg_modify_cgroup(struct cgroup *cgroup); +int cg_create_cgroup(struct cgroup *cgroup, int ignore_ownership); +int cg_delete_cgroup(struct cgroup *cgroup, int ignore_migration); +int cg_attach_task_pid(struct cgroup *cgroup, pid_t tid); __END_DECLS diff --git a/scripts/README b/scripts/README new file mode 100644 index 0000000..8007b60 --- /dev/null +++ b/scripts/README @@ -0,0 +1,4 @@ +NOTE: These are temporary intermediate scripts, till libcg is up and running +on its own. The current parser works, but has a lot of band-aid around it. +We need a good cleanroom implementation of the parser. Doing so, would +enable us to build a good daemon and a set of init scripts around it. diff --git a/scripts/doc/howto.txt b/scripts/doc/howto.txt new file mode 100644 index 0000000..c6796d6 --- /dev/null +++ b/scripts/doc/howto.txt @@ -0,0 +1,76 @@ +initscripts - Initialization scripts; they are used to initialize the workload +management system. The script consists of two major components + +Configuration files +------------------- + +The main configuraiton file /etc/wlm.conf. This file has a format + +mount <mountpoint> <list of controllers> +<controller> <controller configuration file> + +A sample configuration file is included below + +# +# controller file +# +mount /container cpu +cpu /etc/wlm/cpu.conf + +NOTE: Any line beginning with '#' is ignored as comments. The sample +configuration above, mounts the cpu controller at mount point /container. +It then parses /etc/wlm/cpu.conf as the configuration file for the +cpu controller. + +The controller configuration file is of the format + +<name of the class> <options> + +In the case of the CPU controller a sample configuration would look +like + +class1 cpu.shares=1024 +class2 cpu.shares=512 + +The configuration below creates two classes class1 and class2 and +assigns shares of 1024 to class1 and 512 to class1. + +The other options that can be specified are + +tuid = owner of the tasks file +tgid = group permissions of the tasks file +cuid = owner of the newly created node +cgid = group permissions of the newly created node + +Example + +class1 cpu.shares = 1024 tuid=root tgid=root cuid=database cgid=database. + +By default all these files are owned by root. The flexibilty of specifying +owners makes it easier for other applications to use resource management. + +Intialization script +-------------------- + +The initialization script is installed in /etc/init.d, it is called +"wlm". Depending on the run-level, it is installed in the appropriate +/etc/rc.d/rc<N>.d. The script comes with two options + +a. start + +start, starts the workload management, parses the configuration file. +If required creates the mount point directory and then mounts the +cgroup filesystem with the controllers specified. + +b. stop + +stops workload management, moves all tasks from various groups to +the root class. It then removes all other classes and then unmounts +the workload management system. + +Assumptions +----------- + +1. The kernel is compiled in with the correct options to support + cgroups and the CPU controller. +2. This version has been tested with 2.6.24-rc1 only. diff --git a/scripts/etc/wlm.conf b/scripts/etc/wlm.conf new file mode 100644 index 0000000..01a12b3 --- /dev/null +++ b/scripts/etc/wlm.conf @@ -0,0 +1,16 @@ +# +# Copyright IBM Corporation. 2008 +# +# 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 +# +mount /tmp/container cpu +cpu /etc/wlm/cpu.conf diff --git a/scripts/etc/wlm/cpu.conf b/scripts/etc/wlm/cpu.conf new file mode 100644 index 0000000..10ac391 --- /dev/null +++ b/scripts/etc/wlm/cpu.conf @@ -0,0 +1,14 @@ +# +# Copyright IBM Corporation. 2008 +# +# 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. +# +class1 cpu.shares=1024 tuid=balbir tgid=balbir cuid=root cgid=root +class2 cpu.shares=512 tuid=root tgid=root cuid=balbir cgid=balbir diff --git a/scripts/init.d/wlm b/scripts/init.d/wlm new file mode 100755 index 0000000..d4e1dd7 --- /dev/null +++ b/scripts/init.d/wlm @@ -0,0 +1,256 @@ +# +# Start/Stop the workload manager +# +# Copyright IBM Corporation. 2008 +# +# 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. +# +# +# TODO: Make this code more robust, add error checking and recovery +# for invalid configuration or interrupted execution +# +# Make the script LSB compliant +# + +CONF_FILE=/etc/wlm.conf +PROC_CGROUPS_FILE=/proc/cgroups +CGROUP_FS=cgroup +MOUNT_POINT=/dev/container +#MOUNT_OPTS=cpu +IGNORE_OPTS="ns cpuset" + +parse_controller_file() { + ctlr=$1 + file=$2 + tuid="root" + tgid="root" + cuid="root" + cgid="root" + + pushd $PWD 2>&1 > /dev/null + while read name opts + do + case $name in + 'mount') + ;; + *) + if ! echo $name | grep -q ^# + then + echo "Creating class $name" + class=$name + mkdir -p $MOUNTPOINT/$class + cd $MOUNTPOINT/$class + + if echo $opts | grep -q "=" + then + for single_opt in $opts + do + cf=`echo $single_opt | cut -d '=' -f1` + co=`echo $single_opt | cut -d '=' -f2` + case $cf in + "tuid") + tuid=$co + ;; + "cuid") + cuid=$co + ;; + "tgid") + tgid=$co + ;; + "cgid") + cgid=$co + ;; + *) + echo -n $co > $cf + ;; + esac + done + chown -R $cuid:$cgid $MOUNTPOINT/$class + chown -R $tuid:$tgid $MOUNTPOINT/$class/tasks + fi + fi + esac + done < $file + popd 2>&1 > /dev/null +} + +parse_controller_opts() { + name=$1; + file=$2; + + if [ ! -r $PROC_CGROUPS_FILE ] + then + echo "$PROC_CGROUPS_FILE does not exist, please compile" + echo "cgroups into the kernel" + exit 1 + else + line=`grep -w $name $PROC_CGROUPS_FILE` + if [ $? -ne 0 ] + then + echo "$name controller not enabled" + exit 1 + fi + + active=`echo $line | awk '{print $2}'` + if [[ $active -eq 0 ]] + then + echo "$name controller not mounted" + else + parse_controller_file $name $file + fi + fi +} + +parse_conf_file() { + while read name conf + do + # skip all comments + if ! echo $name | grep -q ^# + then + case $name in + 'mount') + MOUNTPOINT=`echo $conf | cut -d ' ' -f1`; + MOUNTOPTS=`echo $conf | cut -d ' ' -f2`; + ;; + esac + fi + done < $CONF_FILE + + return 0; +} + +mount_fs() { + if [ ! -r $PROC_CGROUPS_FILE ] + then + echo "$PROC_CGROUPS_FILE does not exist, please compile" + echo "cgroups into the kernel" + exit 1 + else + while read name hierarchy num_cgroups + do + if ! echo $name | grep -q ^# + then + echo $IGNORE_OPTS | grep -wq $name + if [[ $? -ne 0 ]] + then + MOUNT_OPTS=$name","$MOUNT_OPTS + fi + fi + done < $PROC_CGROUPS_FILE + MOUNT_OPTS=${MOUNT_OPTS%%","} + #line=`grep -w $MOUNT_OPTS $PROC_CGROUPS_FILE` + #if [ $? -ne 0 ] + #then + # echo "$name controller not enabled" + # exit 1 + #fi + fi + mkdir -p $MOUNTPOINT + mount -t $CGROUP_FS $CGROUP_FS -o $MOUNT_OPTS $MOUNTPOINT + # + # Give root tasks read/write permission to all, since tasks + # tasks will be moved to root frequently + # + chmod ago+rwx $MOUNTPOINT/tasks + chmod ago+rwx $MOUNTPOINT + chmod +t $MOUNTPOINT + return $? +} + +umount_fs() { + umount $MOUNTPOINT + rmdir $MOUNTPOINT +} + +create_classes() { + while read name conf + do + # skip all comments + if ! echo $name | grep -q ^# + then + case $name in + 'mount') + ;; + *) + parse_controller_opts $name $conf + esac + fi + + done < $CONF_FILE +} + +start() { + echo "Starting wlm service: " + mount_fs + if [ $? -eq 0 ] + then + create_classes + fi + [ $? == 0 ] && touch /var/lock/subsys/wlm + return $? +} + +move_all_to_init_class() { + cd $MOUNTPOINT + cat /proc/mounts | grep -wq $MOUNTPOINT + if [ $? -ne 0 ] + then + echo "resource control filesystem not mounted" + exit 1 + fi + + for i in `find . -type d` + do + case $i in + '.') + ;; + *) + class=${i#./*} + echo "Removing class $class" + sed -nu p < ./$i/tasks > tasks + rmdir $i + ;; + esac + done + cd - > /dev/null +} + + +stop() { + move_all_to_init_class + umount_fs +} + +trapped() { + # + # Do nothing + # + true +} + +# +# main script work done here +# +trap "trapped ABRT" ABRT +trap "trapped QUIT" QUIT +trap "trapped TERM" TERM +trap "trapped INT" INT + +parse_conf_file + +case $1 in + 'start') + start; + ;; + 'stop') + stop; + ;; +esac + diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..e2779f6 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,9 @@ +CXXFLAGS = -g -O2 -Wall -DDEBUG +LDFLAGS = +LIBS = -lcg + +libcg_ba: libcg_ba.cpp + $(CXX) $(CXXFLAGS) -o $@ $< $(LIBS) + +clean: + \rm libcg_ba diff --git a/tests/libcg_ba.cpp b/tests/libcg_ba.cpp new file mode 100644 index 0000000..b8df520 --- /dev/null +++ b/tests/libcg_ba.cpp @@ -0,0 +1,125 @@ +/* + * 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. + * + * Basic acceptance test for libcg - Written one late night by Balbir Singh + */ +using namespace std; + +#include <string> +#include <stdexcept> +#include <iostream> +#include <libcg.h> +#include <sys/types.h> +#include <pwd.h> +#include <errno.h> + +namespace cgtest { + +class cg { +private: +public: + cg(); + ~cg() + { } + struct cgroup *makenode(const string &name, const string &task_uid, + const string &task_gid, const string &control_uid, + const string &control_gid); +}; + +cg::cg(void) +{ + int ret; + + ret = cg_init(); + if (ret) + throw logic_error("Control Group Initialization failed..." + "Please check that cgroups are mounted and\n" + "at a single place"); +} + +struct cgroup *cg::makenode(const string &name, const string &task_uid, + const string &task_gid, const string &control_uid, + const string &control_gid) +{ + uid_t tuid, cuid; + gid_t tgid, cgid; + struct cgroup *ccg; + struct passwd *passwd; + struct group *grp; + int ret; + + ccg = (struct cgroup *)malloc(sizeof(*ccg)); + + passwd = getpwnam(task_uid.c_str()); + if (!passwd) + return NULL; + tuid = passwd->pw_uid; + + grp = getgrnam(task_gid.c_str()); + if (!grp) + return NULL; + tgid = grp->gr_gid; + + passwd = getpwnam(control_uid.c_str()); + if (!passwd) + return NULL; + cuid = passwd->pw_uid; + + grp = getgrnam(control_gid.c_str()); + if (!grp) + return NULL; + cgid = grp->gr_gid; + + dbg("tuid %d, tgid %d, cuid %d, cgid %d\n", tuid, tgid, cuid, cgid); + + strcpy(ccg->name, name.c_str()); + ccg->controller[0] = (struct controller *) + calloc(1, sizeof(struct controller)); + strcpy(ccg->controller[0]->name,"cpu"); + + ccg->controller[0]->values[0] = (struct control_value *) + calloc(1, sizeof(struct control_value)); + strcpy(ccg->controller[0]->values[0]->name,"cpu.shares"); + strcpy(ccg->controller[0]->values[0]->value, "100"); + ccg->tasks_uid = tuid; + ccg->tasks_gid = tgid; + ccg->control_uid = cuid; + ccg->control_gid = cgid; + + ret = cg_create_cgroup(ccg, 1); + if (ret) { + cout << "cg create group failed " << errno << endl; + ret = cg_delete_cgroup(ccg, 1); + if (ret) + cout << "cg delete group failed " << errno << endl; + } + return ccg; +} + +} // namespace + +using namespace cgtest; +int main(int argc, char *argv[]) +{ + try { + cg *app = new cg(); + struct cgroup *ccg; + ccg = app->makenode("database", "root", "root", "balbir", + "balbir"); + delete app; + } catch (exception &e) { + cout << e.what() << endl; + exit(1); + } + return 0; +} |