diff options
-rw-r--r-- | api.c | 106 | ||||
-rw-r--r-- | libcgroup.h | 50 | ||||
-rw-r--r-- | libcgroup.map | 3 | ||||
-rw-r--r-- | tests/Makefile | 6 | ||||
-rw-r--r-- | tests/walk_test.c | 90 |
5 files changed, 254 insertions, 1 deletions
@@ -105,6 +105,7 @@ char *cgroup_strerror_codes[] = { "Cgroup, rules file does not exist", "Cgroup mounting failed", "The config file can not be opend", + "End of File or iterator", }; static int cg_chown_file(FTS *fts, FTSENT *ent, uid_t owner, gid_t group) @@ -2242,3 +2243,108 @@ int cgroup_get_last_errno() { return last_errno; } + + +static int cg_walk_node(FTS *fts, FTSENT *ent, const int depth, + struct cgroup_file_info *info) +{ + int ret = 0; + int base_level; + + cgroup_dbg("seeing file %s\n", ent->fts_path); + + info->path = ent->fts_name; + info->parent = ent->fts_parent->fts_name; + info->full_path = ent->fts_path; + info->depth = ent->fts_level; + info->type = CGROUP_FILE_TYPE_OTHER; + + if (depth && (info->depth > depth)) + return 0; + + switch (ent->fts_info) { + case FTS_DNR: + case FTS_ERR: + errno = ent->fts_errno; + break; + case FTS_D: + info->type = CGROUP_FILE_TYPE_DIR; + break; + case FTS_DC: + case FTS_NSOK: + case FTS_NS: + case FTS_DP: + break; + case FTS_F: + info->type = CGROUP_FILE_TYPE_FILE; + break; + case FTS_DEFAULT: + break; + } + return ret; +} + +int cgroup_walk_tree_next(const int depth, void **handle, + struct cgroup_file_info *info, int base_level) +{ + int ret = 0; + FTS *fts = *(FTS **)handle; + FTSENT *ent; + + if (!handle) + return ECGINVAL; + ent = fts_read(fts); + if (!ent) + return ECGEOF; + if (!base_level && depth) + base_level = ent->fts_level + depth; + ret = cg_walk_node(fts, ent, base_level, info); + *handle = fts; + return ret; +} + +int cgroup_walk_tree_end(void **handle) +{ + int ret = 0; + FTS *fts = *(FTS **)handle; + + if (!handle) + return ECGINVAL; + fts_close(fts); + return 0; +} + +/* + * TODO: Need to decide a better place to put this function. + */ +int cgroup_walk_tree_begin(char *controller, char *base_path, const int depth, + void **handle, struct cgroup_file_info *info, + int *base_level) +{ + int ret = 0; + cgroup_dbg("path is %s\n", base_path); + char *cg_path[2]; + char full_path[FILENAME_MAX]; + FTSENT *ent; + FTS *fts; + + if (!cg_build_path(base_path, full_path, controller)) + return ECGOTHER; + + *base_level = 0; + cg_path[0] = full_path; + cg_path[1] = NULL; + + fts = fts_open(cg_path, FTS_LOGICAL | FTS_NOCHDIR | + FTS_NOSTAT, NULL); + ent = fts_read(fts); + if (!ent) { + cgroup_dbg("fts_read failed\n"); + return ECGINVAL; + } + if (!*base_level && depth) + *base_level = ent->fts_level + depth; + ret = cg_walk_node(fts, ent, *base_level, info); + *handle = fts; + return ret; +} diff --git a/libcgroup.h b/libcgroup.h index 08613cf..750e36e 100644 --- a/libcgroup.h +++ b/libcgroup.h @@ -94,6 +94,31 @@ enum cgroup_errors { ECGROUPNORULES, /* Rules list does not exist. */ ECGMOUNTFAIL, ECGSENTINEL, /* Please insert further error codes above this */ + ECGEOF, /* End of file, iterator */ +}; + +/* + * Don't use CGROUP_WALK_TYPE_FILE right now. It is added here for + * later refactoring and better implementation. Most users *should* + * use CGROUP_WALK_TYPE_PRE_DIR. + */ +enum cgroup_walk_type { + CGROUP_WALK_TYPE_PRE_DIR = 0x1, /* Pre Order Directory */ + CGROUP_WALK_TYPE_POST_DIR = 0x2, /* Post Order Directory */ +}; + +enum cgroup_file_type { + CGROUP_FILE_TYPE_FILE, /* File */ + CGROUP_FILE_TYPE_DIR, /* Directory */ + CGROUP_FILE_TYPE_OTHER, /* Directory */ +}; + +struct cgroup_file_info { + enum cgroup_file_type type; + const char *path; + const char *parent; + const char *full_path; + short depth; }; #define CG_NV_MAX 100 @@ -199,6 +224,31 @@ char *cgroup_strerror(int code); */ int cgroup_get_last_errno(); +/** + * Walk through the directory tree for the specified controller. + * @controller: Name of the controller, for which we want to walk + * the directory tree + * @base_path: Begin walking from this path + * @depth: The maximum depth to which the function should walk, 0 + * implies all the way down + * @handle: Handle to be used during iteration + * @info: info filled and returned about directory information + */ +int cgroup_walk_tree_begin(char *controller, char *base_path, const int depth, + void **handle, struct cgroup_file_info *info, + int *base_level); +/** + * Get the next element during the walk + * @depth: The maximum depth to which the function should walk, 0 + * implies all the way down + * @handle: Handle to be used during iteration + * @info: info filled and returned about directory information + * + * Returns ECGEOF when we are done walking through the nodes. + */ +int cgroup_walk_tree_next(const int depth, void **handle, + struct cgroup_file_info *info, int base_level); +int cgroup_walk_tree_end(void **handle); /* The wrappers for filling libcg structures */ diff --git a/libcgroup.map b/libcgroup.map index fffe448..1989f90 100644 --- a/libcgroup.map +++ b/libcgroup.map @@ -49,5 +49,8 @@ global: CGROUP_0.33 { global: cgroup_get_last_errno; + cgroup_walk_tree_begin; + cgroup_walk_tree_next; + cgroup_walk_tree_end; } CGROUP_0.32.1; diff --git a/tests/Makefile b/tests/Makefile index 00bfe3d..9ebadac 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -8,7 +8,8 @@ TARGET= libtest_functions.a \ libcgrouptest01 \ libcg_ba \ setuid \ - pathtest + pathtest \ + walk_test all: $(TARGET) @@ -30,5 +31,8 @@ setuid: setuid.c pathtest: pathtest.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) +walk_test: walk_test.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) + clean: \rm -f $(TARGET) test_functions.o diff --git a/tests/walk_test.c b/tests/walk_test.c new file mode 100644 index 0000000..2e16ecd --- /dev/null +++ b/tests/walk_test.c @@ -0,0 +1,90 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <libcgroup.h> + +void visit_node(struct cgroup_file_info *info, char *root) +{ + if (info->type == CGROUP_FILE_TYPE_DIR) { + printf("path %s, parent %s, relative %s, full %s\n", + info->path, info->parent, info->full_path + + + strlen(root) - 1, + info->full_path); + } +} + +int main(int argc, char *argv[]) +{ + int ret; + char *controller; + void *handle; + struct cgroup_file_info info; + char root[FILENAME_MAX]; + int lvl, i; + + if (argc < 2) { + fprintf(stderr, "Usage %s: <controller name>\n", + argv[0]); + exit(EXIT_FAILURE); + } + + controller = argv[1]; + + cgroup_init(); + + ret = cgroup_walk_tree_begin(controller, "/", 0, &handle, &info, &lvl); + + if (ret != 0) { + fprintf(stderr, "Walk failed\n"); + exit(EXIT_FAILURE); + } + strcpy(root, info.full_path); + printf("root is %s\n", root); + visit_node(&info, root); + while ((ret = cgroup_walk_tree_next(0, &handle, &info, lvl)) != + ECGEOF) { + visit_node(&info, root); + } + cgroup_walk_tree_end(&handle); + + ret = cgroup_walk_tree_begin(controller, "/a", 2, &handle, &info, &lvl); + + if (ret != 0) { + fprintf(stderr, "Walk failed\n"); + exit(EXIT_FAILURE); + } + strcpy(root, info.full_path); + printf("root is %s\n", root); + visit_node(&info, root); + while ((ret = cgroup_walk_tree_next(2, &handle, &info, lvl)) != + ECGEOF) { + visit_node(&info, root); + } + cgroup_walk_tree_end(&handle); + + /* + * Walk only the first five nodes + */ + i = 0; + printf("Walking the first 5 nodes\n"); + ret = cgroup_walk_tree_begin(controller, "/", 0, &handle, &info, &lvl); + + if (ret != 0) { + fprintf(stderr, "Walk failed\n"); + exit(EXIT_FAILURE); + } + strcpy(root, info.full_path); + printf("root is %s\n", root); + visit_node(&info, root); + i++; + while ((ret = cgroup_walk_tree_next(0, &handle, &info, lvl)) != + ECGEOF) { + visit_node(&info, root); + if (++i >= 5) + break; + } + cgroup_walk_tree_end(&handle); + return EXIT_SUCCESS; +} |