Feature: Provide new libcgroup walk tree API

From: Balbir Singh <[email protected]>

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. Everytime
a node is visited, a callback function is invoked that can act or record
the node being visited.

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).

Signed-off-by: Balbir Singh <[email protected]>
---

 api.c             |   90 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 libcgroup.h       |   41 ++++++++++++++++++++++++
 libcgroup.map     |    6 ++++
 tests/Makefile    |    6 +++-
 tests/walk_test.c |   63 +++++++++++++++++++++++++++++++++++++
 5 files changed, 205 insertions(+), 1 deletions(-)
 create mode 100644 tests/walk_test.c


diff --git a/api.c b/api.c
index eb94303..ece1414 100644
--- a/api.c
+++ b/api.c
@@ -2170,3 +2170,93 @@ char *cgroup_strerror(int code)
        assert((code >= ECGROUPNOTCOMPILED) && (code < ECGSENTINEL));
        return cgroup_strerror_codes[code % ECGROUPNOTCOMPILED];
 }
+
+static int cg_walk_node(FTS *fts, FTSENT *ent, enum cgroup_walk_type type,
+                       cgroup_walk_callback cb, void *arg)
+{
+       int ret = 0;
+       void *cb_ret;
+       struct cgroup_file_info info;
+
+       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;
+
+       switch (ent->fts_info) {
+       case FTS_DNR:
+       case FTS_ERR:
+               errno = ent->fts_errno;
+               break;
+       case FTS_D:
+               if (type & CGROUP_WALK_TYPE_PRE_DIR) {
+                       info.type = CGROUP_FILE_TYPE_DIR;
+                       cb_ret = cb(&info, arg);
+                       if (cb_ret) {
+                               ret = ECGOTHER;
+                               goto fail_walk;
+                       }
+               }
+               break;
+       case FTS_DC:
+       case FTS_NSOK:
+       case FTS_NS:
+       case FTS_DP:
+               if (type & CGROUP_WALK_TYPE_POST_DIR) {
+                       info.type = CGROUP_FILE_TYPE_DIR;
+                       cb_ret = cb(&info, arg);
+                       if (cb_ret) {
+                               ret = ECGOTHER;
+                               goto fail_walk;
+                       }
+               }
+               break;
+       case FTS_F:
+       case FTS_DEFAULT:
+               if (type & CGROUP_WALK_TYPE_FILE) {
+                       info.type = CGROUP_FILE_TYPE_FILE;
+                       cb_ret = cb(&info, arg);
+                       if (cb_ret) {
+                               ret = ECGOTHER;
+                               goto fail_walk;
+                       }
+               }
+               break;
+       }
+fail_walk:
+       return ret;
+}
+
+/*
+ * TODO: Need to decide a better place to put this function.
+ */
+int cgroup_walk_tree(char *controller, enum cgroup_walk_type type,
+                       cgroup_walk_callback cb, void *arg)
+{
+       int ret = 0;
+       dbg("path is %s\n", path);
+       char *cg_path[2];
+       char full_path[FILENAME_MAX];
+
+       if (!cg_build_path("/", full_path, controller))
+               return ECGOTHER;
+
+       cg_path[0] = full_path;
+       cg_path[1] = NULL;
+
+       FTS *fts = fts_open(cg_path, FTS_LOGICAL | FTS_NOCHDIR |
+                               FTS_NOSTAT, NULL);
+       while (1) {
+               FTSENT *ent;
+               ent = fts_read(fts);
+               if (!ent) {
+                       dbg("fts_read failed\n");
+                       break;
+               }
+               ret = cg_walk_node(fts, ent, type, cb, arg);
+       }
+       fts_close(fts);
+       return ret;
+}
diff --git a/libcgroup.h b/libcgroup.h
index 4fb99ff..1ea5361 100644
--- a/libcgroup.h
+++ b/libcgroup.h
@@ -97,6 +97,35 @@ enum cgroup_errors {
        ECGSENTINEL,    /* Please insert further error codes above this */
 };
 
