summaryrefslogtreecommitdiffstats
path: root/balbir/api.c
diff options
context:
space:
mode:
authorBalbir Singh <balbir@linux.vnet.ibm.com>2008-04-10 12:12:19 +0000
committerBalbir Singh <balbir@linux.vnet.ibm.com>2008-04-10 12:12:19 +0000
commit0b2fec2a92a3e3ef1c1e15ba71c3945674a01487 (patch)
tree19943247496353b23f960c1ec6ca9137a8060b35 /balbir/api.c
parenteaa689850cd42fe4c9d1a316a746c0a36e11744b (diff)
downloadlibcg-0b2fec2a92a3e3ef1c1e15ba71c3945674a01487.tar.gz
libcg-0b2fec2a92a3e3ef1c1e15ba71c3945674a01487.tar.xz
libcg-0b2fec2a92a3e3ef1c1e15ba71c3945674a01487.zip
Commit to trunk (still untested)
Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com> git-svn-id: https://libcg.svn.sourceforge.net/svnroot/libcg/trunk@10 4f4bb910-9a46-0410-90c8-c897d4f1cd53
Diffstat (limited to 'balbir/api.c')
-rw-r--r--balbir/api.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/balbir/api.c b/balbir/api.c
new file mode 100644
index 0000000..df594ea
--- /dev/null
+++ b/balbir/api.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright IBM Corporation. 2007
+ *
+ * Author: Dhaval Giani <dhaval@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.
+ *
+ * TODOs:
+ * 1. Convert comments to Docbook style.
+ * 2. Add more APIs for the control groups.
+ * 3. Handle the configuration related APIs.
+ * 4. Error handling.
+ *
+ * Code initiated and designed by Dhaval Giani. All faults are most likely
+ * his mistake.
+ */
+
+#include <errno.h>
+#include <libcg.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+static char MOUNT_POINT[FILENAME_MAX];
+
+int cg_init()
+{
+ FILE *proc_mount;
+ struct mntent *ent;
+
+ 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;
+ }
+ strcpy(MOUNT_POINT, ent->mnt_dir);
+ fclose(proc_mount);
+ return 0;
+}
+
+static int cg_test_mounted_fs()
+{
+ FILE *proc_mount;
+ struct mntent *ent;
+
+ proc_mount = fopen("/proc/mounts", "r");
+ if (proc_mount == NULL) {
+ return -1;
+ }
+ ent = getmntent(proc_mount);
+
+ while (strcmp(ent->mnt_fsname, "cgroup") !=0) {
+ ent = getmntent(proc_mount);
+ if (ent == NULL)
+ return 0;
+ }
+ fclose(proc_mount);
+ return 1;
+}
+
+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(struct cgroup *cgroup, pid_t tid)
+{
+ char path[FILENAME_MAX];
+ FILE *tasks;
+
+ 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;
+ }
+ }
+ fprintf(tasks, "%d", tid);
+ fclose(tasks);
+
+ return 0;
+
+}
+
+/** cg_attach_task is used to attach the current thread to a cgroup.
+ * struct cgroup *cgroup: The cgroup to assign the current thread to.
+ *
+ * See cg_attach_task_pid for return values.
+ */
+int cg_attach_task(struct cgroup *cgroup)
+{
+ pid_t tid = cg_gettid();
+ int error;
+
+ error = cg_attach_task_pid(cgroup, tid);
+
+ return error;
+}
+
+/*
+ * create_control_group()
+ * This is the basic function used to create the control group. This function
+ * just makes the group. It does not set any permissions, or any control values.
+ * The argument path is the fully qualified path name to make it generic.
+ */
+static int cg_create_control_group(char *path)
+{
+ int error;
+ if (!cg_test_mounted_fs())
+ return ECGROUPNOTMOUNTED;
+ error = mkdir(path, 0700);
+ if (!error) {
+ switch(errno) {
+ case EPERM:
+ return ECGROUPNOTOWNER;
+ default:
+ return ECGROUPNOTALLOWED;
+ }
+ }
+ return error;
+}
+
+/*
+ * set_control_value()
+ * This is the low level function for putting in a value in a control file.
+ * This function takes in the complete path and sets the value in val in that
+ * file.
+ *
+ * TODO:
+ * At this point I am not sure what all values the control file can take. So
+ * I put in an int arg. But this has to be made much more robust.
+ */
+static int cg_set_control_value(char *path, char *val)
+{
+ int error;
+ FILE *control_file;
+ if (!cg_test_mounted_fs())
+ return ECGROUPNOTMOUNTED;
+
+ control_file = fopen(path, "a");
+
+ if (!control_file) {
+ if (errno == EPERM) {
+ /*
+ * We need to set the correct error value, does the
+ * group exist but we don't have the subsystem
+ * mounted at that point, or is it that the group
+ * does not exist. So we check if the tasks file
+ * exist. Before that, we need to extract the path.
+ */
+ int len = strlen(path);
+
+ while (*(path+len) != '/')
+ len--;
+ *(path+len+1) = '\0';
+ strcat(path, "tasks");
+ control_file = fopen(path, "r");
+ if (!control_file) {
+ if (errno == ENOENT)
+ return ECGROUPSUBSYSNOTMOUNTED;
+ }
+ fclose(control_file);
+ return ECGROUPNOTALLOWED;
+ }
+ }
+
+ fprintf(control_file, "%s", val);
+ fclose(control_file);
+ return 0;
+}
+
+/** 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 uids cannot be modified yet.
+ *
+ * returns 0 on success.
+ *
+ */
+
+int cg_modify_cgroup(struct cgroup *cgroup)
+{
+ char path[FILENAME_MAX], base[FILENAME_MAX];
+ 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;
+ 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:
+ return error;
+
+}
+
+/** create_cgroup creates a new control group.
+ * struct cgroup *cgroup: The control group to be created
+ *
+ * returns 0 on success.
+ */
+int cg_create_cgroup(struct cgroup *cgroup)
+{
+ char path[FILENAME_MAX], base[FILENAME_MAX];
+ int i;
+ int error;
+
+ if (MOUNT_POINT == NULL)
+ return ECGROUPNOTMOUNTED;
+
+ cg_build_path(cgroup->name, path);
+
+ error = cg_create_control_group(path);
+
+ strcpy(base, path);
+
+ 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);
+ chown(path, cgroup->control_uid, cgroup->control_gid);
+ if (!error)
+ return error;
+ }
+ }
+ strcpy(path, base);
+ strcat(path, "tasks");
+ chown(path, cgroup->tasks_uid, cgroup->tasks_gid);
+ return error;
+}
+
+/** cg_delete cgroup deletes a control group.
+ * struct cgroup *cgroup takes the group which is to be deleted.
+ *
+ * returns 0 on success.
+ */
+int cg_delete_cgroup(struct cgroup *cgroup)
+{
+ FILE *delete_tasks, *base_tasks;
+ int tids;
+ char path[FILENAME_MAX];
+ int error;
+
+ strcpy(path, MOUNT_POINT);
+ strcat(path,"/tasks");
+
+ base_tasks = fopen(path, "w");
+
+ cg_build_path(cgroup->name, path);
+ strcat(path,"tasks");
+
+ delete_tasks = fopen(path, "r");
+
+ while (!feof(delete_tasks)) {
+ fscanf(delete_tasks, "%d", &tids);
+ fprintf(base_tasks, "%d", tids);
+ }
+
+ cg_build_path(cgroup->name, path);
+
+ error = rmdir(path);
+
+ if (!error) {
+ return ECGROUPNOTALLOWED;
+ }
+
+ return error;
+}