]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
Several bug fixes, some API enhancements.
authorBalbir Singh <balbir@linux.vnet.ibm.com>
Fri, 11 Apr 2008 19:38:06 +0000 (19:38 +0000)
committerBalbir Singh <balbir@linux.vnet.ibm.com>
Fri, 11 Apr 2008 19:38:06 +0000 (19:38 +0000)
The first basic acceptance test is under development and first
prototype is released

 api.c              |  161 +++++++++++++++++++++++++++++++++++++++++++----------
 libcg.h            |   14 +---
 tests/Makefile     |    9 ++
 tests/libcg_ba.cpp |  127 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 274 insertions(+), 37 deletions(-)

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

api.c
libcg.h
tests/Makefile [new file with mode: 0644]
tests/libcg_ba.cpp [new file with mode: 0644]

diff --git a/api.c b/api.c
index df594ea8efe4e3dd1fc82ce59dc4049fea40ef02..8bf5607d573377582db2a2e8a79bce0f9c8c7e21 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 = 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.
+ */
+static int cg_chown_recursive(const char *path, uid_t owner, gid_t group)
+{
+       int ret = 1;
+       dbg("path is %s\n", path);
+       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;
+}
+
+/**
+ * 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);
 
-       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);
+               }
+       }
+
+       /*
+        * Currently we require that all controllers be bound together
+        */
+       if (!found_mnt)
+               ret = ECGROUPNOTMOUNTED;
+       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, "/");
        }
-       strcpy(MOUNT_POINT, ent->mnt_dir);
+
        fclose(proc_mount);
-       return 0;
+       return ret;
 }
 
 static int cg_test_mounted_fs()
@@ -148,8 +234,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;
@@ -202,6 +288,7 @@ static int cg_set_control_value(char *path, char *val)
                        fclose(control_file);
                        return ECGROUPNOTALLOWED;
                }
+               return errno;
        }
 
        fprintf(control_file, "%s", val);
@@ -250,23 +337,28 @@ err:
 /** create_cgroup creates a new control group.
  * struct cgroup *cgroup: The control group to be created
  *
- * returns 0 on success.
+ * returns 0 on success. We recommend calling cg_delete_cgroup
+ * if this routine fails. That should do the cleanup operation.
  */
 int cg_create_cgroup(struct cgroup *cgroup)
 {
-       char path[FILENAME_MAX], base[FILENAME_MAX];
+       char *path, base[FILENAME_MAX];
        int i;
        int error;
 
-       if (MOUNT_POINT == NULL)
-               return ECGROUPNOTMOUNTED;
+       path = (char *)malloc(FILENAME_MAX);
+       if (!path)
+               return ENOMEM;
 
        cg_build_path(cgroup->name, path);
-
        error = cg_create_control_group(path);
+       if (error)
+               goto err;
 
        strcpy(base, path);
 
+       cg_chown_recursive(path, cgroup->control_uid, cgroup->control_gid);
+
        for (i = 0; i < CG_CONTROLLER_MAX && cgroup->controller[i];
                                                i++, strcpy(path, base)) {
                int j;
@@ -274,15 +366,21 @@ int cg_create_cgroup(struct cgroup *cgroup)
                                                j++, strcpy(path, base)) {
                        strcat(path, cgroup->controller[i]->values[j]->name);
                        error = cg_set_control_value(path,
-                                       cgroup->controller[i]->values[j]->value);
-                       chown(path, cgroup->control_uid, cgroup->control_gid);
-                       if (!error)
-                               return error;
+                               cgroup->controller[i]->values[j]->value);
+
+                       /*
+                        * Should we undo, what we've done in the loops above?
+                        */
+                       if (error)
+                               goto err;
                }
        }
+
        strcpy(path, base);
-       strcat(path, "tasks");
+       strcat(path, "/tasks");
        chown(path, cgroup->tasks_uid, cgroup->tasks_gid);
+err:
+       free(path);
        return error;
 }
 
@@ -291,22 +389,26 @@ int cg_create_cgroup(struct cgroup *cgroup)
  *
  *  returns 0 on success.
  */
-int cg_delete_cgroup(struct cgroup *cgroup)
+int cg_delete_cgroup(struct cgroup *cgroup, int force)
 {
        FILE *delete_tasks, *base_tasks;
        int tids;
        char path[FILENAME_MAX];
-       int error;
+       int error = ECGROUPNOTALLOWED;
 
        strcpy(path, MOUNT_POINT);
        strcat(path,"/tasks");
 
        base_tasks = fopen(path, "w");
+       if (!base_tasks)
+               goto base_open_err;
 
        cg_build_path(cgroup->name, path);
-       strcat(path,"tasks");
+       strcat(path,"/tasks");
 
        delete_tasks = fopen(path, "r");
+       if (!delete_tasks)
+               goto del_open_err;
 
        while (!feof(delete_tasks)) {
                fscanf(delete_tasks, "%d", &tids);
@@ -314,12 +416,15 @@ int cg_delete_cgroup(struct cgroup *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 (force) {
+               cg_build_path(cgroup->name, path);
+               error = rmdir(path);
+       }
        return error;
 }
diff --git a/libcg.h b/libcg.h
index 3c4db67518ac13d42dc660a4f53b4383433b2d0c..5b6f4a24c890f115244e30ad8d94286e8d7cb7af 100644 (file)
--- a/libcg.h
+++ b/libcg.h
@@ -38,14 +38,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
 
 /*
@@ -90,7 +86,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;
@@ -110,7 +105,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
@@ -166,7 +162,7 @@ int cg_init(void);
 int cg_attach_task(struct cgroup *cgroup);
 int cg_modify_cgroup(struct cgroup *cgroup);
 int cg_create_cgroup(struct cgroup *cgroup);
-int cg_delete_cgroup(struct cgroup *cgroup);
+int cg_delete_cgroup(struct cgroup *cgroup, int force);
 
 __END_DECLS
 
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..873f12f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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;
+       int ret;
+
+       ccg = (struct cgroup *)malloc(sizeof(*ccg));
+
+       passwd = getpwnam(task_uid.c_str());
+       if (!passwd)
+               return NULL;
+       tuid = passwd->pw_uid;
+
+       passwd = getpwnam(task_gid.c_str());
+       if (!passwd)
+               return NULL;
+       tgid = passwd->pw_gid;
+
+       passwd = getpwnam(control_uid.c_str());
+       if (!passwd)
+               return NULL;
+       cuid = passwd->pw_uid;
+
+       passwd = getpwnam(control_gid.c_str());
+       if (!passwd)
+               return NULL;
+       cgid = passwd->pw_gid;
+
+       dbg("tuid %d, tgid %d, cuid %d, cgid %d\n", tuid, tgid, cuid, cgid);
+
+       ccg->name = (char *)malloc(strlen(name.c_str()) + 1);
+       strcpy(ccg->name, name.c_str());
+       ccg->controller[0] = (struct controller *)
+                               calloc(1, sizeof(struct controller));
+       ccg->controller[0]->name = (char *)malloc(strlen("cpu") + 1);
+       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");
+       ccg->controller[0]->values[0]->value = (char *)malloc(strlen("100") + 1);
+       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);
+       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;
+}