This patch allows to display information related to the cgroup
in /proc/meminfo.

It is a first try, there is certainly a clever way to do that but
it is just to play with it.

Compile with:

	make lxcfs CFLAGS="-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=27" LDFLAGS="-lfuse"

Launch a container as root with:

	lxc-execute -n foo /bin/bash

Set 256MB of memory:

	echo 268435456 > /cgroup/foo/memory.limit_in_bytes

And finally mount /proc/meminfo:

	./lxcfs -odirect_io /proc/meminfo

And the results are:

cat /proc/meminfo:
==================

MemTotal:      262144 kB
MemFree:       255991 kB

top:
====

top - 23:52:17 up  2:43,  2 users,  load average: 0.00, 0.00, 0.00
Tasks:   6 total,   1 running,   5 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.3%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.7%si,  0.0
Mem:    262144k total,     6153k used,   255991k free,        0k buffers
Swap:        0k total,        0k used,        0k free,        0k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND       
    1 dlezcano  20   0  8136  484  408 S  0.0  0.2   0:00.00 lxc-init      
    2 dlezcano  20   0 90112 1764 1304 S  0.0  0.7   0:00.08 bash          
   29 root      20   0  121m 1392 1128 S  0.0  0.5   0:00.05 su            
   30 root      20   0 89984 1696 1328 S  0.0  0.6   0:00.05 bash          
   68 root      20   0 37628  888  544 S  0.0  0.3   0:00.00 lxcfs         
   74 root      20   0 14748 1040  868 R  0.0  0.4   0:00.02 top           

free:
=====

             total       used       free     shared    buffers     cached
Mem:        262144       6153     255991          0          0          0
-/+ buffers/cache:       6153     255991


To be extended ...

Signed-off-by: Daniel Lezcano <daniel.lezcano@free.fr>
---
 lxcfs.c |  217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 217 insertions(+)

