From c74b7e334e27928cbfeee489adcc7c25d2efb369 Mon Sep 17 00:00:00 2001 From: Balbir Singh Date: Sat, 17 May 2008 16:06:20 +0000 Subject: Multiple mount point support. Patches built on top of Dhaval'a patches. Works for me on my testing. NOTE: The testing is insufficient as we only test cg_control_create_cgroup() and not delete_cgroup() or attach_task(). Sudhir's new test cases should really help Several coding style fixes and changes, enhancements to make the code work. NOTE: Since this is development release -DDEBUG is back in the makefile. Once this is committed, I'll bump up the version to 0.05 if no one objects. Test report ----------- debug log from the library matched cpuacct:cpuacct Found cgroup option rw,relatime,cpuacct, count 0 matched cpu:cpu Found cgroup option rw,relatime,cpu, count 1 tuid 0, tgid 0, cuid 1000, cgid 1000 path is /tmp/container_cpu/database/ path is /tmp/container_cpuacct/database/ NOTE: The database directory was created as expected on both mount points /tmp/container_cpu and /tmp/container_cpuacct balbir@localhost:~/deliverables/nextgen/libcg/branches/balbir/tests$ ls -al /tmp/container_cpuacct/ total 424 drwxrwxrwt 5 root root 0 2008-05-17 21:27 . drwxrwxrwt 32 root root 425984 2008-05-17 21:26 .. drwxr-xr-x 2 root root 0 2008-05-17 17:09 class1 drwxr-xr-x 2 balbir balbir 0 2008-05-17 17:09 class2 -rw-r--r-- 1 root root 0 2008-05-17 17:09 cpuacct.usage drwxr-xr-x 2 balbir balbir 0 2008-05-17 21:27 database -rw-r--r-- 1 root root 0 2008-05-17 17:09 notify_on_release -rw-r--r-- 1 root root 0 2008-05-17 17:09 releasable -rw-r--r-- 1 root root 0 2008-05-17 17:09 release_agent -rwxrwxrwx 1 root root 0 2008-05-17 17:09 tasks balbir@localhost:~/deliverables/nextgen/libcg/branches/balbir/tests$ ls -al /tmp/container_cpu total 424 drwxrwxrwt 5 root root 0 2008-05-17 21:27 . drwxrwxrwt 32 root root 425984 2008-05-17 21:26 .. drwxr-xr-x 2 root root 0 2008-05-17 17:09 class1 drwxr-xr-x 2 balbir balbir 0 2008-05-17 17:09 class2 -rw-r--r-- 1 root root 0 2008-05-17 17:09 cpu.rt_runtime_us -rw-r--r-- 1 root root 0 2008-05-17 17:09 cpu.shares drwxr-xr-x 2 balbir balbir 0 2008-05-17 21:27 database -rw-r--r-- 1 root root 0 2008-05-17 17:09 notify_on_release -rw-r--r-- 1 root root 0 2008-05-17 17:09 releasable -rw-r--r-- 1 root root 0 2008-05-17 17:09 release_agent -rwxrwxrwx 1 root root 0 2008-05-17 21:10 tasks Signed-off-by: Balbir Singh Signed-off-by: Dhaval Giani git-svn-id: https://libcg.svn.sourceforge.net/svnroot/libcg/branches/balbir@30 4f4bb910-9a46-0410-90c8-c897d4f1cd53 --- Makefile | 2 +- api.c | 313 ++++++++++++++++++++++++++++++++++++----------------- libcg.h | 5 + tests/libcg_ba.cpp | 16 +++ 4 files changed, 233 insertions(+), 103 deletions(-) diff --git a/Makefile b/Makefile index 40737d3..5e19a79 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ YACC_DEBUG=-t DEBUG=-DDEBUG INC=-I. -CFLAGS=-g -O2 -Wextra $(INC) +CFLAGS=-g -O2 -Wextra $(INC) $(DEBUG) LIBS= -lcg LDFLAGS= -L . INSTALLPREFIX= diff --git a/api.c b/api.c index f88be3f..c0c779b 100644 --- a/api.c +++ b/api.c @@ -39,12 +39,6 @@ */ 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; @@ -104,6 +98,38 @@ int cg_init() struct mntent *ent, *found_ent = NULL; int found_mnt = 0; int ret = 0; + char *mntent_tok; + static char *controllers[CG_CONTROLLER_MAX]; + FILE *proc_cgroup; + char subsys_name[FILENAME_MAX]; + int hierarchy, num_cgroups, enabled; + int i=0; + char *mntopt; + int err; + + proc_cgroup = fopen("/proc/cgroups", "r"); + + if (!proc_cgroup) + return EIO; + + /* + * The first line of the file has stuff we are not interested in. + * So just read it and discard the information. + * + * XX: fix the size for fgets + */ + fgets(subsys_name, FILENAME_MAX, proc_cgroup); + while (!feof(proc_cgroup)) { + err = fscanf(proc_cgroup, "%s %d %d %d", subsys_name, + &hierarchy, &num_cgroups, &enabled); + if (err < 0) + break; + controllers[i] = (char *)malloc(strlen(subsys_name)); + strcpy(controllers[i], subsys_name); + i++; + } + controllers[i] = NULL; + fclose(proc_cgroup); proc_mount = fopen("/proc/mounts", "r"); if (proc_mount == NULL) { @@ -111,36 +137,64 @@ int cg_init() } 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); + if (!strncmp(ent->mnt_type, "cgroup", strlen("cgroup"))) { + for (i = 0; controllers[i] != NULL; i++) { + mntopt = hasmntopt(ent, controllers[i]); + if (mntopt && + strcmp(mntopt, controllers[i]) == 0) { + dbg("matched %s:%s\n", mntopt, + controllers[i]); + strcpy(cg_mount_table[found_mnt].name, + controllers[i]); + strcpy(cg_mount_table[found_mnt].path, + ent->mnt_dir); + dbg("Found cgroup option %s, " + " count %d\n", + ent->mnt_opts, found_mnt); + found_mnt++; + } + } } } - /* - * 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, "/"); + if (!found_mnt) { + cg_mount_table[0].name[0] = '\0'; + return ECGROUPNOTMOUNTED; } + found_mnt++; + cg_mount_table[found_mnt].name[0] = '\0'; + + fclose(proc_mount); return ret; } +static char **get_mounted_controllers(char *mountpoint) +{ + char **controllers; + int i, j; + + i = 0; + j = 0; + + controllers = (char **) malloc(sizeof(char *) * CG_CONTROLLER_MAX); + + for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name != NULL; + i++) { + if (strcmp(cg_mount_table[i].name, mountpoint) == 0) { + controllers[j] = (char *)malloc(sizeof(char) * + FILENAME_MAX); + strcpy(controllers[j], cg_mount_table[i].name); + j++; + } + } + controllers[j] = (char *)malloc(sizeof(char) * FILENAME_MAX); + controllers[j][0] = '\0'; + + return controllers; +} + static int cg_test_mounted_fs() { FILE *proc_mount; @@ -152,7 +206,7 @@ static int cg_test_mounted_fs() } ent = getmntent(proc_mount); - while (strcmp(ent->mnt_fsname, "cgroup") !=0) { + while (strcmp(ent->mnt_type, "cgroup") !=0) { ent = getmntent(proc_mount); if (ent == NULL) return 0; @@ -166,12 +220,19 @@ static inline pid_t cg_gettid() return syscall(__NR_gettid); } -static char* cg_build_path(char *name, char *path) +static char* cg_build_path(char *name, char *path, char *type) { - strcpy(path, MOUNT_POINT); - strcat(path, name); - strcat(path, "/"); - return path; + int i; + for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) { + if (strcmp(cg_mount_table[i].name, type) == 0) { + strcpy(path, cg_mount_table[i].path); + strcat(path, "/"); + strcat(path, name); + strcat(path, "/"); + return path; + } + } + return NULL; } /** cg_attach_task_pid is used to assign tasks to a cgroup. @@ -186,27 +247,50 @@ int cg_attach_task_pid(struct cgroup *cgroup, pid_t tid) { char path[FILENAME_MAX]; FILE *tasks; + int i; - if (cgroup == NULL) - strcpy(path, MOUNT_POINT); - else { - cg_build_path(cgroup->name, path); - } - - strcat(path, "/tasks"); - - tasks = fopen(path, "w"); - if (!tasks) { - switch (errno) { - case EPERM: - return ECGROUPNOTOWNER; - default: - return ECGROUPNOTALLOWED; + if(!cgroup) + { + for(i = 0; i < CG_CONTROLLER_MAX && + cg_mount_table[i].name[0]!='\0'; i++) { + if (!cg_build_path(cgroup->name, path, NULL)) + continue; + strcat(path, "/tasks"); + + tasks = fopen(path, "w"); + if (!tasks) { + switch (errno) { + case EPERM: + return ECGROUPNOTOWNER; + default: + return ECGROUPNOTALLOWED; + } + } + fprintf(tasks, "%d", tid); + fclose(tasks); + } + } else { + for( i = 0; i <= CG_CONTROLLER_MAX && + cgroup->controller[i] != NULL ; i++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + + strcat(path, "/tasks"); + + tasks = fopen(path, "w"); + if (!tasks) { + switch (errno) { + case EPERM: + return ECGROUPNOTOWNER; + default: + return ECGROUPNOTALLOWED; + } + } + fprintf(tasks, "%d", tid); + fclose(tasks); } } - fprintf(tasks, "%d", tid); - fclose(tasks); - return 0; } @@ -316,11 +400,12 @@ int cg_modify_cgroup(struct cgroup *cgroup) int i; int error; - cg_build_path(cgroup->name, base); - for (i = 0; i < CG_CONTROLLER_MAX && cgroup->controller[i]; i++, strcpy(path, base)) { int j; + if (!cg_build_path(cgroup->name, base, + cgroup->controller[i]->name)) + continue; for(j = 0; j < CG_NV_MAX && cgroup->controller[i]->values[j]; j++, strcpy(path, base)) { strcat(path, cgroup->controller[i]->values[j]->name); @@ -345,8 +430,8 @@ err: int cg_create_cgroup(struct cgroup *cgroup, int ignore_ownership) { char *fts_path[2], base[FILENAME_MAX], *path; - int i; - int error; + int i, j, k; + int error = 0; fts_path[0] = (char *)malloc(FILENAME_MAX); if (!fts_path[0]) @@ -354,40 +439,49 @@ int cg_create_cgroup(struct cgroup *cgroup, int ignore_ownership) fts_path[1] = NULL; path = fts_path[0]; - cg_build_path(cgroup->name, path); - error = cg_create_control_group(path); - if (error) - goto err; + /* + * XX: One important test to be done is to check, if you have multiple + * subsystems mounted at one point, all of them *have* be on the cgroup + * data structure. If not, we fail. + */ + for (k = 0; k < CG_CONTROLLER_MAX && cgroup->controller[k]; k++) { + path[0] = '\0'; - strcpy(base, path); + if (!cg_build_path(cgroup->name, path, + cgroup->controller[k]->name)) + continue; - if (!ignore_ownership) - cg_chown_recursive(fts_path, cgroup->control_uid, - cgroup->control_gid); - if (error) - goto err; + dbg("path is %s\n", path); + error = cg_create_control_group(path); + 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); + strcpy(base, path); + + if (!ignore_ownership) + error = cg_chown_recursive(fts_path, + cgroup->control_uid, cgroup->control_gid); + if (error) + goto err; + + for (j = 0; j < CG_NV_MAX && cgroup->controller[k]->values[j]; + j++, strcpy(path, base)) { + strcat(path, cgroup->controller[k]->values[j]->name); + error = cg_set_control_value(path, + cgroup->controller[k]->values[j]->value); /* - * Should we undo, what we've done in the loops above? - */ + * 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); + + if (!ignore_ownership) { + strcpy(path, base); + strcat(path, "/tasks"); + chown(path, cgroup->tasks_uid, cgroup->tasks_gid); + } } err: free(path); @@ -401,40 +495,55 @@ err: */ int cg_delete_cgroup(struct cgroup *cgroup, int ignore_migration) { - FILE *delete_tasks, *base_tasks; + FILE *delete_tasks, *base_tasks = NULL; int tids; char path[FILENAME_MAX]; int error = ECGROUPNOTALLOWED; + int i; - strcpy(path, MOUNT_POINT); - strcat(path,"tasks"); + for (i = 0; i < CG_CONTROLLER_MAX && cgroup->controller; i++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + strcat(path, "../tasks"); - base_tasks = fopen(path, "w"); - if (!base_tasks) - goto base_open_err; + base_tasks = fopen(path, "w"); + if (!base_tasks) + goto base_open_err; - cg_build_path(cgroup->name, path); - strcat(path,"tasks"); + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; - delete_tasks = fopen(path, "r"); - if (!delete_tasks) - goto del_open_err; + strcat(path, "tasks"); - while (!feof(delete_tasks)) { - fscanf(delete_tasks, "%d", &tids); - fprintf(base_tasks, "%d", tids); - } + 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); + } - cg_build_path(cgroup->name, path); - error = rmdir(path); + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + error = rmdir(path); - fclose(delete_tasks); + fclose(delete_tasks); + } del_open_err: - fclose(base_tasks); + if (base_tasks) + fclose(base_tasks); base_open_err: if (ignore_migration) { - cg_build_path(cgroup->name, path); - error = rmdir(path); + for (i = 0; cgroup->controller[i] != NULL; i++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + error = rmdir(path); + } } return error; } diff --git a/libcg.h b/libcg.h index 7dfe9a4..c807cfa 100644 --- a/libcg.h +++ b/libcg.h @@ -160,6 +160,11 @@ struct cgroup { gid_t control_gid; }; +struct cg_mount_table_s { + char name[FILENAME_MAX]; + char path[FILENAME_MAX]; +} cg_mount_table[CG_CONTROLLER_MAX]; + int cg_init(void); int cg_attach_task(struct cgroup *cgroup); int cg_modify_cgroup(struct cgroup *cgroup); diff --git a/tests/libcg_ba.cpp b/tests/libcg_ba.cpp index b8df520..967b1ff 100644 --- a/tests/libcg_ba.cpp +++ b/tests/libcg_ba.cpp @@ -22,6 +22,8 @@ using namespace std; #include #include #include +#include +#include namespace cgtest { @@ -91,6 +93,20 @@ struct cgroup *cg::makenode(const string &name, const string &task_uid, calloc(1, sizeof(struct control_value)); strcpy(ccg->controller[0]->values[0]->name,"cpu.shares"); strcpy(ccg->controller[0]->values[0]->value, "100"); + + ccg->controller[1] = (struct controller *) + calloc(1, sizeof(struct controller)); + strcpy(ccg->controller[1]->name, "memory"); + ccg->controller[1]->values[0] = (struct control_value *) + calloc(1, sizeof(struct control_value)); + strcpy(ccg->controller[1]->values[0]->name,"memory.limit_in_bytes"); + strcpy(ccg->controller[1]->values[0]->value, "102400"); + + strcpy(ccg->controller[2]->name, "cpuacct"); + ccg->controller[2]->values[0] = (struct control_value *) + calloc(1, sizeof(struct control_value)); + strcpy(ccg->controller[2]->values[0]->name,"cpuacct.usage"); + strcpy(ccg->controller[2]->values[0]->value, "0"); ccg->tasks_uid = tuid; ccg->tasks_gid = tgid; ccg->control_uid = cuid; -- cgit