This patch puts the test functions in a separate file so that they can be reused for further testing. It also edits the Makefile so that the patch compiles.
Signed-off-by: Sudhir Kumar <[email protected]> --- tests/Makefile | 19 - tests/functions.c | 727 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/libcgrouptest.h | 19 - tests/libcgrouptest01.c | 706 ---------------------------------------------- 4 files changed, 749 insertions(+), 722 deletions(-) Index: trunk/tests/functions.c =================================================================== --- /dev/null +++ trunk/tests/functions.c @@ -0,0 +1,727 @@ +/* + * Copyright IBM Corporation. 2008 + * + * Author: Sudhir Kumar <[email protected]> + * + * 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. + * + * Description: This file contains the functions for testing libcgroup apis. + */ + +#include "libcgrouptest.h" + +/* The messages that may be useful to the user */ +char info[NUM_MSGS][SIZE] = { + " Parameter nullcgroup\n", /* NULLGRP */ + " Parameter commoncgroup\n", /* COMMONGRP */ + " Parameter not created group\n", /* NOTCRTDGRP */ + " Parameter same cgroup\n", /* SAMEGRP */ + " Task found in group/s\n", /* TASKINGRP */ + " Task not found in group/s\n", /* TASKNOTINGRP */ + " Task not found in all groups\n", /* TASKNOTINANYGRP */ + " group found in filesystem\n", /* GRPINFS */ + " group not found in filesystem\n", /* GRPNOTINFS */ + " group found under both controllers\n", /* GRPINBOTHCTLS */ + " group not found under second controller\n", /* GRPNOTIN2NDCTL */ + " group not found under first controller\n", /* GRPNOTIN1STCTL */ + " group modified under both controllers\n", /* GRPMODINBOTHCTLS */ + " group not modified under second controller\n",/* GRPNOTMODIN2NDCTL */ + " group not modified under any controller\n", /* GRPNOTMODINANYCTL */ + " Group deleted from filesystem\n", /* GRPDELETEDINFS */ + " Group not deleted from filesystem\n", /* GRPNOTDELETEDINFS */ + " Group not deleted globally\n", /* GRPNOTDELETEDGLOBALY */ + /* In case there is no extra info messages to be printed */ + "\n", /* NOMESSAGE */ +}; + +void test_cgroup_init(int retcode, int i) +{ + int retval; + char extra[SIZE] = "\n"; + + retval = cgroup_init(); + if (retval == retcode) + message(i, PASS, "init()\t", retval, extra); + else + message(i, FAIL, "init()", retval, extra); +} + +void test_cgroup_attach_task(int retcode, struct cgroup *cgrp, + const char *group1, const char *group2, + int k, int i) +{ + int retval; + char tasksfile[FILENAME_MAX], tasksfile2[FILENAME_MAX]; + /* Check, In case some error is expected due to a negative scenario */ + if (retcode) { + retval = cgroup_attach_task(cgrp); + if (retval == retcode) + message(i, PASS, "attach_task()", retval, info[k]); + else + message(i, FAIL, "attach_task()", retval, info[k]); + + return; + } + + /* Now there is no error and it is a genuine call */ + retval = cgroup_attach_task(cgrp); + if (retval == 0) { /* API returned success, so perform check */ + build_path(tasksfile, mountpoint, + group1, "tasks"); + + if (check_task(tasksfile)) { + if (fs_mounted == 2) { /* multiple mounts */ + build_path(tasksfile2, mountpoint2, + group2, "tasks"); + if (check_task(tasksfile2)) { + message(i, PASS, "attach_task()", + retval, info[TASKINGRP]); + } else { + message(i, FAIL, "attach_task()", + retval, info[TASKNOTINANYGRP]); + } + } else { /* single mount */ + message(i, PASS, "attach_task()", + retval, info[TASKINGRP]); + } + } else { + message(i, FAIL, "attach_task()", retval, + info[TASKNOTINGRP]); + } + } else { + message(i, FAIL, "attach_task()", retval, (char *)"\n"); + } +} + + +struct cgroup *create_new_cgroup_ds(int ctl, const char *grpname, + int value_type, struct cntl_val_t cval, struct uid_gid_t ids, int i) +{ + int retval; + char group[FILENAME_MAX]; + char controller_name[FILENAME_MAX], control_file[FILENAME_MAX]; + + strncpy(group, grpname, sizeof(group)); + retval = set_controller(ctl, controller_name, control_file); + if (retval) { + fprintf(stderr, "Setting controller failled\n"); + return NULL; + } + + switch (ctl) { + /* control values are controller specific, so will be set + * accordingly from the config file */ + case CPU: + strncpy(cval.val_string, "260000", sizeof(cval.val_string)); + break; + + case MEMORY: + strncpy(cval.val_string, "7000064", sizeof(cval.val_string)); + break; + + /* To be added for other controllers */ + default: + printf("Invalid controller name passed. Setting control value" + " failed. Dependent tests may fail\n"); + return NULL; + break; + } + + return new_cgroup(group, controller_name, control_file, + value_type, cval, ids, i); +} + + +void test_cgroup_create_cgroup(int retcode, struct cgroup *cgrp, + const char *name, int common, int mpnt, int ign, int i) +{ + int retval; + char path1_group[FILENAME_MAX], path2_group[FILENAME_MAX]; + /* Check, In case some error is expected due to a negative scenario */ + if (retcode) { + retval = cgroup_create_cgroup(cgrp, ign); + if (retval == retcode) + message(i, PASS, "create_cgroup()", retval, + info[NOMESSAGE]); + else + message(i, FAIL, "create_cgroup()", retval, + info[NOMESSAGE]); + + return; + } + + /* Now there is no error and it is a genuine call */ + retval = cgroup_create_cgroup(cgrp, ign); + if (retval) { + message(i, FAIL, "create_cgroup()", retval, info[NOMESSAGE]); + return; + } + + /* Let us now check if the group exist in file system */ + if (!common) { /* group only under one mountpoint */ + if (mpnt == 1) /* group under mountpoint */ + build_path(path1_group, mountpoint, name, NULL); + else /* group under mountpoint2 */ + build_path(path1_group, mountpoint2, name, NULL); + + if (group_exist(path1_group) == 0) + message(i, PASS, "create_cgroup()", retval, + info[GRPINFS]); + else + message(i, FAIL, "create_cgroup()", retval, + info[GRPNOTINFS]); + + } else { /* group under both mountpoints */ + /* Check if the group exists under both controllers */ + build_path(path1_group, mountpoint, name, NULL); + if (group_exist(path1_group) == 0) { + build_path(path2_group, mountpoint2, name, NULL); + + if (group_exist(path2_group) == 0) + message(i, PASS, "create_cgroup()", + retval, info[GRPINBOTHCTLS]); + else + message(i, FAIL, "create_cgroup()", + retval, info[GRPNOTIN2NDCTL]); + } else { + message(i, FAIL, "create_cgroup()", retval, + info[GRPNOTIN1STCTL]); + } + } + + return; +} + +void test_cgroup_delete_cgroup(int retcode, struct cgroup *cgrp, + const char *name, int common, int mpnt, int ign, int i) +{ + int retval; + char path1_group[FILENAME_MAX], path2_group[FILENAME_MAX]; + /* Check, In case some error is expected due to a negative scenario */ + if (retcode) { + retval = cgroup_delete_cgroup(cgrp, ign); + if (retval == retcode) + message(i, PASS, "delete_cgroup()", retval, + info[NOMESSAGE]); + else + message(i, FAIL, "delete_cgroup()", retval, + info[NOMESSAGE]); + + return; + } + + /* Now there is no error and it is a genuine call */ + retval = cgroup_delete_cgroup(cgrp, ign); + if (retval) { + message(i, FAIL, "delete_cgroup()", retval, info[NOMESSAGE]); + return; + } + + /* Let us now check if the group has been deleted from file system */ + if (!common) { /* check only under one mountpoint */ + if (mpnt == 1) /* check group under mountpoint */ + build_path(path1_group, mountpoint, name, NULL); + else /* check group under mountpoint2 */ + build_path(path1_group, mountpoint2, name, NULL); + + if (group_exist(path1_group) == -1) + message(i, PASS, "delete_cgroup()", retval, + info[GRPDELETEDINFS]); + else + message(i, FAIL, "delete_cgroup()", retval, + info[GRPNOTDELETEDINFS]); + + } else { /* check group under both mountpoints */ + /* Check if the group deleted under both controllers */ + build_path(path1_group, mountpoint, name, NULL); + if (group_exist(path1_group) == -1) { + build_path(path2_group, mountpoint2, name, NULL); + + if (group_exist(path2_group) == -1) + message(i, PASS, "delete_cgroup()", + retval, info[GRPDELETEDINFS]); + else + message(i, FAIL, "delete_cgroup()", + retval, info[GRPNOTDELETEDGLOBALY]); + } else { + message(i, FAIL, "delete_cgroup()", retval, + info[GRPNOTDELETEDINFS]); + } + } + +} + +void get_controllers(const char *name, int *exist) +{ + int hierarchy, num_cgroups, enabled; + FILE *fd; + char subsys_name[FILENAME_MAX]; + fd = fopen("/proc/cgroups", "r"); + if (!fd) + return; + + while (!feof(fd)) { + fscanf(fd, "%s, %d, %d, %d", subsys_name, + &hierarchy, &num_cgroups, &enabled); + if (strncmp(name, subsys_name, sizeof(*name)) == 0) + *exist = 1; + } +} + +int group_exist(char *path_group) +{ + int ret; + ret = open(path_group, O_DIRECTORY); + if (ret == -1) + return ret; + return 0; +} + +int set_controller(int controller, char *controller_name, + char *control_file) +{ + switch (controller) { + case MEMORY: + if (memory == 0) + return 1; + + strncpy(controller_name, "memory", FILENAME_MAX); + strncpy(control_file, "memory.limit_in_bytes", FILENAME_MAX); + return 0; + break; + + case CPU: + if (cpu == 0) + return 1; + + strncpy(controller_name, "cpu", FILENAME_MAX); + strncpy(control_file, "cpu.shares", FILENAME_MAX); + return 0; + break; + + case CPUSET: + strncpy(controller_name, "cpuset", FILENAME_MAX); + /* What is the exact control file?? */ + strncpy(control_file, "cpuset.mem_exclusive", FILENAME_MAX); + return 0; + break; + /* Future controllers can be added here */ + + default: + printf("Invalid controller name passed. Setting controller" + " failed. Dependent tests may fail\n"); + return 1; + break; + } +} + +int group_modified(char *path_control_file, int value_type, + struct cntl_val_t cval) +{ + bool bool_val; + int64_t int64_val; + u_int64_t uint64_val; + char string_val[FILENAME_MAX]; /* Doubt: what should be the size ? */ + FILE *fd; + + fd = fopen(path_control_file, "r"); + if (!fd) { + fprintf(stderr, "Error in opening %s\n", path_control_file); + fprintf(stderr, "Skipping modified values check....\n"); + return 1; + } + + switch (value_type) { + + case BOOL: + fscanf(fd, "%d", &bool_val); + if (bool_val == cval.val_bool) + return 0; + break; + case INT64: + fscanf(fd, "%lld", &int64_val); + if (int64_val == cval.val_int64) + return 0; + break; + case UINT64: + fscanf(fd, "%llu", &uint64_val); + if (uint64_val == cval.val_uint64) + return 0; + break; + case STRING: + fscanf(fd, "%s", string_val); + if (!strncmp(string_val, cval.val_string, strlen(string_val))) + return 0; + break; + default: + fprintf(stderr, "Wrong value_type passed " + "in group_modified()\n"); + fprintf(stderr, "Skipping modified values check....\n"); + return 0; /* Can not report test result as failure */ + break; + } + return 1; +} +int add_control_value(struct cgroup_controller *newcontroller, + char *control_file, char *wr, int value_type, struct cntl_val_t cval) +{ + int retval; + + switch (value_type) { + + case BOOL: + retval = cgroup_add_value_bool(newcontroller, + control_file, cval.val_bool); + snprintf(wr, SIZE, "add_value_bool()"); + break; + case INT64: + retval = cgroup_add_value_int64(newcontroller, + control_file, cval.val_int64); + snprintf(wr, SIZE, "add_value_int64()"); + break; + case UINT64: + retval = cgroup_add_value_uint64(newcontroller, + control_file, cval.val_uint64); + snprintf(wr, SIZE, "add_value_uint64()"); + break; + case STRING: + retval = cgroup_add_value_string(newcontroller, + control_file, cval.val_string); + snprintf(wr, SIZE, "add_value_string()"); + break; + default: + printf("ERROR: wrong value in add_control_value()\n"); + return 1; + break; + } + return retval; +} + +struct cgroup *new_cgroup(char *group, char *controller_name, + char *control_file, int value_type, + struct cntl_val_t cval, struct uid_gid_t ids, int i) +{ + int retval; + char wr[SIZE]; /* Names of wrapper apis */ + struct cgroup *newcgroup; + struct cgroup_controller *newcontroller; + + newcgroup = cgroup_new_cgroup(group); + + if (newcgroup) { + retval = cgroup_set_uid_gid(newcgroup, ids.tasks_uid, + ids.tasks_gid, ids.control_uid, ids.control_gid); + + if (retval) { + snprintf(wr, SIZE, "set_uid_gid()"); + message(i++, FAIL, wr, retval, extra); + } + + newcontroller = cgroup_add_controller(newcgroup, + controller_name); + if (newcontroller) { + retval = add_control_value(newcontroller, + control_file, wr, value_type, cval); + + if (!retval) { + message(i++, PASS, "new_cgroup()", + retval, extra); + } else { + message(i++, FAIL, wr, retval, extra); + return NULL; + } + } else { + /* Since these wrappers do not return an int so -1 */ + message(i++, FAIL, "add_controller", -1, extra); + return NULL; + } + } else { + message(i++, FAIL, "new_cgroup", -1, extra); + return NULL; + } + return newcgroup; +} + +int check_fsmounted(int multimnt) +{ + int count = 0; + struct mntent *entry, *tmp_entry; + /* Need a better mechanism to decide memory allocation size here */ + char entry_buffer[FILENAME_MAX * 4]; + FILE *proc_file; + + tmp_entry = (struct mntent *) malloc(sizeof(struct mntent)); + if (!tmp_entry) { + perror("Error: failled to mallloc for mntent\n"); + return 1; + } + + proc_file = fopen("/proc/mounts", "r"); + if (!proc_file) { + printf("Error in opening /proc/mounts.\n"); + return EIO; + } + while ((entry = getmntent_r(proc_file, tmp_entry, entry_buffer, + FILENAME_MAX*4)) != NULL) { + if (!strncmp(entry->mnt_type, "cgroup", strlen("cgroup"))) { + count++; + if (multimnt) { + if (count >= 2) { + printf("sanity check pass. %s\n", + entry->mnt_type); + return 0; + } + } else { + printf("sanity check pass. %s\n", + entry->mnt_type); + return 0; + } + } + } + return 1; +} + +int check_task(char *tasksfile) +{ + FILE *file; + pid_t curr_tid, tid; + int pass = 0; + + file = fopen(tasksfile, "r"); + if (!file) { + printf("ERROR: in opening %s\n", tasksfile); + printf("Exiting without running other testcases in this set\n"); + exit(1); + } + + curr_tid = cgrouptest_gettid(); + while (!feof(file)) { + fscanf(file, "%u", &tid); + if (tid == curr_tid) { + pass = 1; + break; + } + } + + return pass; +} + +void message(int num, int pass, const char *api, + int retval, char *extra) +{ + char res[10]; + char buf[2*SIZE]; + if (pass) + strncpy(res, "PASS :", 10); + else + strncpy(res, "FAIL :", 10); + + /* Populate message buffer for the api */ + snprintf(buf, sizeof(buf), "cgroup_%s\t\t Ret Value = ", api); + fprintf(stdout, "TEST%2d:%s %s%d\t%s", num, res, buf, retval, extra); +} + +/* builds the path to target file/group */ +void build_path(char *target, char *mountpoint, + const char *group, const char *file) +{ + strncpy(target, mountpoint, FILENAME_MAX); + + if (group) { + strncat(target, "/", FILENAME_MAX); + strncat(target, group, FILENAME_MAX); + } + + if (file) { + strncat(target, "/", FILENAME_MAX); + strncat(target, file, FILENAME_MAX); + } +} + +void test_cgroup_compare_cgroup(int ctl1, int ctl2, int i) +{ + int retval; + struct cntl_val_t cval = {0, 0, 0, "5000"}; + struct cgroup *cgroup1, *cgroup2; + struct cgroup_controller *controller; + char controller_name[FILENAME_MAX], control_file[FILENAME_MAX]; + char wr[SIZE], extra[] = "in cgroup_compare_cgroup"; + + retval = cgroup_compare_cgroup(NULL, NULL); + if (retval) + message(i++, PASS, "compare_cgroup()", retval, info[NULLGRP]); + else + message(i++, FAIL, "compare_cgroup()", retval, info[NULLGRP]); + + cgroup1 = cgroup_new_cgroup("testgroup"); + cgroup2 = cgroup_new_cgroup("testgroup"); + cgroup_set_uid_gid(cgroup1, 0, 0, 0, 0); + cgroup_set_uid_gid(cgroup2, 0, 0, 0, 0); + + retval = set_controller(ctl1, controller_name, control_file); + + controller = cgroup_add_controller(cgroup1, controller_name); + if (controller) { + retval = add_control_value(controller, + control_file, wr, STRING, cval); + if (retval) + message(i++, FAIL, wr, retval, extra); + } + + controller = cgroup_add_controller(cgroup2, controller_name); + if (controller) { + retval = add_control_value(controller, + control_file, wr, STRING, cval); + if (retval) + message(i++, FAIL, wr, retval, extra); + } + + retval = cgroup_compare_cgroup(cgroup1, cgroup2); + if (retval) + message(i++, FAIL, "compare_cgroup()", retval, info[NOMESSAGE]); + else + message(i++, PASS, "compare_cgroup()", retval, info[NOMESSAGE]); + + /* Test the api by putting diff number of controllers in cgroups */ + retval = set_controller(ctl2, controller_name, control_file); + controller = cgroup_add_controller(cgroup2, controller_name); + if (controller) { + retval = add_control_value(controller, + control_file, wr, STRING, cval); + if (retval) + message(i++, FAIL, wr, retval, extra); + } + + retval = cgroup_compare_cgroup(cgroup1, cgroup2); + if (retval == ECGROUPNOTEQUAL) + message(i++, PASS, "compare_cgroup()", retval, info[NOMESSAGE]); + else + message(i++, FAIL, "compare_cgroup()", retval, info[NOMESSAGE]); + + cgroup_free(&cgroup1); + cgroup_free(&cgroup2); +} + +void test_cgroup_get_cgroup(int ctl1, int ctl2, struct uid_gid_t ids, int i) +{ + struct cgroup *cgroup_filled, *cgroup_a, *cgroup_b; + struct cgroup_controller *controller; + char controller_name[FILENAME_MAX], control_file[FILENAME_MAX]; + struct cntl_val_t cval = {0, 0, 0, "5000"}; + int ret; + + /* + * No need to test the next 3 scenarios again for Multimnt + * so testing them only under single mount + */ + if (fs_mounted == FS_MOUNTED) { + /* 1. Test with nullcgroup first */ + ret = cgroup_get_cgroup(NULL); + if (ret == ECGROUPNOTALLOWED) + message(i++, PASS, "get_cgroup()", ret, info[NULLGRP]); + else + message(i++, FAIL, "get_cgroup()", ret, info[NULLGRP]); + + /* 2. Test with invalid name filled cgroup(non existing) */ + cgroup_filled = cgroup_new_cgroup("nogroup"); + if (!cgroup_filled) + message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]); + + ret = cgroup_get_cgroup(cgroup_filled); + if (ret) + message(i++, PASS, "get_cgroup()", ret, + info[NOTCRTDGRP]); + else + message(i++, FAIL, "get_cgroup()", ret, + info[NOTCRTDGRP]); + + /* 3. + * Test with name filled cgroup. Ensure the group group1 exists + * in the filesystem before calling this test function + */ + cgroup_filled = cgroup_new_cgroup("group1"); + if (!cgroup_filled) + message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]); + + ret = cgroup_get_cgroup(cgroup_filled); + if (!ret) + message(i++, PASS, "get_cgroup()", ret, + info[NOMESSAGE]); + else + message(i++, FAIL, "get_cgroup()", ret, + info[NOMESSAGE]); + } + + /* MULTIMOUNT: Create, get and compare a cgroup under both mounts */ + + /* get cgroup_a ds and create group_a in filesystem */ + cgroup_a = create_new_cgroup_ds(ctl1, "group_a", STRING, cval, ids, 0); + if (fs_mounted == FS_MULTI_MOUNTED) { + /* Create under another controller also */ + ret = set_controller(ctl2, controller_name, control_file); + controller = cgroup_add_controller(cgroup_a, controller_name); + } + test_cgroup_create_cgroup(0, cgroup_a, "group_a", 0, 1, 1, 00); + + /* create group_b ds to be filled by cgroup_get_cgroup */ + cgroup_b = cgroup_new_cgroup("group_a"); + if (!cgroup_b) + message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]); + /* Fill the ds and compare the two */ + ret = cgroup_get_cgroup(cgroup_b); + if (!ret) { + ret = cgroup_compare_cgroup(cgroup_a, cgroup_b); + if (ret == 0) + message(i++, PASS, "get_cgroup()", ret, info[SAMEGRP]); + else + message(i++, FAIL, "get_cgroup()", ret, + info[NOMESSAGE]); + } else { + message(i++, FAIL, "get_cgroup()", ret, info[NOMESSAGE]); + } + + cgroup_free(&cgroup_a); + cgroup_free(&cgroup_b); + cgroup_free(&cgroup_filled); +} + +void test_cgroup_add_free_controller(int i) +{ + struct cgroup *cgroup1 = NULL, *cgroup2 = NULL; + struct cgroup_controller *cgctl1, *cgctl2; + + /* Test with a Null cgroup */ + cgctl1 = cgroup_add_controller(cgroup1, "cpu"); + if (!cgctl1) + message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]); + else + message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]); + + cgroup1 = cgroup_new_cgroup("testgroup"); + cgctl1 = cgroup_add_controller(cgroup1, "cpuset"); + if (cgctl1) + message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]); + else + message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]); + + cgctl2 = cgroup_add_controller(cgroup1, "cpu"); + if (cgctl2) + message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]); + else + message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]); + + cgroup_free(&cgroup1); + cgroup_free_controllers(cgroup2); + +} + +pid_t cgrouptest_gettid() +{ + return syscall(__NR_gettid); +} Index: trunk/tests/libcgrouptest01.c =================================================================== --- trunk.orig/tests/libcgrouptest01.c +++ trunk/tests/libcgrouptest01.c @@ -16,30 +16,6 @@ #include "libcgrouptest.h" -/* The messages that may be useful to the user */ -char info[NUM_MSGS][SIZE] = { - " Parameter nullcgroup\n", /* NULLGRP */ - " Parameter commoncgroup\n", /* COMMONGRP */ - " Parameter not created group\n", /* NOTCRTDGRP */ - " Parameter same cgroup\n", /* SAMEGRP */ - " Task found in group/s\n", /* TASKINGRP */ - " Task not found in group/s\n", /* TASKNOTINGRP */ - " Task not found in all groups\n", /* TASKNOTINANYGRP */ - " group found in filesystem\n", /* GRPINFS */ - " group not found in filesystem\n", /* GRPNOTINFS */ - " group found under both controllers\n", /* GRPINBOTHCTLS */ - " group not found under second controller\n", /* GRPNOTIN2NDCTL */ - " group not found under first controller\n", /* GRPNOTIN1STCTL */ - " group modified under both controllers\n", /* GRPMODINBOTHCTLS */ - " group not modified under second controller\n",/* GRPNOTMODIN2NDCTL */ - " group not modified under any controller\n", /* GRPNOTMODINANYCTL */ - " Group deleted from filesystem\n", /* GRPDELETEDINFS */ - " Group not deleted from filesystem\n", /* GRPNOTDELETEDINFS */ - " Group not deleted globally\n", /* GRPNOTDELETEDGLOBALY */ - /* In case there is no extra info messages to be printed */ - "\n", /* NOMESSAGE */ -}; - int cpu, memory; int fs_mounted; @@ -651,224 +627,7 @@ int main(int argc, char *argv[]) return 0; } - -void test_cgroup_init(int retcode, int i) -{ - int retval; - char extra[SIZE] = "\n"; - - retval = cgroup_init(); - if (retval == retcode) - message(i, PASS, "init()\t", retval, extra); - else - message(i, FAIL, "init()", retval, extra); -} - -void test_cgroup_attach_task(int retcode, struct cgroup *cgrp, - const char *group1, const char *group2, - int k, int i) -{ - int retval; - char tasksfile[FILENAME_MAX], tasksfile2[FILENAME_MAX]; - /* Check, In case some error is expected due to a negative scenario */ - if (retcode) { - retval = cgroup_attach_task(cgrp); - if (retval == retcode) - message(i, PASS, "attach_task()", retval, info[k]); - else - message(i, FAIL, "attach_task()", retval, info[k]); - - return; - } - - /* Now there is no error and it is a genuine call */ - retval = cgroup_attach_task(cgrp); - if (retval == 0) { /* API returned success, so perform check */ - build_path(tasksfile, mountpoint, - group1, "tasks"); - - if (check_task(tasksfile)) { - if (fs_mounted == 2) { /* multiple mounts */ - build_path(tasksfile2, mountpoint2, - group2, "tasks"); - if (check_task(tasksfile2)) { - message(i, PASS, "attach_task()", - retval, info[TASKINGRP]); - } else { - message(i, FAIL, "attach_task()", - retval, info[TASKNOTINANYGRP]); - } - } else { /* single mount */ - message(i, PASS, "attach_task()", - retval, info[TASKINGRP]); - } - } else { - message(i, FAIL, "attach_task()", retval, - info[TASKNOTINGRP]); - } - } else { - message(i, FAIL, "attach_task()", retval, (char *)"\n"); - } -} - - -struct cgroup *create_new_cgroup_ds(int ctl, const char *grpname, - int value_type, struct cntl_val_t cval, struct uid_gid_t ids, int i) -{ - int retval; - char group[FILENAME_MAX]; - char controller_name[FILENAME_MAX], control_file[FILENAME_MAX]; - - strncpy(group, grpname, sizeof(group)); - retval = set_controller(ctl, controller_name, control_file); - if (retval) { - fprintf(stderr, "Setting controller failled\n"); - return NULL; - } - - switch (ctl) { - /* control values are controller specific, so will be set - * accordingly from the config file */ - case CPU: - strncpy(cval.val_string, "260000", sizeof(cval.val_string)); - break; - - case MEMORY: - strncpy(cval.val_string, "7000064", sizeof(cval.val_string)); - break; - - /* To be added for other controllers */ - default: - printf("Invalid controller name passed. Setting control value" - " failed. Dependent tests may fail\n"); - return NULL; - break; - } - - return new_cgroup(group, controller_name, control_file, - value_type, cval, ids, i); -} - - -void test_cgroup_create_cgroup(int retcode, struct cgroup *cgrp, - const char *name, int common, int mpnt, int ign, int i) -{ - int retval; - char path1_group[FILENAME_MAX], path2_group[FILENAME_MAX]; - /* Check, In case some error is expected due to a negative scenario */ - if (retcode) { - retval = cgroup_create_cgroup(cgrp, ign); - if (retval == retcode) - message(i, PASS, "create_cgroup()", retval, - info[NOMESSAGE]); - else - message(i, FAIL, "create_cgroup()", retval, - info[NOMESSAGE]); - - return; - } - - /* Now there is no error and it is a genuine call */ - retval = cgroup_create_cgroup(cgrp, ign); - if (retval) { - message(i, FAIL, "create_cgroup()", retval, info[NOMESSAGE]); - return; - } - - /* Let us now check if the group exist in file system */ - if (!common) { /* group only under one mountpoint */ - if (mpnt == 1) /* group under mountpoint */ - build_path(path1_group, mountpoint, name, NULL); - else /* group under mountpoint2 */ - build_path(path1_group, mountpoint2, name, NULL); - - if (group_exist(path1_group) == 0) - message(i, PASS, "create_cgroup()", retval, - info[GRPINFS]); - else - message(i, FAIL, "create_cgroup()", retval, - info[GRPNOTINFS]); - - } else { /* group under both mountpoints */ - /* Check if the group exists under both controllers */ - build_path(path1_group, mountpoint, name, NULL); - if (group_exist(path1_group) == 0) { - build_path(path2_group, mountpoint2, name, NULL); - - if (group_exist(path2_group) == 0) - message(i, PASS, "create_cgroup()", - retval, info[GRPINBOTHCTLS]); - else - message(i, FAIL, "create_cgroup()", - retval, info[GRPNOTIN2NDCTL]); - } else { - message(i, FAIL, "create_cgroup()", retval, - info[GRPNOTIN1STCTL]); - } - } - - return; -} - -void test_cgroup_delete_cgroup(int retcode, struct cgroup *cgrp, - const char *name, int common, int mpnt, int ign, int i) -{ - int retval; - char path1_group[FILENAME_MAX], path2_group[FILENAME_MAX]; - /* Check, In case some error is expected due to a negative scenario */ - if (retcode) { - retval = cgroup_delete_cgroup(cgrp, ign); - if (retval == retcode) - message(i, PASS, "delete_cgroup()", retval, - info[NOMESSAGE]); - else - message(i, FAIL, "delete_cgroup()", retval, - info[NOMESSAGE]); - - return; - } - - /* Now there is no error and it is a genuine call */ - retval = cgroup_delete_cgroup(cgrp, ign); - if (retval) { - message(i, FAIL, "delete_cgroup()", retval, info[NOMESSAGE]); - return; - } - - /* Let us now check if the group has been deleted from file system */ - if (!common) { /* check only under one mountpoint */ - if (mpnt == 1) /* check group under mountpoint */ - build_path(path1_group, mountpoint, name, NULL); - else /* check group under mountpoint2 */ - build_path(path1_group, mountpoint2, name, NULL); - - if (group_exist(path1_group) == -1) - message(i, PASS, "delete_cgroup()", retval, - info[GRPDELETEDINFS]); - else - message(i, FAIL, "delete_cgroup()", retval, - info[GRPNOTDELETEDINFS]); - - } else { /* check group under both mountpoints */ - /* Check if the group deleted under both controllers */ - build_path(path1_group, mountpoint, name, NULL); - if (group_exist(path1_group) == -1) { - build_path(path2_group, mountpoint2, name, NULL); - - if (group_exist(path2_group) == -1) - message(i, PASS, "delete_cgroup()", - retval, info[GRPDELETEDINFS]); - else - message(i, FAIL, "delete_cgroup()", - retval, info[GRPNOTDELETEDGLOBALY]); - } else { - message(i, FAIL, "delete_cgroup()", retval, - info[GRPNOTDELETEDINFS]); - } - } - -} - +/* This function is not generic enough and handles only 2 controllers */ void test_cgroup_modify_cgroup(int retcode, struct cgroup *cgrp, const char *name, int which_ctl, int ctl1, int ctl2, int value_type, int i) @@ -980,466 +739,3 @@ void test_cgroup_modify_cgroup(int retco return; } -void get_controllers(const char *name, int *exist) -{ - int hierarchy, num_cgroups, enabled; - FILE *fd; - char subsys_name[FILENAME_MAX]; - fd = fopen("/proc/cgroups", "r"); - if (!fd) - return; - - while (!feof(fd)) { - fscanf(fd, "%s, %d, %d, %d", subsys_name, - &hierarchy, &num_cgroups, &enabled); - if (strncmp(name, subsys_name, sizeof(*name)) == 0) - *exist = 1; - } -} - -static int group_exist(char *path_group) -{ - int ret; - ret = open(path_group, O_DIRECTORY); - if (ret == -1) - return ret; - return 0; -} - -static int set_controller(int controller, char *controller_name, - char *control_file) -{ - switch (controller) { - case MEMORY: - if (memory == 0) - return 1; - - strncpy(controller_name, "memory", FILENAME_MAX); - strncpy(control_file, "memory.limit_in_bytes", FILENAME_MAX); - return 0; - break; - - case CPU: - if (cpu == 0) - return 1; - - strncpy(controller_name, "cpu", FILENAME_MAX); - strncpy(control_file, "cpu.shares", FILENAME_MAX); - return 0; - break; - - case CPUSET: - strncpy(controller_name, "cpuset", FILENAME_MAX); - /* What is the exact control file?? */ - strncpy(control_file, "cpuset.mem_exclusive", FILENAME_MAX); - return 0; - break; - /* Future controllers can be added here */ - - default: - printf("Invalid controller name passed. Setting controller" - " failed. Dependent tests may fail\n"); - return 1; - break; - } -} - -static int group_modified(char *path_control_file, int value_type, - struct cntl_val_t cval) -{ - bool bool_val; - int64_t int64_val; - u_int64_t uint64_val; - char string_val[FILENAME_MAX]; /* Doubt: what should be the size ? */ - FILE *fd; - - fd = fopen(path_control_file, "r"); - if (!fd) { - fprintf(stderr, "Error in opening %s\n", path_control_file); - fprintf(stderr, "Skipping modified values check....\n"); - return 1; - } - - switch (value_type) { - - case BOOL: - fscanf(fd, "%d", &bool_val); - if (bool_val == cval.val_bool) - return 0; - break; - case INT64: - fscanf(fd, "%lld", &int64_val); - if (int64_val == cval.val_int64) - return 0; - break; - case UINT64: - fscanf(fd, "%llu", &uint64_val); - if (uint64_val == cval.val_uint64) - return 0; - break; - case STRING: - fscanf(fd, "%s", string_val); - if (!strncmp(string_val, cval.val_string, strlen(string_val))) - return 0; - break; - default: - fprintf(stderr, "Wrong value_type passed " - "in group_modified()\n"); - fprintf(stderr, "Skipping modified values check....\n"); - return 0; /* Can not report test result as failure */ - break; - } - return 1; -} -static int add_control_value(struct cgroup_controller *newcontroller, - char *control_file, char *wr, int value_type, struct cntl_val_t cval) -{ - int retval; - - switch (value_type) { - - case BOOL: - retval = cgroup_add_value_bool(newcontroller, - control_file, cval.val_bool); - snprintf(wr, SIZE, "add_value_bool()"); - break; - case INT64: - retval = cgroup_add_value_int64(newcontroller, - control_file, cval.val_int64); - snprintf(wr, SIZE, "add_value_int64()"); - break; - case UINT64: - retval = cgroup_add_value_uint64(newcontroller, - control_file, cval.val_uint64); - snprintf(wr, SIZE, "add_value_uint64()"); - break; - case STRING: - retval = cgroup_add_value_string(newcontroller, - control_file, cval.val_string); - snprintf(wr, SIZE, "add_value_string()"); - break; - default: - printf("ERROR: wrong value in add_control_value()\n"); - return 1; - break; - } - return retval; -} - -struct cgroup *new_cgroup(char *group, char *controller_name, - char *control_file, int value_type, - struct cntl_val_t cval, struct uid_gid_t ids, int i) -{ - int retval; - char wr[SIZE]; /* Names of wrapper apis */ - struct cgroup *newcgroup; - struct cgroup_controller *newcontroller; - - newcgroup = cgroup_new_cgroup(group); - - if (newcgroup) { - retval = cgroup_set_uid_gid(newcgroup, ids.tasks_uid, - ids.tasks_gid, ids.control_uid, ids.control_gid); - - if (retval) { - snprintf(wr, SIZE, "set_uid_gid()"); - message(i++, FAIL, wr, retval, extra); - } - - newcontroller = cgroup_add_controller(newcgroup, - controller_name); - if (newcontroller) { - retval = add_control_value(newcontroller, - control_file, wr, value_type, cval); - - if (!retval) { - message(i++, PASS, "new_cgroup()", - retval, extra); - } else { - message(i++, FAIL, wr, retval, extra); - return NULL; - } - } else { - /* Since these wrappers do not return an int so -1 */ - message(i++, FAIL, "add_controller", -1, extra); - return NULL; - } - } else { - message(i++, FAIL, "new_cgroup", -1, extra); - return NULL; - } - return newcgroup; -} - -int check_fsmounted(int multimnt) -{ - int count = 0; - struct mntent *entry, *tmp_entry; - /* Need a better mechanism to decide memory allocation size here */ - char entry_buffer[FILENAME_MAX * 4]; - FILE *proc_file; - - tmp_entry = (struct mntent *) malloc(sizeof(struct mntent)); - if (!tmp_entry) { - perror("Error: failled to mallloc for mntent\n"); - return 1; - } - - proc_file = fopen("/proc/mounts", "r"); - if (!proc_file) { - printf("Error in opening /proc/mounts.\n"); - return EIO; - } - while ((entry = getmntent_r(proc_file, tmp_entry, entry_buffer, - FILENAME_MAX*4)) != NULL) { - if (!strncmp(entry->mnt_type, "cgroup", strlen("cgroup"))) { - count++; - if (multimnt) { - if (count >= 2) { - printf("sanity check pass. %s\n", - entry->mnt_type); - return 0; - } - } else { - printf("sanity check pass. %s\n", - entry->mnt_type); - return 0; - } - } - } - return 1; -} - -static int check_task(char *tasksfile) -{ - FILE *file; - pid_t curr_tid, tid; - int pass = 0; - - file = fopen(tasksfile, "r"); - if (!file) { - printf("ERROR: in opening %s\n", tasksfile); - printf("Exiting without running other testcases in this set\n"); - exit(1); - } - - curr_tid = cgrouptest_gettid(); - while (!feof(file)) { - fscanf(file, "%u", &tid); - if (tid == curr_tid) { - pass = 1; - break; - } - } - - return pass; -} - -static inline void message(int num, int pass, const char *api, - int retval, char *extra) -{ - char res[10]; - char buf[2*SIZE]; - if (pass) - strncpy(res, "PASS :", 10); - else - strncpy(res, "FAIL :", 10); - - /* Populate message buffer for the api */ - snprintf(buf, sizeof(buf), "cgroup_%s\t\t Ret Value = ", api); - fprintf(stdout, "TEST%2d:%s %s%d\t%s", num, res, buf, retval, extra); -} - -/* builds the path to target file/group */ -static inline void build_path(char *target, char *mountpoint, - const char *group, const char *file) -{ - strncpy(target, mountpoint, FILENAME_MAX); - - if (group) { - strncat(target, "/", FILENAME_MAX); - strncat(target, group, FILENAME_MAX); - } - - if (file) { - strncat(target, "/", FILENAME_MAX); - strncat(target, file, FILENAME_MAX); - } -} - -void test_cgroup_compare_cgroup(int ctl1, int ctl2, int i) -{ - int retval; - struct cntl_val_t cval = {0, 0, 0, "5000"}; - struct cgroup *cgroup1, *cgroup2; - struct cgroup_controller *controller; - char controller_name[FILENAME_MAX], control_file[FILENAME_MAX]; - char wr[SIZE], extra[] = "in cgroup_compare_cgroup"; - - retval = cgroup_compare_cgroup(NULL, NULL); - if (retval) - message(i++, PASS, "compare_cgroup()", retval, info[NULLGRP]); - else - message(i++, FAIL, "compare_cgroup()", retval, info[NULLGRP]); - - cgroup1 = cgroup_new_cgroup("testgroup"); - cgroup2 = cgroup_new_cgroup("testgroup"); - cgroup_set_uid_gid(cgroup1, 0, 0, 0, 0); - cgroup_set_uid_gid(cgroup2, 0, 0, 0, 0); - - retval = set_controller(ctl1, controller_name, control_file); - - controller = cgroup_add_controller(cgroup1, controller_name); - if (controller) { - retval = add_control_value(controller, - control_file, wr, STRING, cval); - if (retval) - message(i++, FAIL, wr, retval, extra); - } - - controller = cgroup_add_controller(cgroup2, controller_name); - if (controller) { - retval = add_control_value(controller, - control_file, wr, STRING, cval); - if (retval) - message(i++, FAIL, wr, retval, extra); - } - - retval = cgroup_compare_cgroup(cgroup1, cgroup2); - if (retval) - message(i++, FAIL, "compare_cgroup()", retval, info[NOMESSAGE]); - else - message(i++, PASS, "compare_cgroup()", retval, info[NOMESSAGE]); - - /* Test the api by putting diff number of controllers in cgroups */ - retval = set_controller(ctl2, controller_name, control_file); - controller = cgroup_add_controller(cgroup2, controller_name); - if (controller) { - retval = add_control_value(controller, - control_file, wr, STRING, cval); - if (retval) - message(i++, FAIL, wr, retval, extra); - } - - retval = cgroup_compare_cgroup(cgroup1, cgroup2); - if (retval == ECGROUPNOTEQUAL) - message(i++, PASS, "compare_cgroup()", retval, info[NOMESSAGE]); - else - message(i++, FAIL, "compare_cgroup()", retval, info[NOMESSAGE]); - - cgroup_free(&cgroup1); - cgroup_free(&cgroup2); -} - -void test_cgroup_get_cgroup(int ctl1, int ctl2, struct uid_gid_t ids, int i) -{ - struct cgroup *cgroup_filled, *cgroup_a, *cgroup_b; - struct cgroup_controller *controller; - char controller_name[FILENAME_MAX], control_file[FILENAME_MAX]; - struct cntl_val_t cval = {0, 0, 0, "5000"}; - int ret; - - /* - * No need to test the next 3 scenarios again for Multimnt - * so testing them only under single mount - */ - if (fs_mounted == FS_MOUNTED) { - /* 1. Test with nullcgroup first */ - ret = cgroup_get_cgroup(NULL); - if (ret == ECGROUPNOTALLOWED) - message(i++, PASS, "get_cgroup()", ret, info[NULLGRP]); - else - message(i++, FAIL, "get_cgroup()", ret, info[NULLGRP]); - - /* 2. Test with invalid name filled cgroup(non existing) */ - cgroup_filled = cgroup_new_cgroup("nogroup"); - if (!cgroup_filled) - message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]); - - ret = cgroup_get_cgroup(cgroup_filled); - if (ret) - message(i++, PASS, "get_cgroup()", ret, - info[NOTCRTDGRP]); - else - message(i++, FAIL, "get_cgroup()", ret, - info[NOTCRTDGRP]); - - /* 3. - * Test with name filled cgroup. Ensure the group group1 exists - * in the filesystem before calling this test function - */ - cgroup_filled = cgroup_new_cgroup("group1"); - if (!cgroup_filled) - message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]); - - ret = cgroup_get_cgroup(cgroup_filled); - if (!ret) - message(i++, PASS, "get_cgroup()", ret, - info[NOMESSAGE]); - else - message(i++, FAIL, "get_cgroup()", ret, - info[NOMESSAGE]); - } - - /* MULTIMOUNT: Create, get and compare a cgroup under both mounts */ - - /* get cgroup_a ds and create group_a in filesystem */ - cgroup_a = create_new_cgroup_ds(ctl1, "group_a", STRING, cval, ids, 0); - if (fs_mounted == FS_MULTI_MOUNTED) { - /* Create under another controller also */ - ret = set_controller(ctl2, controller_name, control_file); - controller = cgroup_add_controller(cgroup_a, controller_name); - } - test_cgroup_create_cgroup(0, cgroup_a, "group_a", 0, 1, 1, 00); - - /* create group_b ds to be filled by cgroup_get_cgroup */ - cgroup_b = cgroup_new_cgroup("group_a"); - if (!cgroup_b) - message(i++, FAIL, "new_cgroup()", 0, info[NOMESSAGE]); - /* Fill the ds and compare the two */ - ret = cgroup_get_cgroup(cgroup_b); - if (!ret) { - ret = cgroup_compare_cgroup(cgroup_a, cgroup_b); - if (ret == 0) - message(i++, PASS, "get_cgroup()", ret, info[SAMEGRP]); - else - message(i++, FAIL, "get_cgroup()", ret, - info[NOMESSAGE]); - } else { - message(i++, FAIL, "get_cgroup()", ret, info[NOMESSAGE]); - } - - cgroup_free(&cgroup_a); - cgroup_free(&cgroup_b); - cgroup_free(&cgroup_filled); -} - -void test_cgroup_add_free_controller(int i) -{ - struct cgroup *cgroup1 = NULL, *cgroup2 = NULL; - struct cgroup_controller *cgctl1, *cgctl2; - - /* Test with a Null cgroup */ - cgctl1 = cgroup_add_controller(cgroup1, "cpu"); - if (!cgctl1) - message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]); - else - message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]); - - cgroup1 = cgroup_new_cgroup("testgroup"); - cgctl1 = cgroup_add_controller(cgroup1, "cpuset"); - if (cgctl1) - message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]); - else - message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]); - - cgctl2 = cgroup_add_controller(cgroup1, "cpu"); - if (cgctl2) - message(i++, PASS, "add_controller()", 0, info[NOMESSAGE]); - else - message(i++, FAIL, "add_controller()", -1, info[NOMESSAGE]); - - cgroup_free(&cgroup1); - cgroup_free_controllers(cgroup2); - -} Index: trunk/tests/Makefile =================================================================== --- trunk.orig/tests/Makefile +++ trunk/tests/Makefile @@ -1,18 +1,25 @@ -LDFLAGS = -L .. -LIBS = -lcgroup -lpthread +LDFLAGS = -L .. -L. +LIBS = -lcgroup -lpthread -lfunctions INC = -I .. CXXFLAGS = -g -O2 -Wall -DDEBUG $(INC) CFLAGS = -g -O2 -Wall -DDEBUG -TARGET= libcgrouptest01 \ +TARGET= libfunctions.a \ + libcgrouptest01 \ libcg_ba \ setuid \ pathtest all: $(TARGET) -libcgrouptest01: libcgrouptest01.c - $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) +functions.o: functions.c + $(CXX) -c $< + +libfunctions.a: functions.o + $(AR) -cr $@ $^ + +libcgrouptest01: functions.o libcgrouptest01.c + $(CXX) $(CXXFLAGS) -o $@ libcgrouptest01.c $(LDFLAGS) $(LIBS) libcg_ba: libcg_ba.cpp $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) @@ -24,4 +31,4 @@ pathtest: pathtest.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) clean: - \rm -f $(TARGET) + \rm -f $(TARGET) functions.o Index: trunk/tests/libcgrouptest.h =================================================================== --- trunk.orig/tests/libcgrouptest.h +++ trunk/tests/libcgrouptest.h @@ -127,26 +127,23 @@ void test_cgroup_get_cgroup(int ctl1, in void test_cgroup_compare_cgroup(int ctl1, int ctl2, int i); void test_cgroup_add_free_controller(int i); void get_controllers(const char *name, int *exist); -static int group_exist(char *path_group); -static int set_controller(int controller, char *controller_name, +int group_exist(char *path_group); +int set_controller(int controller, char *controller_name, char *control_file); -static int group_modified(char *path_control_file, int value_type, +int group_modified(char *path_control_file, int value_type, struct cntl_val_t cval); -static int add_control_value(struct cgroup_controller *newcontroller, +int add_control_value(struct cgroup_controller *newcontroller, char *control_file, char *wr, int value_type, struct cntl_val_t cval); struct cgroup *new_cgroup(char *group, char *controller_name, char *control_file, int value_type, struct cntl_val_t cval, struct uid_gid_t ids, int i); int check_fsmounted(int multimnt); -static int check_task(char *tasksfile); +int check_task(char *tasksfile); /* function to print messages in better format */ -static inline void message(int num, int pass, const char *api, +void message(int num, int pass, const char *api, int ret, char *extra); -static inline void build_path(char *target, char *mountpoint, +void build_path(char *target, char *mountpoint, const char *group, const char *file); +pid_t cgrouptest_gettid(); -static inline pid_t cgrouptest_gettid() -{ - return syscall(__NR_gettid); -} #endif ------------------------------------------------------------------------------ _______________________________________________ Libcg-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/libcg-devel
