]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
Merge from Balbir's and Dhaval's branches
authorBalbir Singh <balbir@linux.vnet.ibm.com>
Sun, 11 May 2008 10:15:14 +0000 (10:15 +0000)
committerBalbir Singh <balbir@linux.vnet.ibm.com>
Sun, 11 May 2008 10:15:14 +0000 (10:15 +0000)
 Makefile |   13 ++
 api.c    |  335 ++++++++++++++++++++++++++++++++++++++++-----------------------
 libcg.h  |   42 +++++--
 3 files changed, 255 insertions(+), 135 deletions(-)

Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
git-svn-id: https://libcg.svn.sourceforge.net/svnroot/libcg/trunk@24 4f4bb910-9a46-0410-90c8-c897d4f1cd53

Makefile
api.c
libcg.h
scripts/README [new file with mode: 0644]
scripts/doc/howto.txt [new file with mode: 0644]
scripts/etc/wlm.conf [new file with mode: 0644]
scripts/etc/wlm/cpu.conf [new file with mode: 0644]
scripts/init.d/wlm [new file with mode: 0755]
tests/Makefile [new file with mode: 0644]
tests/libcg_ba.cpp [new file with mode: 0644]

index 686396e32ffc51f53b0eab2ffe06afe6b6c79e44..40737d3a224c914b935b3ac4ab46abfeded0871b 100644 (file)
--- a/Makefile
+++ b/Makefile
 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
diff --git a/api.c b/api.c
index f842040ceca5d83729f80f576b983da65b408969..9f144a307c8811885bdfb40fe23e1e2ae650c04f 100644 (file)
--- a/api.c
+++ b/api.c
@@ -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
 #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;
 }
diff --git a/libcg.h b/libcg.h
index 91496cad6cfebaf5277cf344f6cb0a0e2b647260..7dfe9a49c4e9088c868037009feeae114215100f 100644 (file)
--- a/libcg.h
+++ b/libcg.h
 __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 (file)
index 0000000..8007b60
--- /dev/null
@@ -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 (file)
index 0000000..c6796d6
--- /dev/null
@@ -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 (file)
index 0000000..01a12b3
--- /dev/null
@@ -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 (file)
index 0000000..10ac391
--- /dev/null
@@ -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 (executable)
index 0000000..d4e1dd7
--- /dev/null
@@ -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 (file)
index 0000000..e2779f6
--- /dev/null
@@ -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 (file)
index 0000000..b8df520
--- /dev/null
@@ -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;
+}