Index: lxcfs/lxcfs.c
===================================================================
--- /dev/null
+++ lxcfs/lxcfs.c
@@ -0,0 +1,217 @@
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mntent.h>
+#include <limits.h>
+#include <sys/param.h>
+
+#define PAGE_SIZE 4096
+#define BUFFER_SIZE 2*PAGE_SIZE
+
+struct cginfo {
+	char *cgpath;
+	char buffer[BUFFER_SIZE];
+	size_t buflen;
+};
+
+struct lxcfs_info {
+	char *name;
+	struct cginfo cginfo;
+};
+
+#ifndef LLONG_MAX
+#define LLONG_MAX    9223372036854775807LL
+#endif
+
+static int lxcfs_getattr(const char *path, struct stat *stbuf)
+{
+	struct fuse_context *context = fuse_get_context();
+
+	memset(stbuf, 0, sizeof(*stbuf));
+
+	stbuf->st_mode = S_IFREG | 0755;
+	stbuf->st_nlink = 2;
+	stbuf->st_uid = context->uid;
+	stbuf->st_gid = context->gid;
+
+	return 0;
+}
+
+static int lxcfs_open(const char *path, struct fuse_file_info *fi)
+{
+	struct fuse_context *context = fuse_get_context();
+	struct lxcfs_info *lxcfs_info = context->private_data;
+	struct cginfo *cginfo = &lxcfs_info->cginfo;
+	char limit_in_bytes[MAXPATHLEN];
+	char usage_in_bytes[MAXPATHLEN];
+
+	FILE *limit_file, *usage_file;
+	long long limit, usage;
+
+	int err;
+
+	snprintf(limit_in_bytes, MAXPATHLEN, "%s/%s/memory.limit_in_bytes",
+		 cginfo->cgpath, lxcfs_info->name);
+
+	snprintf(usage_in_bytes, MAXPATHLEN, "%s/%s/memory.usage_in_bytes",
+		 cginfo->cgpath, lxcfs_info->name);
+
+	limit_file = fopen(limit_in_bytes, "r");
+	if (!limit_file)
+		return -errno;
+
+	fscanf(limit_file, "%lld", &limit);
+	if (limit == LLONG_MAX)
+		return 0;
+
+	usage_file = fopen(usage_in_bytes, "r");
+	if (!usage_file)
+		goto out_close_limit;
+
+	fscanf(limit_file, "%lld", &limit);
+
+	cginfo->buflen = 0;
+	cginfo->buflen += sprintf(cginfo->buffer, "MemTotal:      %lld kB\n", limit >> 10);
+	cginfo->buflen += sprintf(cginfo->buffer + cginfo->buflen,
+				  "MemFree:       %lld kB\n", (limit - usage) >> 10);
+
+	err = 0;
+out:
+
+	fclose(limit_file);
+	fclose(usage_file);
+
+	return err;
+
+out_close_limit:
+	err = -errno;
+	fclose(limit_file);
+	goto out;
+}
+
+static int lxcfs_read(const char *path, char *buf, size_t size,
+		      off_t offset, struct fuse_file_info *fi)
+{
+	struct fuse_context *context = fuse_get_context();
+	struct lxcfs_info *lxcfs_info = context->private_data;
+	struct cginfo *cginfo = &lxcfs_info->cginfo;
+
+	size_t wrote = offset + size > cginfo->buflen ? cginfo->buflen - offset : size;
+
+	memcpy(buf, cginfo->buffer + offset, wrote);
+
+	return wrote;
+}
+
+static int lxcfs_release(const char *path, struct fuse_file_info *fi)
+{
+	return 0;
+}
+
+static int get_cgroup_mount(const char *mtab, char *mnt)
+{
+        struct mntent *mntent;
+        FILE *file = NULL;
+        int err = -1;
+
+        file = setmntent(mtab, "r");
+        if (!file)
+                goto out;
+
+        while ((mntent = getmntent(file))) {
+
+		/* there is a cgroup mounted named "lxc" */
+		if (!strcmp(mntent->mnt_fsname, "lxc") &&
+		    !strcmp(mntent->mnt_type, "cgroup")) {
+			strcpy(mnt, mntent->mnt_dir);
+			err = 0;
+			break;
+		}
+
+		/* fallback to the first non-lxc cgroup found */
+                if (!strcmp(mntent->mnt_type, "cgroup") && err) {
+			strcpy(mnt, mntent->mnt_dir);
+			err = 0;
+		}
+        };
+
+        fclose(file);
+out:
+        return err;
+}
+
+static char *cgroup_name(void)
+{
+	char *path = "/proc/self/cgroup", *name;
+        char line[MAXPATHLEN];
+        FILE *file;
+
+        file = fopen(path, "r");
+        if (!file)
+                return NULL;
+
+        fscanf(file, "%s", line);
+
+        strtok(line, ":");
+        strtok(NULL, ":");
+        name = strtok(NULL, ":");
+        name = strtok(name, "/");
+
+        return strdup(name);
+}
+
+static void *lxcfs_init(struct fuse_conn_info *conn)
+{
+	struct lxcfs_info *lxcfs_info;
+
+	lxcfs_info = malloc(sizeof(*lxcfs_info));
+	if (!lxcfs_info)
+		return NULL;
+
+	lxcfs_info->cginfo.cgpath = malloc(MAXPATHLEN);
+	if (!lxcfs_info->cginfo.cgpath)
+		goto out_free_info;
+
+	if (get_cgroup_mount("/etc/mtab", lxcfs_info->cginfo.cgpath))
+		if (get_cgroup_mount("/proc/mounts", lxcfs_info->cginfo.cgpath))
+			goto out_free_cgroup;
+
+	lxcfs_info->name = cgroup_name();
+	if (!lxcfs_info->name)
+		goto out_free_cgroup;
+
+out:
+	return lxcfs_info;
+
+out_free_cgroup:
+	free(lxcfs_info->cginfo.cgpath);
+out_free_info:
+	free(lxcfs_info);
+	lxcfs_info = NULL;
+	goto out;
+}
+
+static void lxcfs_destroy(void *private_data)
+{
+	struct lxcfs_info *lxcfs_info = private_data;
+
+	free(lxcfs_info->cginfo.cgpath);
+	free(lxcfs_info->name);
+	free(lxcfs_info);
+}
+
+static struct fuse_operations lxcfs_ops = {
+	.getattr = lxcfs_getattr,
+	.open    = lxcfs_open,
+	.read    = lxcfs_read,
+	.release = lxcfs_release,
+ 	.init    = lxcfs_init,
+ 	.destroy = lxcfs_destroy,
+};
+
+int main(int argc, char *argv[])
+{
+	return fuse_main(argc, argv, &lxcfs_ops, NULL);
+}



