--- include/libcgroup/config.h | 17 +++++ src/config.c | 140 ++++++++++++++++++++++++++++++++++++++++++++ src/libcgroup.map | 1 3 files changed, 158 insertions(+), 0 deletions(-)
diff --git a/include/libcgroup/config.h b/include/libcgroup/config.h index 3865603..4cf5ce0 100644 --- a/include/libcgroup/config.h +++ b/include/libcgroup/config.h @@ -39,6 +39,23 @@ int cgroup_config_load_config(const char *pathname); int cgroup_unload_cgroups(void); /** + * Delete all cgroups and unmount all mount points defined in specified config + * file. + * + * The groups are either removed recursively or only the empty ones, based + * on given flags. Mount point are always umounted only if they are empty, + * regardless of any flags. + * + * The groups are sorted before they are removed, so the removal of empty ones + * actually works (i.e. subgroups are removed first). + * + * @param pathname Name of the configuration file to unload. + * @param flags Combination of CGFLAG_DELETE_* flags, which indicate what and + * how to delete. + */ +int cgroup_config_unload_config(const char *pathname, int flags); + +/** * @} * @} */ diff --git a/src/config.c b/src/config.c index e71a400..8548174 100644 --- a/src/config.c +++ b/src/config.c @@ -798,6 +798,20 @@ err: return ret; } +int _cgroup_config_compare_groups(const void *p1, const void *p2) +{ + const struct cgroup *g1 = p1; + const struct cgroup *g2 = p2; + + return strcmp(g1->name, g2->name); +} + +static void cgroup_config_sort_groups() +{ + qsort(config_cgroup_table, cgroup_table_index, sizeof(struct cgroup), + _cgroup_config_compare_groups); +} + /* * The main function which does all the setup of the data structures * and finally creates the cgroups @@ -868,6 +882,132 @@ err_mnt: return error; } +/* unmounts given mount, but only if it is empty */ +static int cgroup_config_try_unmount(struct cg_mount_table_s *mount_info) +{ + char *controller, *controller_list; + struct cg_mount_point *mount = &(mount_info->mount); + void *handle = NULL; + int ret, lvl; + struct cgroup_file_info info; + char *saveptr = NULL; + + /* parse the first controller name from list of controllers */ + controller_list = strdup(mount_info->name); + if (!controller_list) { + last_errno = errno; + return ECGOTHER; + } + controller = strtok_r(controller_list, ",", &saveptr); + if (!controller) { + free(controller_list); + return ECGINVAL; + } + + /* check if the hierarchy is empty */ + ret = cgroup_walk_tree_begin(controller, "/", 0, &handle, &info, &lvl); + free(controller_list); + if (ret == ECGCONTROLLEREXISTS) + return 0; + if (ret) + return ret; + /* skip the first found directory, it's '/' */ + ret = cgroup_walk_tree_next(0, &handle, &info, lvl); + /* find any other subdirectory */ + while (ret == 0) { + if (info.type == CGROUP_FILE_TYPE_DIR) + break; + ret = cgroup_walk_tree_next(0, &handle, &info, lvl); + } + cgroup_walk_tree_end(&handle); + if (ret == 0) { + cgroup_dbg("won't unmount %s: hieararchy is not empty\n", + mount_info->name); + return 0; /* the hieararchy is not empty */ + } + if (ret != ECGEOF) + return ret; + + + /* + * ret must be ECGEOF now = there is only root group in the hierarchy + * -> unmount all mount points. + */ + ret = 0; + while (mount) { + int err; + cgroup_dbg("unmounting %s at %s\n", mount_info->name, + mount->path); + err = umount(mount->path); + + if (err && !ret) { + ret = ECGOTHER; + last_errno = errno; + } + mount = mount->next; + } + return ret; +} + +int cgroup_config_unload_config(const char *pathname, int flags) +{ + int ret, i, error; + int namespace_enabled = 0; + int mount_enabled = 0; + + cgroup_dbg("cgroup_config_unload_config: parsing %s\n", pathname); + ret = cgroup_parse_config(pathname); + if (ret) + goto err; + + namespace_enabled = (config_namespace_table[0].name[0] != '\0'); + mount_enabled = (config_mount_table[0].name[0] != '\0'); + /* + * The configuration should have namespace or mount, not both. + */ + if (namespace_enabled && mount_enabled) { + free(config_cgroup_table); + return ECGMOUNTNAMESPACE; + } + + ret = config_order_namespace_table(); + if (ret) + goto err; + + ret = config_validate_namespaces(); + if (ret) + goto err; + + /* + * Delete the groups in reverse order, i.e. subgroups first, then + * parents. + */ + cgroup_config_sort_groups(); + for (i = cgroup_table_index-1; i >= 0; i--) { + struct cgroup *cgroup = &config_cgroup_table[i]; + cgroup_dbg("removing %s\n", pathname); + error = cgroup_delete_cgroup_ext(cgroup, flags); + if (error && !ret) { + /* store the error, but continue deleting the rest */ + ret = error; + } + } + + if (mount_enabled) { + for (i = 0; i < config_table_index; i++) { + struct cg_mount_table_s *m = &(config_mount_table[i]); + cgroup_dbg("unmounting %s\n", m->name); + error = cgroup_config_try_unmount(m); + if (error && !ret) + ret = error; + } + } + +err: + cgroup_free_config(); + return ret; +} + static int cgroup_config_unload_controller(const struct cgroup_mount_point *mount_info) { int ret, error; diff --git a/src/libcgroup.map b/src/libcgroup.map index f1afaf6..7a0927e 100644 --- a/src/libcgroup.map +++ b/src/libcgroup.map @@ -102,4 +102,5 @@ CGROUP_0.38 { cgroup_get_subsys_mount_point_next; cgroup_get_subsys_mount_point_end; cgroup_set_permissions; + cgroup_config_unload_config; } CGROUP_0.37; ------------------------------------------------------------------------------ All of the data generated in your IT infrastructure is seriously valuable. Why? It contains a definitive record of application performance, security threats, fraudulent activity, and more. Splunk takes this data and makes sense of it. IT sense. And common sense. http://p.sf.net/sfu/splunk-d2dcopy2 _______________________________________________ Libcg-devel mailing list Libcg-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libcg-devel