changeset 0:6ce4443e7545

Add the draft of an API to collect statistics on LXC
author Louis Opter <kalessin@kalessin.fr>
date Wed, 29 Dec 2010 23:28:14 +0100
parents
children 3e30c3b2d6da
files .hgignore CMakeLists.txt _lxcstats.h close.c compat/compat.h compat/strlcat.c compat/strlcpy.c container.c lxcstats.h open.c probes/CMakeLists.txt probes/cpuacct.c probes/probes.c probes/probes.h probes/tests/CMakeLists.txt probes/tests/cpuacct.c tests/CMakeLists.txt tests/close.c tests/open.c tests/read_big_file.c tests/read_file.c tests/span_containers.c utils.c
diffstat 23 files changed, 925 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,3 @@
+\.sw.$
+^build/.+
+Session\.vim$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CMakeLists.txt	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,33 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+PROJECT(LIBLXCSTATS C)
+
+ENABLE_TESTING()
+
+IF (CMAKE_COMPILER_IS_GNUCC)
+    ADD_DEFINITIONS("-pipe -Wextra -Wall -std=c99 -Wstrict-prototypes")
+ENDIF (CMAKE_COMPILER_IS_GNUCC)
+
+IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
+    ADD_DEFINITIONS("-D_POSIX_C_SOURCE=200112L" "-D_XOPEN_SOURCE=500" "-D_FILE_OFFSET_BITS=64" "-D_BSD_SOURCE")
+ELSE (CMAKE_SYSTEM_NAME MATCHES "Linux")
+    MESSAGE(SEND_ERROR "The liblxcstats is Linux specific.")
+ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
+
+SET(SRC
+    compat/strlcpy.c
+    compat/strlcat.c
+    close.c
+    container.c
+    open.c
+    utils.c
+   )
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
+ADD_LIBRARY(lxcstats SHARED ${SRC})
+
+TARGET_LINK_LIBRARIES(lxcstats probes)
+
+ADD_SUBDIRECTORY(tests)
+ADD_SUBDIRECTORY(probes)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_lxcstats.h	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,53 @@
+#ifndef _LXC_STATS_PRIV_H_
+# define _LXC_STATS_PRIV_H_
+
+# include "compat/compat.h"
+# include "probes/probes.h"
+
+struct      dirent;
+
+/**
+ * Store all informations needed to get statistics from a cgroup
+ * hierarchy.
+ */
+struct      _lxcst_controller {
+    char    *cgroup_dir; /**< Mount point of the cgroup hierarchy. */
+};
+
+/**
+ * Allocate a new lxcst structure for the given container.
+ */
+struct lxcst    *_lxcst_container_new(struct _lxcst_controller *, const char *);
+
+/**
+ * Free a lxcst structure.
+ */
+void            _lxcst_container_delete(struct lxcst *);
+
+/**
+ * Get statistics on a container.
+ */
+int             _lxcst_container_read_infos(struct lxcst *);
+
+char            *_lxcst_join_path(char *, size_t , const char *, const char *);
+
+/**
+ * Used to select only directories with scandir. We can't directly use it with
+ * scandir because we need the cgroup_dir.
+ *
+ * @return true if the given directory entry is a directory.
+ */
+int             _lxcst_isdir(const struct _lxcst_controller *, const struct dirent *);
+
+/**
+ * Allocate a buffer and read an entire file in it.
+ *
+ * @param [in] path Path to the file to read
+ * @param [out] content A pointer to a pointer which will hold the allocated
+ * buffer.
+ *
+ * @return The size of the buffer on sucess, -1 with errno set otherwise.
+ */
+ssize_t         _lxcst_read_file(const char *path, char **content);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/close.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+
+#include "_lxcstats.h"
+#include "lxcstats.h"
+
+int
+lxcst_close(lxcst_handle *hdl)
+{
+    if (hdl) {
+        free(hdl->cgroup_dir);
+        free(hdl);
+    }
+
+    return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compat/compat.h	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,9 @@
+#ifndef _LXC_STATS_COMPAT_H_
+# define _LXC_STATS_COMPAT_H_
+
+/* Courtesy of OpenBSD 4.8 */
+
+size_t  _lxcst_strlcat(char *dst, const char *src, size_t siz);
+size_t  _lxcst_strlcpy(char *dst, const char *src, size_t siz);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compat/strlcat.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,55 @@
+/*	$OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+_lxcst_strlcat(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/compat/strlcpy.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,51 @@
+/*	$OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+_lxcst_strlcpy(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0) {
+		while (--n != 0) {
+			if ((*d++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/container.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,113 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lxcstats.h"
+#include "_lxcstats.h"
+
+static int
+_lxcst_unselect_current_and_parent(const struct dirent* d)
+{
+    return (strcmp(d->d_name, ".") && strcmp(d->d_name, ".."));
+}
+
+struct lxcst *
+_lxcst_container_new(struct _lxcst_controller *ct, const char *name)
+{
+    char            buf[256];
+    struct lxcst    *c;
+
+    assert(ct);
+    assert(name);
+
+    printf("new container %s\n", name);
+
+    c = calloc(1, sizeof(*c));
+    if (!c)
+        return (NULL);
+
+    _lxcst_join_path(buf, sizeof(buf), ct->cgroup_dir, name);
+
+    c->name = strdup(name);
+    c->cgroup_dir = strdup(buf);
+    if (!c->name || !c->cgroup_dir)
+        goto free_container;
+
+    return (c);
+
+free_container:
+    free(c->name);
+    free(c);
+    return (NULL);
+}
+
+void
+_lxcst_container_delete(struct lxcst *c)
+{
+    if (c) {
+        free(c->name);
+        free(c->cgroup_dir);
+        free(c->cpuacct.percpu);
+        free(c);
+    }
+}
+
+int
+_lxcst_container_read_infos(struct lxcst *c)
+{
+    int i;
+
+    assert(c);
+
+    for (i = 0; _lxcst_probes[i].fn; ++i)
+        if (_lxcst_probes[i].fn((c)))
+            warn("%s probe failed for container %s", _lxcst_probes[i].name, c->name);
+
+    return (0);
+}
+
+int
+lxcst_span_containers(lxcst_handle *hdl, int (*cb)(void *, const struct lxcst *), void *ctx)
+{
+    int             n;
+    struct dirent   **c_vec;
+    struct lxcst    *c;
+
+    assert(hdl);
+    assert(hdl->cgroup_dir);
+    assert(cb);
+
+    n = scandir(hdl->cgroup_dir, &c_vec, &_lxcst_unselect_current_and_parent, NULL);
+    if (n > 0) {
+        while (n--) {
+            if (_lxcst_isdir(hdl, c_vec[n])) {
+                c = _lxcst_container_new(hdl, c_vec[n]->d_name);
+                if (c && _lxcst_container_read_infos(c) == 0
+                    && cb(ctx, c)) {
+                    errno = EINTR;
+                    goto abort_by_cb;
+                }
+            }
+            free(c_vec[n]);
+        }
+        free(c_vec);
+    } else if (n < 0) {
+        return (-1);
+    }
+
+    return (0);
+
+abort_by_cb:
+    free(c_vec[n + 1]);
+    while (n--)
+        free(c_vec[n]);
+    free(c_vec);
+    return (-1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lxcstats.h	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,67 @@
+#ifndef _LXC_STATS_H_
+# define _LXC_STATS_H_
+
+# include <stdint.h>
+
+/**
+ * Opaque structure used as a context for all liblxcst functions.
+ */
+typedef struct _lxcst_controller    lxcst_handle;
+
+/**
+ * This structure contains available statistics on a container.
+ */
+struct              lxcst {
+    char            *name;      /*< Name of the container                               */
+    char            *cgroup_dir;/*< cgroup directory of the container                   */
+    struct  {
+        uint32_t    user;       /*< CPU time spent in userland                          */
+        uint32_t    system;     /*< CPU time spent in kernelland                        */
+        uint64_t    *percpu;    /*< CPU time per CPU (zero-terminated array or NULL)    */
+    }               cpuacct;
+};
+
+/**
+ * This structure contains globals statistics on LXC.
+ *
+ * Note: see how lxc-info gets these infos.
+ */
+struct          lxcst_globals {
+    uint32_t    running_containers;
+    uint32_t    stopped_containers;
+};
+
+/**
+ * @brief Initialize the library.
+ *
+ * @return A pointer to an opaque structure which can be free'd with free
+ * or NULL on failure with errno set.
+ *
+ * @see lxcst_close
+ */
+lxcst_handle    *lxcst_open(void);
+
+/**
+ * @brief Close the library.
+ *
+ * @param [in] hdl The pointer returned by lxcst_open.
+ *
+ * @return 0 on success, -1 if an error occured with errno set.
+ */
+int             lxcst_close(lxcst_handle *hdl);
+
+/**
+ * @brief Collect statistic on all containers.
+ *
+ * @param [in] hdl The pointer returned by lxcst_handle.
+ * @param [in] cb A function pointer to a callback which takes a pointer to an
+ * user context and a pointer to lxcst structure.
+ * @param [in] ctx A pointer to an user defined variable which will be used as
+ * an argument to the callback. If the callback returns non zero
+ * lxcst_span_containers stops and return with -1 with errno set to EINTR.
+ *
+ * @return 0 on success, -1 on error with errno set.
+ */
+int             lxcst_span_containers(lxcst_handle *hdl, int (*cb)(void *, const struct lxcst *), void *ctx);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/open.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,56 @@
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <mntent.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "_lxcstats.h"
+#include "lxcstats.h"
+
+static char *
+lxcst_cgroup_mount_point(void)
+{
+    FILE            *mtab;
+    struct mntent   mntbuf;
+    char            strbuf[1024];
+    bool            found = false;
+
+    mtab = fopen("/etc/mtab", "r");
+    if (!mtab)
+        return (NULL);
+
+    while (getmntent_r(mtab, &mntbuf, strbuf, sizeof(strbuf))) {
+        if (!strcmp(mntbuf.mnt_type, "cgroup")) {
+            found = true;
+            break ;
+        }
+    }
+
+    fclose(mtab);
+
+    if (found)
+        return (strdup(mntbuf.mnt_dir));
+
+    errno = ENOENT;
+    return (NULL);
+}
+
+lxcst_handle    *lxcst_open(void)
+{
+    struct _lxcst_controller    *c;
+
+    c = calloc(1, sizeof(*c));
+    if (!c)
+        return (NULL);
+
+    c->cgroup_dir = lxcst_cgroup_mount_point();
+    if (!c->cgroup_dir)
+        goto free_controller;
+
+    return (c);
+
+free_controller:
+    free(c);
+    return (NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/probes/CMakeLists.txt	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,9 @@
+SET(PROBES_SRC
+    cpuacct.c
+    probes.c
+   )
+
+ADD_LIBRARY(probes STATIC ${PROBES_SRC})
+SET_TARGET_PROPERTIES(probes PROPERTIES COMPILE_FLAGS "-fPIC")
+
+ADD_SUBDIRECTORY(tests)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/probes/cpuacct.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,90 @@
+#include <sys/types.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lxcstats.h"
+#include "_lxcstats.h"
+
+static int
+read_stat(struct lxcst *c)
+{
+    char    path[PATH_MAX];
+    char    *values;
+    int     ret;
+
+    _lxcst_join_path(path, sizeof(path), c->cgroup_dir, "cpuacct.stat");
+    ret = _lxcst_read_file(path, &values);
+    if (ret > 0) {
+        values[ret - 1] = '\0'; /* Replace the last \n by a \0 */
+        ret = sscanf(values, "user %u\nsystem %u", &c->cpuacct.user, &c->cpuacct.system);
+        free(values);
+        if (ret == 2)
+            return (0);
+    }
+
+    c->cpuacct.user = 0;
+    c->cpuacct.system = 0;
+    return (1);
+}
+
+static int
+read_usage_percpu(struct lxcst *c)
+{
+    char    path[PATH_MAX];
+    char    *values;
+    int     ret;
+    char    *p;
+    int     ncpus;
+    int     i;
+
+    _lxcst_join_path(path, sizeof(path), c->cgroup_dir, "cpuacct.usage_percpu");
+    ret = _lxcst_read_file(path, &values);
+    if (ret > 0) {
+        values[ret - 1] = '\0';
+
+        p = values;
+        for (ncpus = 0; ; ncpus++) {
+            p = strchr(p + 1, ' ');
+            if (!p)
+                break ;
+        }
+
+        if (ncpus)
+            c->cpuacct.percpu = calloc(ncpus + 1, sizeof(*c->cpuacct.percpu));
+
+        if (c->cpuacct.percpu) {
+            errno = 0;
+            p = values;
+            for (i = 0; i != ncpus && *p; ++i) {
+                c->cpuacct.percpu[i] = strtoll(p, &p, 10);
+                if (errno)
+                    goto err;
+                p += strcspn(p, "0123456789");
+            }
+
+            free(values);
+            return (0);
+        } 
+
+err:
+        free(c->cpuacct.percpu);
+        c->cpuacct.percpu = NULL;
+        free(values);
+    }
+
+    return (1);
+}
+
+int
+_lxcst_probe_cpuacct(struct lxcst *c)
+{
+    assert(c);
+
+    return (read_stat(c)
+            || read_usage_percpu(c));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/probes/probes.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+
+#include "lxcstats.h"
+#include "probes.h"
+
+const struct _lxcst_probe _lxcst_probes[] = {
+    { &_lxcst_probe_cpuacct, "cpuacct" },
+    { NULL, NULL }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/probes/probes.h	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,15 @@
+#ifndef _LXC_STATS_PROBES_H_
+# define _LXC_STATS_PROBES_H_
+
+struct  lxcst;
+
+struct  _lxcst_probe {
+    int         (*fn)(struct lxcst *);
+    const char  *name;
+};
+
+extern const struct _lxcst_probe _lxcst_probes[];
+
+int _lxcst_probe_cpuacct(struct lxcst *c);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/probes/tests/CMakeLists.txt	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,13 @@
+MACRO(ADD_LXCSTATS_PROBE_TEST TEST)
+    ADD_EXECUTABLE(test_probe_${TEST} ${TEST}.c)
+    TARGET_LINK_LIBRARIES(test_probe_${TEST} lxcstats)
+    ADD_TEST(probe_${TEST} test_probe_${TEST})
+ENDMACRO(ADD_LXCSTATS_PROBE_TEST TEST)
+
+SET(TESTS
+    cpuacct
+   )
+
+FOREACH(I ${TESTS})
+    ADD_LXCSTATS_PROBE_TEST(${I})
+ENDFOREACH(I ${TESTS})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/probes/tests/cpuacct.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,38 @@
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "lxcstats.h"
+#include "_lxcstats.h"
+#include "probes/probes.h"
+
+int
+main(void)
+{
+    lxcst_handle    *hdl;
+    struct lxcst    *c;
+    int             i;
+
+    hdl = lxcst_open();
+    if (!hdl)
+        err(EXIT_FAILURE, "lxcst_open failed");
+
+    c = _lxcst_container_new(hdl, ".");
+    if (!c)
+        err(EXIT_FAILURE, "cant create container");
+
+    if (_lxcst_probe_cpuacct(c))
+        err(EXIT_FAILURE, "probe cpuacct failed");
+
+    printf("user %d\nsystem %d\n", c->cpuacct.user, c->cpuacct.system);
+    for (i = 0; c->cpuacct.percpu[i]; ++i)
+        printf("cpu[%d] %lu\n", i, c->cpuacct.percpu[i]);
+
+    _lxcst_container_delete(c);
+
+    lxcst_close(hdl);
+
+    return (EXIT_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/CMakeLists.txt	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,22 @@
+# This is fairly limited for now, this will require more work to test all cases
+# like:
+# - cgroups are not mounted;
+# - there is no containers running.
+
+MACRO(ADD_LXCSTATS_TEST TEST)
+    ADD_EXECUTABLE(test_${TEST} ${TEST}.c)
+    TARGET_LINK_LIBRARIES(test_${TEST} lxcstats)
+    ADD_TEST(${TEST} test_${TEST})
+ENDMACRO(ADD_LXCSTATS_TEST TEST)
+
+SET(TESTS
+    close
+    open
+    read_file
+    read_big_file
+    span_containers
+   )
+
+FOREACH(I ${TESTS})
+    ADD_LXCSTATS_TEST(${I})
+ENDFOREACH(I ${TESTS})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/close.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,22 @@
+#include <err.h>
+#include <stdlib.h>
+
+#include "lxcstats.h"
+
+int
+main(void)
+{
+    lxcst_handle    *hdl;
+
+    if (lxcst_close(NULL) != 0)
+        errx(EXIT_FAILURE, "lxcst_close fail on NULL handle");
+
+    hdl = lxcst_open();
+    if (!hdl)
+        err(EXIT_FAILURE, "lxcst_open failed");
+
+    if (lxcst_close(hdl) != 0)
+        errx(EXIT_FAILURE, "lxcst_close fail on valid handle");
+
+    return (EXIT_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/open.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,23 @@
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "_lxcstats.h"
+#include "lxcstats.h"
+
+int
+main(int argc, char *argv[])
+{
+    lxcst_handle    *hdl;
+
+    hdl = lxcst_open();
+
+    if (hdl) {
+        printf("%s: mnt_dir=%s.\n", *argv, hdl->cgroup_dir);
+        return (EXIT_SUCCESS);
+    }
+
+    err(EXIT_FAILURE, "lxcst_open failed");
+
+    (void)argc;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/read_big_file.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,47 @@
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lxcstats.h"
+#include "_lxcstats.h"
+
+int
+main(void)
+{
+    char        *reference;
+    struct stat sb;
+    char        *trial;
+    size_t      size;
+    int         fd;
+
+    fd = open("/etc/services", O_RDONLY);
+    if (fd == -1)
+        err(EXIT_FAILURE, "can't open /etc/services");
+
+    size = _lxcst_read_file("/etc/services", &trial);
+    if (!trial)
+        err(EXIT_FAILURE, "can't read /etc/services");
+
+    if (fstat(fd, &sb) != 0)
+        err(EXIT_FAILURE, "can't stat /etc/services");
+
+    reference = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (!reference)
+        err(EXIT_FAILURE, "can't map /etc/services");
+
+    if ((size_t)sb.st_size != size)
+        errx(EXIT_FAILURE, "sizes are different");
+
+    if (memcmp(reference, trial, size))
+        errx(EXIT_FAILURE, "buffers are different");
+
+    free(trial);
+
+    return (EXIT_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/read_file.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,42 @@
+#include <sys/types.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lxcstats.h"
+#include "_lxcstats.h"
+
+int
+main(void)
+{
+    char        reference[1024];
+    int         rdsize;
+    char        *trial;
+    size_t      size;
+    int         fd;
+
+    fd = open("/etc/hostname", O_RDONLY);
+    if (fd == -1)
+        err(EXIT_FAILURE, "can't open /etc/hostname");
+
+    size = _lxcst_read_file("/etc/hostname", &trial);
+    if (!trial)
+        err(EXIT_FAILURE, "can't read /etc/hostname");
+
+    rdsize = read(fd, reference, sizeof(reference));
+    if (rdsize == -1)
+        err(EXIT_FAILURE, "can't read /etc/hostname");
+
+    if ((size_t)rdsize != size)
+        errx(EXIT_FAILURE, "sizes are different");
+
+    if (memcmp(reference, trial, size))
+        errx(EXIT_FAILURE, "buffers are different");
+
+    free(trial);
+
+    return (EXIT_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/span_containers.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,40 @@
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lxcstats.h"
+
+unsigned int n = 0;
+
+int
+cb(void *ctx, const struct lxcst *container)
+{
+    if (ctx)
+        errx(EXIT_FAILURE, "ctx must be NULL");
+
+    if (!container)
+        errx(EXIT_FAILURE, "container must not be NULL");
+
+    ++n;
+
+    return (0); /* continue */
+}
+
+int
+main(int argc, char *argv[])
+{
+    lxcst_handle   *hdl;
+
+    hdl = lxcst_open();
+    if (!hdl)
+        err(EXIT_FAILURE, "lxcst_open failed");
+
+    lxcst_span_containers(hdl, &cb, NULL);
+
+    lxcst_close(hdl);
+
+    printf("%s: spanned %d containers.\n", *argv, n);
+
+    return (EXIT_SUCCESS);
+    (void)argc;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils.c	Wed Dec 29 23:28:14 2010 +0100
@@ -0,0 +1,100 @@
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "_lxcstats.h"
+
+char *
+_lxcst_join_path(char *dest, size_t size, const char *left, const char *right)
+{
+    if (*right != '/') {
+        _lxcst_strlcpy(dest, left, size);
+        _lxcst_strlcat(dest, "/", size);
+        _lxcst_strlcat(dest, right, size);
+    } else {
+        _lxcst_strlcpy(dest, right, size);
+    }
+
+    return (dest);
+}
+
+int
+_lxcst_isdir(const struct _lxcst_controller *hdl, const struct dirent *d)
+{
+    struct stat sb;
+    char        buf[PATH_MAX];
+
+    _lxcst_join_path(buf, sizeof(buf), hdl->cgroup_dir, d->d_name);
+
+    if (stat(buf, &sb) == -1) {
+        warn("can't stat file: %s", buf);
+        return (0);
+    }
+
+    return (sb.st_mode & S_IFDIR);
+}
+
+/*
+ * Files under cgroups are virtual and always have a size of zero so we have to
+ * do some reallocs.
+ */
+ssize_t
+_lxcst_read_file(const char *path, char **content)
+{
+    char        buf[1024];
+    int         fd;
+    ssize_t     ret;
+    ssize_t     size;
+    char        *p;
+    int         sverrno;
+
+    assert(path);
+    assert(content);
+
+    *content = NULL;
+    size = 0;
+
+    fd = open(path, O_RDONLY);
+    if (fd != -1) {
+
+        while (1) {
+            ret = read(fd, buf, sizeof(buf));
+            if (ret == -1) {
+                if (errno == EINTR)
+                    continue ;
+                break ;
+            }
+            if (ret == 0)
+                break ;
+
+            p = realloc(*content, size + ret);
+            if (!p)
+                break ;
+
+            *content = p;
+            memcpy(&content[0][size], buf, ret);
+            size += ret;
+        }
+
+        if (*content) {
+            close(fd);
+            return (size);
+        }
+
+        sverrno = errno;
+        close(fd);
+        errno = sverrno;
+    }
+
+    free(*content);
+    return (ret);
+}