diff options
-rw-r--r-- | api.c | 161 | ||||
-rw-r--r-- | libcg.h | 14 | ||||
-rw-r--r-- | tests/Makefile | 9 | ||||
-rw-r--r-- | tests/libcg_ba.cpp | 127 |
4 files changed, 274 insertions, 37 deletions
@@ -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,25 +32,110 @@ #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; } @@ -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 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..873f12f --- /dev/null +++ b/tests/libcg_ba.cpp @@ -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; +} |