+/*
+ * 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 */
+       CGROUP_WALK_TYPE_FILE = 0x4,    /* Post Order Directory */
+       CGROUP_WALK_TYPE_ALL = 0x5,     /* directories (preorder) and files */
+};
+
+enum cgroup_file_type {
+       CGROUP_FILE_TYPE_FILE,          /* File */
+       CGROUP_FILE_TYPE_DIR,           /* Directory */
+       CGROUP_FILE_TYPE_SL,            /* Symbolic Link */
+};
+
+struct cgroup_file_info {
+       enum cgroup_file_type type;
+       const char *path;
+       const char *parent;
+       const char *full_path;
+       short depth;
+};
+
+typedef void *(*cgroup_walk_callback)(const struct cgroup_file_info *info,
+                                       void *arg);
+
 #define CG_NV_MAX 100
 #define CG_CONTROLLER_MAX 100
 #define CG_VALUE_MAX 100
@@ -195,6 +224,18 @@ int cgroup_get_current_controller_path(pid_t pid, const 
char *controller,
  */
 char *cgroup_strerror(int code);
 
+/**
+ * Walk through the directory tree for the specified controller.
+ * @controller: Name of the controller, for which we want to walk
+ * the directory tree
+ * @type: Walk through files or directories, specify walk order as well
+ * @cb: Callback, provides information filled by the iterator and
+ * allows implementation of customic logic
+ * @arg: Custom argument to be used in the callback
+ */
+int cgroup_walk_tree(char *controller, enum cgroup_walk_type type,
+                       cgroup_walk_callback cb, void *arg);
+
 /* The wrappers for filling libcg structures */
 
 struct cgroup *cgroup_new_cgroup(const char *name);
diff --git a/libcgroup.map b/libcgroup.map
index 3b55ff2..11abea6 100644
--- a/libcgroup.map
+++ b/libcgroup.map
@@ -45,3 +45,9 @@ CGROUP_0.32.1 {
 global:
        cgroup_strerror;
 } CGROUP_0.32;
+
+CGROUP_0.33 {
+global:
+       cgroup_walk_tree;
+} 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..cd50b5b
--- /dev/null
+++ b/tests/walk_test.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <libcgroup.h>
+
+void *visit_node(const struct cgroup_file_info *info, void *arg)
+{
+       printf("Visiting %s, parent %s\n", info->path, info->parent);
+       if (info->type == CGROUP_FILE_TYPE_DIR)
+               printf("Node is a directory\n");
+       else
+               printf("Node is a file\n");
+       printf("Full Path is %s\n", info->full_path);
+       return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+       int ret;
+       char *controller;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage %s: <controller name>\n",
+                       argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       controller = argv[1];
+
+       cgroup_init();
+
+       printf("Walking post dir\n");
+       ret = cgroup_walk_tree(controller, CGROUP_WALK_TYPE_POST_DIR,
+                               visit_node, NULL);
+       if (ret)
+               printf("Cannot walk %s, check if controller "
+                       "is mounted\n", controller);
+
+       printf("Walking pre dir\n");
+       ret = cgroup_walk_tree(controller, CGROUP_WALK_TYPE_PRE_DIR, visit_node,
+                               NULL);
+       if (ret)
+               printf("Cannot walk %s, check if controller "
+                       "is mounted\n", controller);
+
+       printf("Walking all files post dir\n");
+       ret = cgroup_walk_tree(controller, CGROUP_WALK_TYPE_ALL, visit_node,
+                               NULL);
+       if (ret)
+               printf("Cannot walk %s, check if controller "
+                       "is mounted\n", controller);
+
+       printf("Walking files\n");
+       ret = cgroup_walk_tree(controller, CGROUP_WALK_TYPE_FILE, visit_node,
+                               NULL);
+       if (ret)
+               printf("Cannot walk %s, check if controller "
+                       "is mounted\n", controller);
+
+       return EXIT_SUCCESS;
+}

-- 
        Balbir

------------------------------------------------------------------------------
Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA
-OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise
-Strategies to boost innovation and cut costs with open source participation
-Receive a $600 discount off the registration fee with the source code: SFAD
http://p.sf.net/sfu/XcvMzF8H
_______________________________________________
Libcg-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libcg-devel

Reply via email to