summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api.c106
-rw-r--r--libcgroup.h50
-rw-r--r--libcgroup.map3
-rw-r--r--tests/Makefile6
-rw-r--r--tests/walk_test.c90
5 files changed, 254 insertions, 1 deletions
diff --git a/api.c b/api.c
index 968f9e1..ac5fcbb 100644
--- a/api.c
+++ b/api.c
@@ -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;
+}