]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
Feature: Provide new libcgroup walk tree API
authorDhaval Giani <dhaval@linux.vnet.ibm.com>
Sat, 28 Feb 2009 09:27:31 +0000 (09:27 +0000)
committerDhaval Giani <dhaval@linux.vnet.ibm.com>
Sat, 28 Feb 2009 09:27:31 +0000 (09:27 +0000)
From: Balbir Singh <balbir@linux.vnet.ibm.com>

Changelog v3..v4

1. Add _end() method for the iterator

Changelog v3..v2

1. Move to iterator based design

Changelog v2..v1

1. Add base path and depth semantics for walking

This patch adds the capability to walk cgroups by providing a new API
called cgroup_walk_tree. The API accepts the controller to walk and the
order in which the directories and files must be visited. The code is
implemented as an iterator, the begin function starts the walk and
we have depth control. The next function gets the following node
and returns ECGEOF when done.

libcgroup.map has been updated to reflect the same change and the prototype
is exported in libcgroup.h.

I've also added test cases (tests/walk_test.c). Sample output is show

root is /cgroup/cpu///
path , parent , relative /, full /cgroup/cpu///
path l3, parent , relative /l3, full /cgroup/cpu///l3
path ll1, parent l3, relative /l3/ll1, full /cgroup/cpu///l3/ll1
path lll1, parent ll1, relative /l3/ll1/lll1, full /cgroup/cpu///l3/ll1/lll1
path l2, parent , relative /l2, full /cgroup/cpu///l2
path ll1, parent l2, relative /l2/ll1, full /cgroup/cpu///l2/ll1
path lll1, parent ll1, relative /l2/ll1/lll1, full /cgroup/cpu///l2/ll1/lll1
path l1, parent , relative /l1, full /cgroup/cpu///l1
path ll1, parent l1, relative /l1/ll1, full /cgroup/cpu///l1/ll1
path lll1, parent ll1, relative /l1/ll1/lll1, full /cgroup/cpu///l1/ll1/lll1
path a, parent , relative /a, full /cgroup/cpu///a
path e, parent a, relative /a/e, full /cgroup/cpu///a/e
path f, parent e, relative /a/e/f, full /cgroup/cpu///a/e/f
path f, parent a, relative /a/f, full /cgroup/cpu///a/f
path x, parent a, relative /a/x, full /cgroup/cpu///a/x
path b, parent a, relative /a/b, full /cgroup/cpu///a/b
path c, parent b, relative /a/b/c, full /cgroup/cpu///a/b/c
path d, parent c, relative /a/b/c/d, full /cgroup/cpu///a/b/c/d
path default, parent , relative /default, full /cgroup/cpu///default
root is /cgroup/cpu//a/
path , parent , relative /, full /cgroup/cpu//a/
path e, parent , relative /e, full /cgroup/cpu//a/e
path f, parent e, relative /e/f, full /cgroup/cpu//a/e/f
path f, parent , relative /f, full /cgroup/cpu//a/f
path x, parent , relative /x, full /cgroup/cpu//a/x
path b, parent , relative /b, full /cgroup/cpu//a/b
path c, parent b, relative /b/c, full /cgroup/cpu//a/b/c
Walking the first 5 nodes
root is /cgroup/cpu///
path , parent , relative /, full /cgroup/cpu///
path l3, parent , relative /l3, full /cgroup/cpu///l3
path ll1, parent l3, relative /l3/ll1, full /cgroup/cpu///l3/ll1
path lll1, parent ll1, relative /l3/ll1/lll1, full /cgroup/cpu///l3/ll1/lll1

NOTE: Parent directory is represented by an empty (not NULL) string "".
The length of the string is 0.

Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
Signed-off-by: Dhaval Giani <dhaval@linux.vnet.ibm.com>
git-svn-id: https://libcg.svn.sourceforge.net/svnroot/libcg/trunk@356 4f4bb910-9a46-0410-90c8-c897d4f1cd53

api.c
libcgroup.h
libcgroup.map
tests/Makefile
tests/walk_test.c [new file with mode: 0644]

diff --git a/api.c b/api.c
index 968f9e1316b8b95f66e43d929c9a7f7bc70b8414..ac5fcbb27460319e7ea3570d28b57d04cab13bda 100644 (file)
--- 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;
+}
index 08613cf1bdf9cd7dfb7fccbfd46122f435a5b373..750e36e306d3658af9609680f5c894b6f21f95c6 100644 (file)
@@ -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 */
 
index fffe44806dbfe376516ecc694603a077c9da71bd..1989f901cd129da6032c93dcb302ad73ee1f99cf 100644 (file)
@@ -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;
 
index 00bfe3dde8348eb3616fb6026c65fdf81a739b83..9ebadac98e5c5efcba59267b0ff555c801cd6ded 100644 (file)
@@ -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 (file)
index 0000000..2e16ecd
--- /dev/null
@@ -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;
+}