# HG changeset patch # User Louis Opter # Date 1293661694 -3600 # Node ID 6ce4443e75455b96265269823f848a55dfd3494c Add the draft of an API to collect statistics on LXC diff -r 000000000000 -r 6ce4443e7545 .hgignore --- /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$ diff -r 000000000000 -r 6ce4443e7545 CMakeLists.txt --- /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) diff -r 000000000000 -r 6ce4443e7545 _lxcstats.h --- /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 diff -r 000000000000 -r 6ce4443e7545 close.c --- /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 + +#include "_lxcstats.h" +#include "lxcstats.h" + +int +lxcst_close(lxcst_handle *hdl) +{ + if (hdl) { + free(hdl->cgroup_dir); + free(hdl); + } + + return (0); +} diff -r 000000000000 -r 6ce4443e7545 compat/compat.h --- /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 diff -r 000000000000 -r 6ce4443e7545 compat/strlcat.c --- /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 + * + * 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 +#include + +/* + * 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 */ +} diff -r 000000000000 -r 6ce4443e7545 compat/strlcpy.c --- /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 + * + * 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 +#include + +/* + * 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 */ +} diff -r 000000000000 -r 6ce4443e7545 container.c --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff -r 000000000000 -r 6ce4443e7545 lxcstats.h --- /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 + +/** + * 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 diff -r 000000000000 -r 6ce4443e7545 open.c --- /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 +#include +#include +#include +#include +#include + +#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); +} diff -r 000000000000 -r 6ce4443e7545 probes/CMakeLists.txt --- /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) diff -r 000000000000 -r 6ce4443e7545 probes/cpuacct.c --- /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 + +#include +#include +#include +#include +#include +#include + +#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)); +} diff -r 000000000000 -r 6ce4443e7545 probes/probes.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 + +#include "lxcstats.h" +#include "probes.h" + +const struct _lxcst_probe _lxcst_probes[] = { + { &_lxcst_probe_cpuacct, "cpuacct" }, + { NULL, NULL } +}; diff -r 000000000000 -r 6ce4443e7545 probes/probes.h --- /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 diff -r 000000000000 -r 6ce4443e7545 probes/tests/CMakeLists.txt --- /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}) diff -r 000000000000 -r 6ce4443e7545 probes/tests/cpuacct.c --- /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 + +#include +#include +#include + +#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); +} diff -r 000000000000 -r 6ce4443e7545 tests/CMakeLists.txt --- /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}) diff -r 000000000000 -r 6ce4443e7545 tests/close.c --- /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 +#include + +#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); +} diff -r 000000000000 -r 6ce4443e7545 tests/open.c --- /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 +#include +#include + +#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; +} diff -r 000000000000 -r 6ce4443e7545 tests/read_big_file.c --- /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 +#include +#include + +#include +#include +#include +#include +#include + +#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); +} diff -r 000000000000 -r 6ce4443e7545 tests/read_file.c --- /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 + +#include +#include +#include +#include +#include + +#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); +} diff -r 000000000000 -r 6ce4443e7545 tests/span_containers.c --- /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 +#include +#include + +#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; +} diff -r 000000000000 -r 6ce4443e7545 utils.c --- /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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}