diff -Nru lxcfs-0.7/configure lxcfs-0.9/configure --- lxcfs-0.7/configure 2015-04-03 20:09:51.000000000 +0000 +++ lxcfs-0.9/configure 2015-05-09 00:53:25.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for lxcfs 0.7. +# Generated by GNU Autoconf 2.69 for lxcfs 0.9. # # Report bugs to . # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='lxcfs' PACKAGE_TARNAME='lxcfs' -PACKAGE_VERSION='0.7' -PACKAGE_STRING='lxcfs 0.7' +PACKAGE_VERSION='0.9' +PACKAGE_STRING='lxcfs 0.9' PACKAGE_BUGREPORT='lxc-devel@lists.linuxcontainers.org' PACKAGE_URL='' @@ -1342,7 +1342,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures lxcfs 0.7 to adapt to many kinds of systems. +\`configure' configures lxcfs 0.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1412,7 +1412,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of lxcfs 0.7:";; + short | recursive ) echo "Configuration of lxcfs 0.9:";; esac cat <<\_ACEOF @@ -1536,7 +1536,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -lxcfs configure 0.7 +lxcfs configure 0.9 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1905,7 +1905,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by lxcfs $as_me 0.7, which was +It was created by lxcfs $as_me 0.9, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -4148,7 +4148,7 @@ # Define the identity of the package. PACKAGE='lxcfs' - VERSION='0.7' + VERSION='0.9' cat >>confdefs.h <<_ACEOF @@ -13685,7 +13685,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by lxcfs $as_me 0.7, which was +This file was extended by lxcfs $as_me 0.9, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13751,7 +13751,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -lxcfs config.status 0.7 +lxcfs config.status 0.9 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru lxcfs-0.7/configure.ac lxcfs-0.9/configure.ac --- lxcfs-0.7/configure.ac 2015-04-03 20:08:52.000000000 +0000 +++ lxcfs-0.9/configure.ac 2015-05-09 00:52:09.000000000 +0000 @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([lxcfs], [0.7], [lxc-devel@lists.linuxcontainers.org]) +AC_INIT([lxcfs], [0.9], [lxc-devel@lists.linuxcontainers.org]) AC_SUBST(ACLOCAL_AMFLAGS, "-I m4") AC_CONFIG_MACRO_DIR([m4]) diff -Nru lxcfs-0.7/cpuset.c lxcfs-0.9/cpuset.c --- lxcfs-0.7/cpuset.c 1970-01-01 00:00:00.000000000 +0000 +++ lxcfs-0.9/cpuset.c 2015-05-07 17:24:02.000000000 +0000 @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +/* + * Helper functions for cpuset_in-set + */ +static char *cpuset_nexttok(const char *c) +{ + char *r = strchr(c+1, ','); + if (r) + return r+1; + return NULL; +} + +static int cpuset_getrange(const char *c, int *a, int *b) +{ + int ret; + + ret = sscanf(c, "%d-%d", a, b); + return ret; +} + +/* + * cpusets are in format "1,2-3,4" + * iow, comma-delimited ranges + */ +bool cpu_in_cpuset(int cpu, const char *cpuset) +{ + const char *c; + + for (c = cpuset; c; c = cpuset_nexttok(c)) { + int a, b, ret; + + ret = cpuset_getrange(c, &a, &b); + if (ret == 1 && cpu == a) // "1" or "1,6" + return true; + else if (ret == 2 && cpu >= a && cpu <= b) // range match + return true; + } + + return false; +} + diff -Nru lxcfs-0.7/debian/changelog lxcfs-0.9/debian/changelog --- lxcfs-0.7/debian/changelog 2015-04-21 13:42:28.000000000 +0000 +++ lxcfs-0.9/debian/changelog 2015-05-14 14:01:55.000000000 +0000 @@ -1,8 +1,23 @@ -lxcfs (0.7-0ubuntu4~ubuntu14.04.1~ppa1) trusty; urgency=medium +lxcfs (0.9-0ubuntu1~ubuntu14.04.1~ppa1) trusty; urgency=medium * No-change backport to trusty - -- Chris Townsend Tue, 21 Apr 2015 09:42:28 -0400 + -- Chris Townsend Thu, 14 May 2015 10:01:55 -0400 + +lxcfs (0.9-0ubuntu1) wily; urgency=medium + + * New upstream release avoiding fuse 'too many bytes' read errors + * Drop now-unneeded fuse arguments from the init scripts. + + -- Serge Hallyn Fri, 08 May 2015 19:56:14 -0500 + +lxcfs (0.8-0ubuntu1) wily; urgency=medium + + * New upstream release. + - drop upstream patches + - fixes lxcfs.1 (LP: #1452578) + + -- Serge Hallyn Thu, 07 May 2015 14:47:12 -0500 lxcfs (0.7-0ubuntu4) vivid; urgency=medium diff -Nru lxcfs-0.7/debian/lxcfs.init lxcfs-0.9/debian/lxcfs.init --- lxcfs-0.7/debian/lxcfs.init 2015-04-07 20:20:56.000000000 +0000 +++ lxcfs-0.9/debian/lxcfs.init 2015-05-09 00:55:51.000000000 +0000 @@ -37,7 +37,7 @@ sed -i "/^lxcfs \/var\/lib\/lxcfs fuse.lxcfs/d" /etc/mtab echo -n "Starting $DESC: " - if start-stop-daemon ${START} -- -f -s -o allow_other /var/lib/lxcfs >/dev/null 2>&1 ; then + if start-stop-daemon ${START} -- /var/lib/lxcfs >/dev/null 2>&1 ; then echo "${NAME}." else if start-stop-daemon --test ${START} >/dev/null 2>&1; then diff -Nru lxcfs-0.7/debian/lxcfs.service lxcfs-0.9/debian/lxcfs.service --- lxcfs-0.7/debian/lxcfs.service 2015-04-07 20:20:56.000000000 +0000 +++ lxcfs-0.9/debian/lxcfs.service 2015-05-09 00:56:12.000000000 +0000 @@ -6,7 +6,7 @@ Requires=cgmanager.service [Service] -ExecStart=/usr/bin/lxcfs -f -s -o allow_other /var/lib/lxcfs/ +ExecStart=/usr/bin/lxcfs /var/lib/lxcfs/ KillMode=process Restart=on-failure ExecStopPost=-/bin/fusermount -u /var/lib/lxcfs diff -Nru lxcfs-0.7/debian/lxcfs.upstart lxcfs-0.9/debian/lxcfs.upstart --- lxcfs-0.7/debian/lxcfs.upstart 2015-04-07 20:20:56.000000000 +0000 +++ lxcfs-0.9/debian/lxcfs.upstart 2015-05-09 00:56:02.000000000 +0000 @@ -10,7 +10,7 @@ [ ! -d /var/lib/lxcfs/proc ] || { stop; exit 0; } end script -exec /usr/bin/lxcfs -s -f -o allow_other /var/lib/lxcfs +exec /usr/bin/lxcfs /var/lib/lxcfs post-stop script # Cleanup in case of crash diff -Nru lxcfs-0.7/debian/patches/0001-LXC-hook-fixes.patch lxcfs-0.9/debian/patches/0001-LXC-hook-fixes.patch --- lxcfs-0.7/debian/patches/0001-LXC-hook-fixes.patch 2015-04-07 20:20:56.000000000 +0000 +++ lxcfs-0.9/debian/patches/0001-LXC-hook-fixes.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -From 288bdc12a7dd59602884b5c0bb630a5efd5b8922 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?St=C3=A9phane=20Graber?= -Date: Tue, 7 Apr 2015 16:17:40 -0400 -Subject: LXC hook fixes -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - - - Detects whether cgroups are enabled in the container's config - - Don't mount a second tmpfs on /sys/fs/cgroup - -Signed-off-by: Stéphane Graber ---- - share/lxc.mount.hook.in | 20 +++++++++----------- - 1 file changed, 9 insertions(+), 11 deletions(-) - -diff --git a/share/lxc.mount.hook.in b/share/lxc.mount.hook.in -index f9cbea0..c8916ef 100755 ---- a/share/lxc.mount.hook.in -+++ b/share/lxc.mount.hook.in -@@ -1,5 +1,14 @@ - #!/bin/sh -eu - -+# Don't mess with containers that don't have /sys/fs/cgroup configured -+# (lxc.mount.auto = cgroup:mixed) -+ -+if touch ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/lxcfs; then -+ rm ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/lxcfs -+else -+ exit 0 -+fi -+ - # /proc files - if [ -d @LXCFSTARGETDIR@/proc/ ]; then - for entry in @LXCFSTARGETDIR@/proc/*; do -@@ -10,17 +19,6 @@ fi - - # /sys/fs/cgroup files - if [ -d "${LXC_ROOTFS_MOUNT}/sys/fs/cgroup" ]; then -- mount -n -t tmpfs none ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/ -o size=4k,mode=755 -- if [ -d /sys/fs/cgroup/cgmanager.lower ]; then -- mkdir ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager -- mount -n --bind /sys/fs/cgroup/cgmanager.lower \ -- ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager -- elif [ -d /sys/fs/cgroup/cgmanager ]; then -- mkdir ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager -- mount -n --bind /sys/fs/cgroup/cgmanager \ -- ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager -- fi -- - if [ -d @LXCFSTARGETDIR@/cgroup ]; then - for entry in @LXCFSTARGETDIR@/cgroup/*; do - DEST=$(basename $entry) diff -Nru lxcfs-0.7/debian/patches/0002-Make-sure-that-that-cgroup-and-the-controller-are-se.patch lxcfs-0.9/debian/patches/0002-Make-sure-that-that-cgroup-and-the-controller-are-se.patch --- lxcfs-0.7/debian/patches/0002-Make-sure-that-that-cgroup-and-the-controller-are-se.patch 2015-04-19 13:45:56.000000000 +0000 +++ lxcfs-0.9/debian/patches/0002-Make-sure-that-that-cgroup-and-the-controller-are-se.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -From ac5d9d48767f7a580d45f743afc902497adeb4d8 Mon Sep 17 00:00:00 2001 -From: Serge Hallyn -Date: Sat, 18 Apr 2015 23:03:45 -0500 -Subject: [PATCH 1/2] Make sure that that '/cgroup' and the controller are - sep'd by / - -Signed-off-by: Serge Hallyn ---- - lxcfs.c | 2 ++ - 1 file changed, 2 insertions(+) - -Index: lxcfs-0.7/lxcfs.c -=================================================================== ---- lxcfs-0.7.orig/lxcfs.c -+++ lxcfs-0.7/lxcfs.c -@@ -313,6 +313,8 @@ static char *pick_controller_from_path(s - - if (strlen(path) < 9) - return NULL; -+ if (*(path+7) != '/') -+ return NULL; - p1 = path+8; - ret = nih_strdup(NULL, p1); - if (!ret) diff -Nru lxcfs-0.7/debian/patches/0003-free-d-at-program-end.patch lxcfs-0.9/debian/patches/0003-free-d-at-program-end.patch --- lxcfs-0.7/debian/patches/0003-free-d-at-program-end.patch 2015-04-19 13:47:08.000000000 +0000 +++ lxcfs-0.9/debian/patches/0003-free-d-at-program-end.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -From e5d26e0be93698fb99f81d2c6d472124a982a22d Mon Sep 17 00:00:00 2001 -From: Serge Hallyn -Date: Sat, 18 Apr 2015 23:51:03 -0500 -Subject: [PATCH 2/2] free d at program end - -to silence warnings from memory checkers - -Signed-off-by: Serge Hallyn ---- - lxcfs.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -Index: lxcfs-0.7/lxcfs.c -=================================================================== ---- lxcfs-0.7.orig/lxcfs.c -+++ lxcfs-0.7/lxcfs.c -@@ -2393,7 +2393,7 @@ static bool is_help(char *w) - int main(int argc, char *argv[]) - { - int ret; -- struct lxcfs_state *d; -+ struct lxcfs_state *d = NULL; - - if (argc < 2 || is_help(argv[1])) - usage(argv[0]); -@@ -2410,5 +2410,6 @@ int main(int argc, char *argv[]) - - ret = fuse_main(argc, argv, &lxcfs_ops, d); - -+ free(d); - return ret; - } diff -Nru lxcfs-0.7/debian/patches/0004-Add-some-more-sanity-checks.patch lxcfs-0.9/debian/patches/0004-Add-some-more-sanity-checks.patch --- lxcfs-0.7/debian/patches/0004-Add-some-more-sanity-checks.patch 2015-04-20 13:57:43.000000000 +0000 +++ lxcfs-0.9/debian/patches/0004-Add-some-more-sanity-checks.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -From 356e35c054bcb06687aad7b8805bc6ab9517adf0 Mon Sep 17 00:00:00 2001 -From: Serge Hallyn -Date: Mon, 20 Apr 2015 08:40:39 -0500 -Subject: [PATCH 1/1] Add some more sanity checks - -Limit string size in sscanf, convert some sprintfs to snprintfs -(to make them future-proof), and free d in case of error. - -Signed-off-by: Serge Hallyn ---- - lxcfs.c | 18 ++++++++++++------ - 1 file changed, 12 insertions(+), 6 deletions(-) - -Index: lxcfs-0.7/lxcfs.c -=================================================================== ---- lxcfs-0.7.orig/lxcfs.c -+++ lxcfs-0.7/lxcfs.c -@@ -846,7 +846,9 @@ static void pid_to_ns_wrapper(int sock, - fd_set s; - char v; - -- sprintf(fnam, "/proc/%d/ns/pid", tpid); -+ ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid); -+ if (ret < 0 || ret >= sizeof(fnam)) -+ exit(1); - newnsfd = open(fnam, O_RDONLY); - if (newnsfd < 0) - exit(1); -@@ -1107,7 +1109,9 @@ static void pid_from_ns_wrapper(int sock - struct timeval tv; - char v; - -- sprintf(fnam, "/proc/%d/ns/pid", tpid); -+ ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid); -+ if (ret < 0 || ret >= sizeof(fnam)) -+ exit(1); - newnsfd = open(fnam, O_RDONLY); - if (newnsfd < 0) - exit(1); -@@ -1857,7 +1861,10 @@ static long int get_pid1_time(pid_t pid) - return 0; - } - -- sprintf(fnam, "/proc/%d/ns/pid", pid); -+ ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", pid); -+ if (ret < 0 || ret >= sizeof(fnam)) -+ return 0; -+ - fd = open(fnam, O_RDONLY); - if (fd < 0) { - perror("get_pid1_time open of ns/pid"); -@@ -2045,7 +2052,7 @@ static int proc_diskstats_read(char *buf - size_t l; - char *printme, lbuf[256]; - -- i = sscanf(line, "%u %u %s", &major, &minor, dev_name); -+ i = sscanf(line, "%u %u %71s", &major, &minor, dev_name); - if(i == 3){ - get_blkio_io_value(io_serviced_str, major, minor, "Read", &read); - get_blkio_io_value(io_serviced_str, major, minor, "Write", &write); -@@ -2392,7 +2399,7 @@ static bool is_help(char *w) - - int main(int argc, char *argv[]) - { -- int ret; -+ int ret = -1; - struct lxcfs_state *d = NULL; - - if (argc < 2 || is_help(argv[1])) -@@ -2406,10 +2413,11 @@ int main(int argc, char *argv[]) - fprintf(stderr, "WARNING: failed to escape to root cgroup\n"); - - if (!cgm_get_controllers(&d->subsystems)) -- return -1; -+ goto out; - - ret = fuse_main(argc, argv, &lxcfs_ops, d); - -+out: - free(d); - return ret; - } diff -Nru lxcfs-0.7/debian/patches/series lxcfs-0.9/debian/patches/series --- lxcfs-0.7/debian/patches/series 2015-04-20 13:47:37.000000000 +0000 +++ lxcfs-0.9/debian/patches/series 2015-05-07 19:51:10.000000000 +0000 @@ -1,4 +0,0 @@ -0001-LXC-hook-fixes.patch -0002-Make-sure-that-that-cgroup-and-the-controller-are-se.patch -0003-free-d-at-program-end.patch -0004-Add-some-more-sanity-checks.patch diff -Nru lxcfs-0.7/lxcfs.c lxcfs-0.9/lxcfs.c --- lxcfs-0.7/lxcfs.c 2015-04-03 19:57:21.000000000 +0000 +++ lxcfs-0.9/lxcfs.c 2015-05-09 00:49:47.000000000 +0000 @@ -6,10 +6,6 @@ * See COPYING file for details. */ -/* - * NOTES - make sure to run this as -s to avoid threading. - * TODO - can we enforce that here from the code? - */ #define FUSE_USE_VERSION 26 #include @@ -33,6 +29,7 @@ #include #include "cgmanager.h" +#include "config.h" // for VERSION struct lxcfs_state { /* @@ -43,6 +40,36 @@ }; #define LXCFS_DATA ((struct lxcfs_state *) fuse_get_context()->private_data) +enum { + LXC_TYPE_CGDIR, + LXC_TYPE_CGFILE, + LXC_TYPE_PROC_MEMINFO, + LXC_TYPE_PROC_CPUINFO, + LXC_TYPE_PROC_UPTIME, + LXC_TYPE_PROC_STAT, + LXC_TYPE_PROC_DISKSTATS, +}; + +struct file_info { + char *controller; + char *cgroup; + char *file; + int type; + char *buf; // unused as of yet + int buflen; + int size; //actual data size +}; + +/* reserve buffer size, for cpuall in /proc/stat */ +#define BUF_RESERVE_SIZE 256 + +static char *must_copy_string(void *parent, const char *str) +{ + if (!str) + return NULL; + return NIH_MUST( nih_strdup(parent, str) ); +} + /* * TODO - return value should denote whether child exited with failure * so callers can return errors. Esp read/write of tasks and cgroup.procs @@ -313,6 +340,8 @@ if (strlen(path) < 9) return NULL; + if (*(path+7) != '/') + return NULL; p1 = path+8; ret = nih_strdup(NULL, p1); if (!ret) @@ -412,16 +441,6 @@ *p = '\0'; } -static size_t get_file_size(const char *contrl, const char *cg, const char *f) -{ - nih_local char *data = NULL; - size_t s; - if (!cgm_get_value(contrl, cg, f, &data)) - return -EINVAL; - s = strlen(data); - return s; -} - /* * FUSE ops for /cgroup */ @@ -514,7 +533,7 @@ sb->st_nlink = 1; sb->st_uid = k->uid; sb->st_gid = k->gid; - sb->st_size = get_file_size(controller, path1, path2); + sb->st_size = 0; return 0; } @@ -530,41 +549,59 @@ struct fuse_context *fc = fuse_get_context(); nih_local struct cgm_keys **list = NULL; const char *cgroup; + struct file_info *dir_info; nih_local char *controller = NULL; - nih_local char *nextcg = NULL; if (!fc) return -EIO; - if (strcmp(path, "/cgroup") == 0) - return 0; - - // return list of keys for the controller, and list of child cgroups - controller = pick_controller_from_path(fc, path); - if (!controller) - return -EIO; + if (strcmp(path, "/cgroup") == 0) { + cgroup = NULL; + controller = NULL; + } else { + // return list of keys for the controller, and list of child cgroups + controller = pick_controller_from_path(fc, path); + if (!controller) + return -EIO; - cgroup = find_cgroup_in_path(path); - if (!cgroup) { - /* this is just /cgroup/controller, return its contents */ - cgroup = "/"; + cgroup = find_cgroup_in_path(path); + if (!cgroup) { + /* this is just /cgroup/controller, return its contents */ + cgroup = "/"; + } } - if (!fc_may_access(fc, controller, cgroup, NULL, O_RDONLY)) + if (cgroup && !fc_may_access(fc, controller, cgroup, NULL, O_RDONLY)) return -EACCES; + + /* we'll free this at cg_releasedir */ + dir_info = NIH_MUST( nih_alloc(NULL, sizeof(*dir_info)) ); + dir_info->controller = must_copy_string(dir_info, controller); + dir_info->cgroup = must_copy_string(dir_info, cgroup); + dir_info->type = LXC_TYPE_CGDIR; + dir_info->buf = NULL; + dir_info->file = NULL; + dir_info->buflen = 0; + + fi->fh = (unsigned long)dir_info; return 0; } static int cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { + struct file_info *d = (struct file_info *)fi->fh; + nih_local struct cgm_keys **list = NULL; + int i; + nih_local char *nextcg = NULL; struct fuse_context *fc = fuse_get_context(); - if (!fc) + if (d->type != LXC_TYPE_CGDIR) { + fprintf(stderr, "Internal error: file cache info used in readdir\n"); return -EIO; - - if (strcmp(path, "/cgroup") == 0) { - // get list of controllers + } + if (!d->cgroup && !d->controller) { + // ls /var/lib/lxcfs/cgroup - just show list of controllers char **list = LXCFS_DATA ? LXCFS_DATA->subsystems : NULL; int i; @@ -579,31 +616,11 @@ return 0; } - // return list of keys for the controller, and list of child cgroups - nih_local struct cgm_keys **list = NULL; - const char *cgroup; - nih_local char *controller = NULL; - int i; - nih_local char *nextcg = NULL; - - controller = pick_controller_from_path(fc, path); - if (!controller) - return -EIO; - - cgroup = find_cgroup_in_path(path); - if (!cgroup) { - /* this is just /cgroup/controller, return its contents */ - cgroup = "/"; - } - - if (!fc_may_access(fc, controller, cgroup, NULL, O_RDONLY)) - return -EACCES; - - if (!cgm_list_keys(controller, cgroup, &list)) + if (!cgm_list_keys(d->controller, d->cgroup, &list)) // not a valid cgroup return -EINVAL; - if (!caller_is_in_ancestor(fc->pid, controller, cgroup, &nextcg)) { + if (!caller_is_in_ancestor(fc->pid, d->controller, d->cgroup, &nextcg)) { if (nextcg) { int ret; ret = filler(buf, nextcg, NULL, 0); @@ -622,7 +639,7 @@ // now get the list of child cgroups nih_local char **clist = NULL; - if (!cgm_list_children(controller, cgroup, &clist)) + if (!cgm_list_children(d->controller, d->cgroup, &clist)) return 0; for (i = 0; clist[i]; i++) { if (filler(buf, clist[i], NULL, 0) != 0) { @@ -632,14 +649,23 @@ return 0; } +static void do_release_file_info(struct file_info *f) +{ + /* + * all file_info fields which are nih_alloc()d with f as parent + * will be automatically freed + */ + nih_free(f); +} + static int cg_releasedir(const char *path, struct fuse_file_info *fi) { + struct file_info *d = (struct file_info *)fi->fh; + + do_release_file_info(d); return 0; } -/* - * TODO - cache info here for read/write, release in cg_release. - */ static int cg_open(const char *path, struct fuse_file_info *fi) { nih_local char *controller = NULL; @@ -647,6 +673,7 @@ char *fpath = NULL, *path1, *path2; nih_local char * cgdir = NULL; nih_local struct cgm_keys *k = NULL; + struct file_info *file_info; struct fuse_context *fc = fuse_get_context(); if (!fc) @@ -668,15 +695,33 @@ path2 = fpath; } - if ((k = get_cgroup_key(controller, path1, path2)) != NULL) { - if (!fc_may_access(fc, controller, path1, path2, fi->flags)) - // should never get here - return -EACCES; + k = get_cgroup_key(controller, path1, path2); + if (!k) + return -EINVAL; - return 0; - } + if (!fc_may_access(fc, controller, path1, path2, fi->flags)) + // should never get here + return -EACCES; - return -EINVAL; + /* we'll free this at cg_release */ + file_info = NIH_MUST( nih_alloc(NULL, sizeof(*file_info)) ); + file_info->controller = must_copy_string(file_info, controller); + file_info->cgroup = must_copy_string(file_info, path1); + file_info->file = must_copy_string(file_info, path2); + file_info->type = LXC_TYPE_CGFILE; + file_info->buf = NULL; + file_info->buflen = 0; + + fi->fh = (unsigned long)file_info; + return 0; +} + +static int cg_release(const char *path, struct fuse_file_info *fi) +{ + struct file_info *f = (struct file_info *)fi->fh; + + do_release_file_info(f); + return 0; } static int msgrecv(int sockfd, void *buf, size_t len) @@ -844,7 +889,9 @@ fd_set s; char v; - sprintf(fnam, "/proc/%d/ns/pid", tpid); + ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid); + if (ret < 0 || ret >= sizeof(fnam)) + exit(1); newnsfd = open(fnam, O_RDONLY); if (newnsfd < 0) exit(1); @@ -992,52 +1039,41 @@ static int cg_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - nih_local char *controller = NULL; - const char *cgroup; - char *fpath = NULL, *path1, *path2; struct fuse_context *fc = fuse_get_context(); - nih_local char * cgdir = NULL; + struct file_info *f = (struct file_info *)fi->fh; nih_local struct cgm_keys *k = NULL; - if (offset) + if (f->type != LXC_TYPE_CGFILE) { + fprintf(stderr, "Internal error: directory cache info used in cg_read\n"); return -EIO; + } + + if (offset) + return 0; if (!fc) return -EIO; - controller = pick_controller_from_path(fc, path); - if (!controller) + if (!f->controller) return -EINVAL; - cgroup = find_cgroup_in_path(path); - if (!cgroup) - return -EINVAL; - - get_cgdir_and_path(cgroup, &cgdir, &fpath); - if (!fpath) { - path1 = "/"; - path2 = cgdir; - } else { - path1 = cgdir; - path2 = fpath; - } - if ((k = get_cgroup_key(controller, path1, path2)) != NULL) { + if ((k = get_cgroup_key(f->controller, f->cgroup, f->file)) != NULL) { nih_local char *data = NULL; int s; bool r; - if (!fc_may_access(fc, controller, path1, path2, O_RDONLY)) + if (!fc_may_access(fc, f->controller, f->cgroup, f->file, O_RDONLY)) // should never get here return -EACCES; - if (strcmp(path2, "tasks") == 0 || - strcmp(path2, "/tasks") == 0 || - strcmp(path2, "/cgroup.procs") == 0 || - strcmp(path2, "cgroup.procs") == 0) + if (strcmp(f->file, "tasks") == 0 || + strcmp(f->file, "/tasks") == 0 || + strcmp(f->file, "/cgroup.procs") == 0 || + strcmp(f->file, "cgroup.procs") == 0) // special case - we have to translate the pids - r = do_read_pids(fc->pid, controller, path1, path2, &data); + r = do_read_pids(fc->pid, f->controller, f->cgroup, f->file, &data); else - r = cgm_get_value(controller, path1, path2, &data); + r = cgm_get_value(f->controller, f->cgroup, f->file, &data); if (!r) return -EINVAL; @@ -1048,6 +1084,8 @@ if (s > size) s = size; memcpy(buf, data, s); + if (s > 0 && s < size && data[s-1] != '\n') + buf[s++] = '\n'; return s; } @@ -1105,7 +1143,9 @@ struct timeval tv; char v; - sprintf(fnam, "/proc/%d/ns/pid", tpid); + ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", tpid); + if (ret < 0 || ret >= sizeof(fnam)) + exit(1); newnsfd = open(fnam, O_RDONLY); if (newnsfd < 0) exit(1); @@ -1224,16 +1264,18 @@ int cg_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - nih_local char *controller = NULL; - const char *cgroup; - char *fpath = NULL, *path1, *path2; struct fuse_context *fc = fuse_get_context(); - nih_local char * cgdir = NULL; - nih_local struct cgm_keys *k = NULL; nih_local char *localbuf = NULL; + nih_local struct cgm_keys *k = NULL; + struct file_info *f = (struct file_info *)fi->fh; + + if (f->type != LXC_TYPE_CGFILE) { + fprintf(stderr, "Internal error: directory cache info used in cg_write\n"); + return -EIO; + } if (offset) - return -EINVAL; + return 0; if (!fc) return -EIO; @@ -1241,36 +1283,21 @@ localbuf = NIH_MUST( nih_alloc(NULL, size+1) ); localbuf[size] = '\0'; memcpy(localbuf, buf, size); - controller = pick_controller_from_path(fc, path); - if (!controller) - return -EINVAL; - cgroup = find_cgroup_in_path(path); - if (!cgroup) - return -EINVAL; - get_cgdir_and_path(cgroup, &cgdir, &fpath); - if (!fpath) { - path1 = "/"; - path2 = cgdir; - } else { - path1 = cgdir; - path2 = fpath; - } - - if ((k = get_cgroup_key(controller, path1, path2)) != NULL) { + if ((k = get_cgroup_key(f->controller, f->cgroup, f->file)) != NULL) { bool r; - if (!fc_may_access(fc, controller, path1, path2, O_WRONLY)) + if (!fc_may_access(fc, f->controller, f->cgroup, f->file, O_WRONLY)) return -EACCES; - if (strcmp(path2, "tasks") == 0 || - strcmp(path2, "/tasks") == 0 || - strcmp(path2, "/cgroup.procs") == 0 || - strcmp(path2, "cgroup.procs") == 0) + if (strcmp(f->file, "tasks") == 0 || + strcmp(f->file, "/tasks") == 0 || + strcmp(f->file, "/cgroup.procs") == 0 || + strcmp(f->file, "cgroup.procs") == 0) // special case - we have to translate the pids - r = do_write_pids(fc->pid, controller, path1, path2, localbuf); + r = do_write_pids(fc->pid, f->controller, f->cgroup, f->file, localbuf); else - r = cgm_set_value(controller, path1, path2, localbuf); + r = cgm_set_value(f->controller, f->cgroup, f->file, localbuf); if (!r) return -EINVAL; @@ -1495,24 +1522,24 @@ } static void get_blkio_io_value(char *str, unsigned major, unsigned minor, char *iotype, unsigned long *v) -{ +{ char *eol; char key[32]; - + memset(key, 0, 32); snprintf(key, 32, "%u:%u %s", major, minor, iotype); - + size_t len = strlen(key); *v = 0; while (*str) { if (startswith(str, key)) { - sscanf(str + len, "%lu", v); - return; - } - eol = strchr(str, '\n'); + sscanf(str + len, "%lu", v); + return; + } + eol = strchr(str, '\n'); if (!eol) - return; + return; str = eol+1; } } @@ -1563,15 +1590,24 @@ struct fuse_file_info *fi) { struct fuse_context *fc = fuse_get_context(); + struct file_info *d = (struct file_info *)fi->fh; nih_local char *cg = get_pid_cgroup(fc->pid, "memory"); nih_local char *memlimit_str = NULL, *memusage_str = NULL, *memstat_str = NULL; unsigned long memlimit = 0, memusage = 0, cached = 0, hosttotal = 0; char *line = NULL; - size_t linelen = 0, total_len = 0; + size_t linelen = 0, total_len = 0, rv = 0; + char *cache = d->buf; + size_t cache_size = d->buflen; FILE *f; - if (offset) - return -EINVAL; + if (offset){ + if (offset > d->size) + return -EINVAL; + int left = d->size - offset; + total_len = left > size ? size: left; + memcpy(buf, cache + offset, total_len); + return total_len; + } if (!cg) return 0; @@ -1620,15 +1656,34 @@ printme = lbuf; } else printme = line; - l = snprintf(buf, size, "%s", printme); - buf += l; - size -= l; + + l = snprintf(cache, cache_size, "%s", printme); + if (l < 0) { + perror("Error writing to cache"); + rv = 0; + goto err; + + } + if (l >= cache_size) { + fprintf(stderr, "Internal error: truncated write to cache\n"); + rv = 0; + goto err; + } + + cache += l; + cache_size -= l; total_len += l; } + d->size = total_len; + if (total_len > size ) total_len = size; + memcpy(buf, d->buf, total_len); + + rv = total_len; + err: fclose(f); free(line); - return total_len; + return rv; } /* @@ -1644,47 +1699,7 @@ return answer; } -/* - * Helper functions for cpuset_in-set - */ -char *cpuset_nexttok(const char *c) -{ - char *r = strchr(c+1, ','); - if (r) - return r+1; - return NULL; -} - -int cpuset_getrange(const char *c, int *a, int *b) -{ - int ret; - - ret = sscanf(c, "%d-%d", a, b); - return ret; -} - -/* - * cpusets are in format "1,2-3,4" - * iow, comma-delimited ranges - */ -static bool cpu_in_cpuset(int cpu, const char *cpuset) -{ - const char *c; - - for (c = cpuset; c; c = cpuset_nexttok(c)) { - int a, b, ret; - - ret = cpuset_getrange(c, &a, &b); - if (ret == 1 && cpu == a) - return true; - if (ret != 2) // bad cpuset! - return false; - if (cpu >= a && cpu <= b) - return true; - } - - return false; -} +bool cpu_in_cpuset(int cpu, const char *cpuset); static bool cpuline_in_cpuset(const char *line, const char *cpuset) { @@ -1711,16 +1726,25 @@ struct fuse_file_info *fi) { struct fuse_context *fc = fuse_get_context(); + struct file_info *d = (struct file_info *)fi->fh; nih_local char *cg = get_pid_cgroup(fc->pid, "cpuset"); nih_local char *cpuset = NULL; char *line = NULL; - size_t linelen = 0, total_len = 0; + size_t linelen = 0, total_len = 0, rv = 0; bool am_printing = false; int curcpu = -1; + char *cache = d->buf; + size_t cache_size = d->buflen; FILE *f; - if (offset) - return -EINVAL; + if (offset){ + if (offset > d->size) + return -EINVAL; + int left = d->size - offset; + total_len = left > size ? size: left; + memcpy(buf, cache + offset, total_len); + return total_len; + } if (!cg) return 0; @@ -1739,39 +1763,95 @@ am_printing = cpuline_in_cpuset(line, cpuset); if (am_printing) { curcpu ++; - l = snprintf(buf, size, "processor : %d\n", curcpu); - buf += l; - size -= l; - total_len += l; + l = snprintf(cache, cache_size, "processor : %d\n", curcpu); + if (l < 0) { + perror("Error writing to cache"); + rv = 0; + goto err; + } + if (l >= cache_size) { + fprintf(stderr, "Internal error: truncated write to cache\n"); + rv = 0; + goto err; + } + if (l < cache_size){ + cache += l; + cache_size -= l; + total_len += l; + }else{ + cache += cache_size; + total_len += cache_size; + cache_size = 0; + break; + } } continue; } if (am_printing) { - l = snprintf(buf, size, "%s", line); - buf += l; - size -= l; - total_len += l; + l = snprintf(cache, cache_size, "%s", line); + if (l < 0) { + perror("Error writing to cache"); + rv = 0; + goto err; + } + if (l >= cache_size) { + fprintf(stderr, "Internal error: truncated write to cache\n"); + rv = 0; + goto err; + } + if (l < cache_size) { + cache += l; + cache_size -= l; + total_len += l; + } else { + cache += cache_size; + total_len += cache_size; + cache_size = 0; + break; + } } } + d->size = total_len; + if (total_len > size ) total_len = size; + + /* read from off 0 */ + memcpy(buf, d->buf, total_len); + rv = total_len; + err: fclose(f); free(line); - return total_len; + return rv; } static int proc_stat_read(char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct fuse_context *fc = fuse_get_context(); + struct file_info *d = (struct file_info *)fi->fh; nih_local char *cg = get_pid_cgroup(fc->pid, "cpuset"); nih_local char *cpuset = NULL; char *line = NULL; - size_t linelen = 0, total_len = 0; + size_t linelen = 0, total_len = 0, rv = 0; int curcpu = -1; /* cpu numbering starts at 0 */ + unsigned long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0; + unsigned long user_sum = 0, nice_sum = 0, system_sum = 0, idle_sum = 0, iowait_sum = 0, + irq_sum = 0, softirq_sum = 0, steal_sum = 0, guest_sum = 0; +#define CPUALL_MAX_SIZE BUF_RESERVE_SIZE + char cpuall[CPUALL_MAX_SIZE]; + /* reserve for cpu all */ + char *cache = d->buf + CPUALL_MAX_SIZE; + size_t cache_size = d->buflen - CPUALL_MAX_SIZE; FILE *f; - if (offset) - return -EINVAL; + if (offset){ + if (offset > d->size) + return -EINVAL; + int left = d->size - offset; + total_len = left > size ? size: left; + memcpy(buf, d->buf + offset, total_len); + return total_len; + } if (!cg) return 0; @@ -1784,6 +1864,12 @@ if (!f) return 0; + //skip first line + if (getline(&line, &linelen, f) < 0) { + fprintf(stderr, "proc_stat_read read first line failed\n"); + goto out; + } + while (getline(&line, &linelen, f) != -1) { size_t l; int cpu; @@ -1792,11 +1878,29 @@ if (sscanf(line, "cpu%9[^ ]", cpu_char) != 1) { /* not a ^cpuN line containing a number N, just print it */ - l = snprintf(buf, size, "%s", line); - buf += l; - size -= l; - total_len += l; - continue; + l = snprintf(cache, cache_size, "%s", line); + if (l < 0) { + perror("Error writing to cache"); + rv = 0; + goto err; + } + if (l >= cache_size) { + fprintf(stderr, "Internal error: truncated write to cache\n"); + rv = 0; + goto err; + } + if (l < cache_size) { + cache += l; + cache_size -= l; + total_len += l; + continue; + } else { + //no more space, break it + cache += cache_size; + total_len += cache_size; + cache_size = 0; + break; + } } if (sscanf(cpu_char, "%d", &cpu) != 1) @@ -1808,15 +1912,62 @@ c = strchr(line, ' '); if (!c) continue; - l = snprintf(buf, size, "cpu%d %s", curcpu, c); - buf += l; - size -= l; + l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c); + if (l < 0) { + perror("Error writing to cache"); + rv = 0; + goto err; + + } + if (l >= cache_size) { + fprintf(stderr, "Internal error: truncated write to cache\n"); + rv = 0; + goto err; + } + + cache += l; + cache_size -= l; total_len += l; - } + if (sscanf(line, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu", &user, &nice, &system, &idle, &iowait, &irq, + &softirq, &steal, &guest) != 9) + continue; + user_sum += user; + nice_sum += nice; + system_sum += system; + idle_sum += idle; + iowait_sum += iowait; + irq_sum += irq; + softirq_sum += softirq; + steal_sum += steal; + guest_sum += guest; + } + + cache = d->buf; + + int cpuall_len = snprintf(cpuall, CPUALL_MAX_SIZE, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", + "cpu ", user_sum, nice_sum, system_sum, idle_sum, iowait_sum, irq_sum, softirq_sum, steal_sum, guest_sum); + if (cpuall_len > 0 && cpuall_len < CPUALL_MAX_SIZE){ + memcpy(cache, cpuall, cpuall_len); + cache += cpuall_len; + }else{ + /* shouldn't happen */ + fprintf(stderr, "proc_stat_read copy cpuall failed, cpuall_len=%d\n", cpuall_len); + cpuall_len = 0; + } + + memmove(cache, d->buf + CPUALL_MAX_SIZE, total_len); + total_len += cpuall_len; + d->size = total_len; + if (total_len > size ) total_len = size; + + memcpy(buf, d->buf, total_len); + out: + rv = total_len; + err: fclose(f); free(line); - return total_len; + return rv; } /* @@ -1855,7 +2006,10 @@ return 0; } - sprintf(fnam, "/proc/%d/ns/pid", pid); + ret = snprintf(fnam, sizeof(fnam), "/proc/%d/ns/pid", pid); + if (ret < 0 || ret >= sizeof(fnam)) + return 0; + fd = open(fnam, O_RDONLY); if (fd < 0) { perror("get_pid1_time open of ns/pid"); @@ -1989,12 +2143,29 @@ struct fuse_file_info *fi) { struct fuse_context *fc = fuse_get_context(); + struct file_info *d = (struct file_info *)fi->fh; long int reaperage = getreaperage(fc->pid);; long int idletime = getprocidle(); + size_t total_len = 0; - if (offset) - return -EINVAL; - return snprintf(buf, size, "%ld %ld\n", reaperage, idletime); + if (offset){ + if (offset > d->size) + return -EINVAL; + return 0; + } + + total_len = snprintf(buf, size, "%ld %ld\n", reaperage, idletime); + if (total_len < 0){ + perror("Error writing to cache"); + return 0; + } + if (total_len >= size){ + d->size = size; + return size; + } + + d->size = total_len; + return total_len; } static int proc_diskstats_read(char *buf, size_t size, off_t offset, @@ -2002,6 +2173,7 @@ { char dev_name[72]; struct fuse_context *fc = fuse_get_context(); + struct file_info *d = (struct file_info *)fi->fh; nih_local char *cg = get_pid_cgroup(fc->pid, "blkio"); nih_local char *io_serviced_str = NULL, *io_merged_str = NULL, *io_service_bytes_str = NULL, *io_wait_time_str = NULL, *io_service_time_str = NULL; @@ -2012,13 +2184,16 @@ unsigned long ios_pgr = 0, tot_ticks = 0, rq_ticks = 0; unsigned long rd_svctm = 0, wr_svctm = 0, rd_wait = 0, wr_wait = 0; char *line = NULL; - size_t linelen = 0, total_len = 0; + size_t linelen = 0, total_len = 0, rv = 0; unsigned int major = 0, minor = 0; int i = 0; FILE *f; - if (offset) - return -EINVAL; + if (offset){ + if (offset > d->size) + return -EINVAL; + return 0; + } if (!cg) return 0; @@ -2043,7 +2218,7 @@ size_t l; char *printme, lbuf[256]; - i = sscanf(line, "%u %u %s", &major, &minor, dev_name); + i = sscanf(line, "%u %u %71s", &major, &minor, dev_name); if(i == 3){ get_blkio_io_value(io_serviced_str, major, minor, "Read", &read); get_blkio_io_value(io_serviced_str, major, minor, "Write", &write); @@ -2053,7 +2228,7 @@ read_sectors = read_sectors/512; get_blkio_io_value(io_service_bytes_str, major, minor, "Write", &write_sectors); write_sectors = write_sectors/512; - + get_blkio_io_value(io_service_time_str, major, minor, "Read", &rd_svctm); rd_svctm = rd_svctm/1000000; get_blkio_io_value(io_wait_time_str, major, minor, "Read", &rd_wait); @@ -2074,7 +2249,7 @@ memset(lbuf, 0, 256); if (read || write || read_merged || write_merged || read_sectors || write_sectors || read_ticks || write_ticks) { - snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", + snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", major, minor, dev_name, read, read_merged, read_sectors, read_ticks, write, write_merged, write_sectors, write_ticks, ios_pgr, tot_ticks, rq_ticks); printme = lbuf; @@ -2082,14 +2257,27 @@ continue; l = snprintf(buf, size, "%s", printme); + if (l < 0) { + perror("Error writing to fuse buf"); + rv = 0; + goto err; + } + if (l >= size) { + fprintf(stderr, "Internal error: truncated write to cache\n"); + rv = 0; + goto err; + } buf += l; size -= l; total_len += l; } + d->size = total_len; + rv = total_len; + err: fclose(f); free(line); - return total_len; + return rv; } static off_t get_procfile_size(const char *which) @@ -2128,7 +2316,7 @@ strcmp(path, "/proc/uptime") == 0 || strcmp(path, "/proc/stat") == 0 || strcmp(path, "/proc/diskstats") == 0) { - sb->st_size = get_procfile_size(path); + sb->st_size = 0; sb->st_mode = S_IFREG | 00444; sb->st_nlink = 1; return 0; @@ -2151,29 +2339,63 @@ static int proc_open(const char *path, struct fuse_file_info *fi) { - if (strcmp(path, "/proc/meminfo") == 0 || - strcmp(path, "/proc/cpuinfo") == 0 || - strcmp(path, "/proc/uptime") == 0 || - strcmp(path, "/proc/stat") == 0 || - strcmp(path, "/proc/diskstats") == 0) - return 0; - return -ENOENT; + int type = -1; + struct file_info *info; + + if (strcmp(path, "/proc/meminfo") == 0) + type = LXC_TYPE_PROC_MEMINFO; + else if (strcmp(path, "/proc/cpuinfo") == 0) + type = LXC_TYPE_PROC_CPUINFO; + else if (strcmp(path, "/proc/uptime") == 0) + type = LXC_TYPE_PROC_UPTIME; + else if (strcmp(path, "/proc/stat") == 0) + type = LXC_TYPE_PROC_STAT; + else if (strcmp(path, "/proc/diskstats") == 0) + type = LXC_TYPE_PROC_DISKSTATS; + if (type == -1) + return -ENOENT; + + info = NIH_MUST( nih_alloc(NULL, sizeof(*info)) ); + memset(info, 0, sizeof(*info)); + info->type = type; + + info->buflen = get_procfile_size(path) + BUF_RESERVE_SIZE; + info->buf = NIH_MUST( nih_alloc(info, info->buflen) ); + memset(info->buf, 0, info->buflen); + /* set actual size to buffer size */ + info->size = info->buflen; + + fi->fh = (unsigned long)info; + return 0; +} + +static int proc_release(const char *path, struct fuse_file_info *fi) +{ + struct file_info *f = (struct file_info *)fi->fh; + + do_release_file_info(f); + return 0; } static int proc_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - if (strcmp(path, "/proc/meminfo") == 0) + struct file_info *f = (struct file_info *) fi->fh; + + switch (f->type) { + case LXC_TYPE_PROC_MEMINFO: return proc_meminfo_read(buf, size, offset, fi); - if (strcmp(path, "/proc/cpuinfo") == 0) + case LXC_TYPE_PROC_CPUINFO: return proc_cpuinfo_read(buf, size, offset, fi); - if (strcmp(path, "/proc/uptime") == 0) + case LXC_TYPE_PROC_UPTIME: return proc_uptime_read(buf, size, offset, fi); - if (strcmp(path, "/proc/stat") == 0) + case LXC_TYPE_PROC_STAT: return proc_stat_read(buf, size, offset, fi); - if (strcmp(path, "/proc/diskstats") == 0) + case LXC_TYPE_PROC_DISKSTATS: return proc_diskstats_read(buf, size, offset, fi); - return -EINVAL; + default: + return -EINVAL; + } } /* @@ -2277,7 +2499,12 @@ static int lxcfs_release(const char *path, struct fuse_file_info *fi) { - return 0; + if (strncmp(path, "/cgroup", 7) == 0) + return cg_release(path, fi); + if (strncmp(path, "/proc", 5) == 0) + return proc_release(path, fi); + + return -EINVAL; } static int lxcfs_fsync(const char *path, int datasync, struct fuse_file_info *fi) @@ -2374,7 +2601,8 @@ { fprintf(stderr, "Usage:\n"); fprintf(stderr, "\n"); - fprintf(stderr, "%s [FUSE and mount options] mountpoint\n", me); + fprintf(stderr, "%s mountpoint\n", me); + fprintf(stderr, "%s -h\n", me); exit(1); } @@ -2388,25 +2616,84 @@ return false; } +void swallow_arg(int *argcp, char *argv[], char *which) +{ + int i; + + for (i = 1; argv[i]; i++) { + if (strcmp(argv[i], which) != 0) + continue; + for (; argv[i]; i++) { + argv[i] = argv[i+1]; + } + (*argcp)--; + return; + } +} + +void swallow_option(int *argcp, char *argv[], char *opt, char *v) +{ + int i; + + for (i = 1; argv[i]; i++) { + if (!argv[i+1]) + continue; + if (strcmp(argv[i], opt) != 0) + continue; + if (strcmp(argv[i+1], v) != 0) { + fprintf(stderr, "Warning: unexpected fuse option %s\n", v); + exit(1); + } + for (; argv[i+1]; i++) { + argv[i] = argv[i+2]; + } + (*argcp) -= 2; + return; + } +} + int main(int argc, char *argv[]) { - int ret; - struct lxcfs_state *d; + int ret = -1; + struct lxcfs_state *d = NULL; + /* + * what we pass to fuse_main is: + * argv[0] -s -f -o allow_other,directio argv[1] NULL + */ +#define NARGS 7 + char *newargv[7]; - if (argc < 2 || is_help(argv[1])) + /* accomodate older init scripts */ + swallow_arg(&argc, argv, "-s"); + swallow_arg(&argc, argv, "-f"); + swallow_option(&argc, argv, "-o", "allow_other"); + + if (argc == 2 && strcmp(argv[1], "--version") == 0) { + fprintf(stderr, "%s\n", VERSION); + exit(0); + } + if (argc != 2 || is_help(argv[1])) usage(argv[0]); - d = malloc(sizeof(*d)); - if (!d) - return -1; + d = NIH_MUST( malloc(sizeof(*d)) ); + + newargv[0] = argv[0]; + newargv[1] = "-s"; + newargv[2] = "-f"; + newargv[3] = "-o"; + newargv[4] = "allow_other,direct_io"; + newargv[5] = argv[1]; + newargv[6] = NULL; if (!cgm_escape_cgroup()) fprintf(stderr, "WARNING: failed to escape to root cgroup\n"); if (!cgm_get_controllers(&d->subsystems)) - return -1; + goto out; - ret = fuse_main(argc, argv, &lxcfs_ops, d); + ret = fuse_main(NARGS - 1, newargv, &lxcfs_ops, d); +out: + free(d); return ret; } diff -Nru lxcfs-0.7/Makefile.am lxcfs-0.9/Makefile.am --- lxcfs-0.7/Makefile.am 2015-01-23 18:30:53.000000000 +0000 +++ lxcfs-0.9/Makefile.am 2015-05-07 17:24:02.000000000 +0000 @@ -13,7 +13,7 @@ bin_PROGRAMS = lxcfs -lxcfs_SOURCES = lxcfs.c cgmanager.c cgmanager.h +lxcfs_SOURCES = lxcfs.c cgmanager.c cgmanager.h cpuset.c EXTRA_DIST = \ lxcfs.man.add @@ -25,6 +25,14 @@ $(HELP2MAN) -n "Set up cgroup fs for containers" --no-discard-stderr -s 1 -I lxcfs.man.add -N ./lxcfs > lxcfs.1 endif +TEST_READ: tests/test-read.c + $(CC) -o tests/test-read tests/test-read.c + +TEST_CPUSET: tests/cpusetrange.c cpuset.c + $(CC) -o tests/cpusetrange tests/cpusetrange.c cpuset.c + +tests: TEST_READ TEST_CPUSET + distclean: rm -rf .deps/ \ INSTALL \ diff -Nru lxcfs-0.7/Makefile.in lxcfs-0.9/Makefile.in --- lxcfs-0.7/Makefile.in 2015-04-03 20:09:51.000000000 +0000 +++ lxcfs-0.9/Makefile.in 2015-05-09 00:53:25.000000000 +0000 @@ -100,7 +100,8 @@ CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" PROGRAMS = $(bin_PROGRAMS) -am_lxcfs_OBJECTS = lxcfs.$(OBJEXT) cgmanager.$(OBJEXT) +am_lxcfs_OBJECTS = lxcfs.$(OBJEXT) cgmanager.$(OBJEXT) \ + cpuset.$(OBJEXT) lxcfs_OBJECTS = $(am_lxcfs_OBJECTS) lxcfs_LDADD = $(LDADD) AM_V_lt = $(am__v_lt_@AM_V@) @@ -398,7 +399,7 @@ $(DBUS_CFLAGS) $(NIH_CFLAGS) $(NIH_DBUS_CFLAGS) \ $(CGMANAGER_CFLAGS) $(FUSE_CFLAGS) AM_LDFLAGS = $(DBUS_LIBS) $(NIH_LIBS) $(NIH_DBUS_LIBS) $(CGMANAGER_LIBS) $(FUSE_LIBS) -lxcfs_SOURCES = lxcfs.c cgmanager.c cgmanager.h +lxcfs_SOURCES = lxcfs.c cgmanager.c cgmanager.h cpuset.c EXTRA_DIST = \ lxcfs.man.add @@ -518,6 +519,7 @@ -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgmanager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpuset.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lxcfs.Po@am__quote@ .c.o: @@ -1021,6 +1023,14 @@ @HAVE_HELP2MAN_TRUE@lxcfs.1: lxcfs lxcfs.man.add @HAVE_HELP2MAN_TRUE@ $(HELP2MAN) -n "Set up cgroup fs for containers" --no-discard-stderr -s 1 -I lxcfs.man.add -N ./lxcfs > lxcfs.1 +TEST_READ: tests/test-read.c + $(CC) -o tests/test-read tests/test-read.c + +TEST_CPUSET: tests/cpusetrange.c cpuset.c + $(CC) -o tests/cpusetrange tests/cpusetrange.c cpuset.c + +tests: TEST_READ TEST_CPUSET + distclean: rm -rf .deps/ \ INSTALL \ diff -Nru lxcfs-0.7/share/lxc.mount.hook.in lxcfs-0.9/share/lxc.mount.hook.in --- lxcfs-0.7/share/lxc.mount.hook.in 2015-02-09 20:16:54.000000000 +0000 +++ lxcfs-0.9/share/lxc.mount.hook.in 2015-04-10 18:52:17.000000000 +0000 @@ -1,5 +1,14 @@ #!/bin/sh -eu +# Don't mess with containers that don't have /sys/fs/cgroup configured +# (lxc.mount.auto = cgroup:mixed) + +if touch ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/lxcfs; then + rm ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/lxcfs +else + exit 0 +fi + # /proc files if [ -d @LXCFSTARGETDIR@/proc/ ]; then for entry in @LXCFSTARGETDIR@/proc/*; do @@ -10,17 +19,6 @@ # /sys/fs/cgroup files if [ -d "${LXC_ROOTFS_MOUNT}/sys/fs/cgroup" ]; then - mount -n -t tmpfs none ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/ -o size=4k,mode=755 - if [ -d /sys/fs/cgroup/cgmanager.lower ]; then - mkdir ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager - mount -n --bind /sys/fs/cgroup/cgmanager.lower \ - ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager - elif [ -d /sys/fs/cgroup/cgmanager ]; then - mkdir ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager - mount -n --bind /sys/fs/cgroup/cgmanager \ - ${LXC_ROOTFS_MOUNT}/sys/fs/cgroup/cgmanager - fi - if [ -d @LXCFSTARGETDIR@/cgroup ]; then for entry in @LXCFSTARGETDIR@/cgroup/*; do DEST=$(basename $entry)