This patch provides the ability to hook some files access and display what we want instead. Signed-off-by: Daniel Lezcano --- procfs.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 240 insertions(+), 6 deletions(-) Index: lxcfs/procfs.c =================================================================== --- lxcfs.orig/procfs.c +++ lxcfs/procfs.c @@ -17,31 +17,63 @@ enum { PROCFS_PROXY, + PROCFS_MIRROR, }; struct procfs_info { DIR *procdir; DIR *subdir; struct hsearch_data shadow_hash; + struct hsearch_data mirror_hash; +}; + +struct mirror_ops { + int (*open)(const char *, struct fuse_file_info *); + int (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *); + int (*write)(const char *, const char *, size_t, off_t, struct fuse_file_info *); + int (*release)(const char *, struct fuse_file_info *); +}; + +static int meminfo_open(const char *, struct fuse_file_info *); +static int meminfo_read(const char *, char *, size_t, off_t, struct fuse_file_info *); +static int meminfo_release(const char *, struct fuse_file_info *); + +static struct mirror_ops meminfo_ops = { + .open = meminfo_open, + .read = meminfo_read, + .release = meminfo_release, }; struct proxy_file { int fd; }; +struct mirror_file { + struct mirror_ops *ops; + void *data; +}; + struct procfs_file { int type; union { struct proxy_file proxy; + struct mirror_file mirror; } file; + + void *private_data; }; static ENTRY shadow_entries[] = { { "sys", NULL }, }; +static ENTRY mirror_entries[] = { + { "meminfo", &meminfo_ops }, +}; + #define ARRAY_SIZE(t) (sizeof(t) / sizeof(t[0])) #define SHADOW_HASH_SIZE 32 +#define MIRROR_HASH_SIZE 32 ENTRY *hash_find(char *key, struct hsearch_data *hash) { @@ -78,6 +110,8 @@ static int procfs_getattr(const char *pa struct fuse_context *context = fuse_get_context(); struct procfs_info *fsinfo = context->private_data; char *bname = strchr(path, '/'); + struct mirror_ops *ops; + ENTRY *entry; if (strcmp(path, "/")) bname += 1; @@ -92,8 +126,9 @@ static int procfs_open(const char *path, { struct fuse_context *context = fuse_get_context(); struct procfs_info *fsinfo = context->private_data; - char *bname = strchr(path, '/'); struct procfs_file *pfile; + char *bname = strchr(path, '/'); + ENTRY *entry; if (strcmp(path, "/")) bname += 1; @@ -105,6 +140,16 @@ static int procfs_open(const char *path, if (!pfile) return -ENOMEM; + fi->fh = (typeof(fi->fh))pfile; + + entry = hash_find(bname, &fsinfo->mirror_hash); + if (entry) { + pfile->type = PROCFS_MIRROR; + pfile->file.mirror.ops = entry->data; + if (pfile->file.mirror.ops->open) + return pfile->file.mirror.ops->open(path, fi); + } + pfile->type = PROCFS_PROXY; pfile->file.proxy.fd = openat(dirfd(fsinfo->procdir), bname, fi->flags); if (pfile->file.proxy.fd < 0) { @@ -112,8 +157,6 @@ static int procfs_open(const char *path, return -errno; } - fi->fh = (typeof(fi->fh))pfile; - return 0; } @@ -121,10 +164,14 @@ static int procfs_read(const char *path, off_t offset, struct fuse_file_info *fi) { int ret; - struct procfs_file *pfile = (typeof(pfile))fi->fh;; + struct procfs_file *pfile = (typeof(pfile))fi->fh; switch (pfile->type) { + case PROCFS_MIRROR: + ret = pfile->file.mirror.ops->read(path, buf, size, offset, fi); + break; + case PROCFS_PROXY: ret = read(pfile->file.proxy.fd, buf, size); if (ret < 0) @@ -143,6 +190,10 @@ static int procfs_write(const char *path switch (pfile->type) { + case PROCFS_MIRROR: + ret = pfile->file.mirror.ops->write(path, buf, size, offset, fi); + break; + case PROCFS_PROXY: ret = write(pfile->file.proxy.fd, buf, size); if (ret < 0) @@ -156,16 +207,25 @@ static int procfs_write(const char *path static int procfs_release(const char *path, struct fuse_file_info *fi) { struct procfs_file *pfile = (typeof(pfile))fi->fh;; + int ret; switch (pfile->type) { + case PROCFS_MIRROR: + ret = pfile->file.mirror.ops->release(path, fi); + break; + case PROCFS_PROXY: if (close(pfile->file.proxy.fd)) - return -errno; + ret = -errno; break; + default: + ret = -EIO; } - return 0; + free(pfile); + + return ret; } static int procfs_opendir(const char *path, struct fuse_file_info *fi) @@ -265,9 +325,15 @@ static void *procfs_init(struct fuse_con shadow_entries, ARRAY_SIZE(shadow_entries))) goto out_free_fsinfo; + if (procfs_init_hash(&fsinfo->mirror_hash, MIRROR_HASH_SIZE, + mirror_entries, ARRAY_SIZE(mirror_entries))) + goto out_destroy_shadow_hash; + out: return fsinfo; +out_destroy_shadow_hash: + hdestroy_r(&fsinfo->shadow_hash); out_free_fsinfo: free(fsinfo); fsinfo = NULL; @@ -280,6 +346,7 @@ static void procfs_destroy(void *private closedir(fsinfo->procdir); hdestroy_r(&fsinfo->shadow_hash); + hdestroy_r(&fsinfo->mirror_hash); free(fsinfo); } @@ -301,3 +368,170 @@ int main(int argc, char *argv[]) { return fuse_main(argc, argv, &procfs_ops, NULL); } + +/* + * Mirroring specific functions + */ + +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); + fclose(file); + + strtok(line, ":"); + strtok(NULL, ":"); + name = strtok(NULL, ":"); + name = strtok(name, "/"); + + return strdup(name); +} + +struct meminfo { + FILE *memlimit; + FILE *memusage; + FILE *swaplimit; + FILE *swapusage; +}; + +static int meminfo_open(const char *path, struct fuse_file_info *fi) +{ + struct procfs_file *pfile = (typeof(pfile))fi->fh; + char cgmntpath[MAXPATHLEN]; + char cgpath[MAXPATHLEN]; + char *cgname; + struct meminfo *meminfo; + int ret; + + if (get_cgroup_mount("/etc/mtab", cgmntpath)) + if (get_cgroup_mount("/proc/mounts", cgmntpath)) + return -ENOENT; + + cgname = cgroup_name(); + if (!cgname) + return -errno; + + meminfo = malloc(sizeof(*meminfo)); + if (!meminfo) { + ret = -ENOMEM; + goto out; + } + pfile->file.mirror.data = meminfo; + + ret = -ENOENT; + + sprintf(cgpath, "%s/%s/memory.limit_in_bytes", cgmntpath, cgname); + meminfo->memlimit = fopen(cgpath, "r"); + if (!meminfo->memlimit) + goto out_free_meminfo; + + sprintf(cgpath, "%s/%s/memory.usage_in_bytes", cgmntpath, cgname); + meminfo->memusage = fopen(cgpath, "r"); + if (!meminfo->memusage) + goto out_close_memlimit; + + sprintf(cgpath, "%s/%s/memory.memsw.limit_in_bytes", cgmntpath, cgname); + meminfo->swaplimit = fopen(cgpath, "r"); + if (!meminfo->swaplimit) + goto out_close_memusage; + + sprintf(cgpath, "%s/%s/memory.memsw.limit_in_bytes", cgmntpath, cgname); + meminfo->swapusage = fopen(cgpath, "r"); + if (!meminfo->swapusage) + goto out_close_swaplimit; + + ret = 0; +out: + free(cgname); + return ret; + +out_close_swaplimit: + fclose(meminfo->swaplimit); +out_close_memusage: + fclose(meminfo->memusage); +out_close_memlimit: + fclose(meminfo->memlimit); +out_free_meminfo: + free(meminfo); + goto out; +} + +static int meminfo_read(const char *path, char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + struct procfs_file *pfile = (typeof(pfile))fi->fh; + struct meminfo *meminfo = pfile->file.mirror.data; + long long memlimit, memusage, swaplimit, swapusage; + size_t len; + + if (offset) + return 0; + + fscanf(meminfo->memlimit, "%lld", &memlimit); + fscanf(meminfo->memusage, "%lld", &memusage); + fscanf(meminfo->swaplimit, "%lld", &swaplimit); + fscanf(meminfo->swapusage, "%lld", &swapusage); + + len = sprintf(buf, "MemTotal: %8lu kB\n", + memlimit >> 10); + len += sprintf(buf + len, "MemFree: %8lu kB\n", + (memlimit - memusage) >> 10); + len += sprintf(buf + len, "SwapTotal: %8lu kB\n", + swaplimit >> 10); + len += sprintf(buf + len, "SwapFree: %8lu kB\n\n", + (swaplimit - swapusage) >> 10); + + return len; +} + +static int meminfo_release(const char *path, struct fuse_file_info *fi) +{ + struct procfs_file *pfile = (typeof(pfile))fi->fh; + struct meminfo *meminfo = pfile->file.mirror.data; + + fclose(meminfo->memlimit); + fclose(meminfo->memusage); + fclose(meminfo->swaplimit); + fclose(meminfo->swapusage); + free(meminfo); + + return 0; +}