]> git.proxmox.com Git - mirror_lxcfs.git/blobdiff - lxcfs.c
Fix test_proc on s390x
[mirror_lxcfs.git] / lxcfs.c
diff --git a/lxcfs.c b/lxcfs.c
index 778977fb79f680072b4122a305a777a550ee6cd6..62cfd3509751b248df3ae432ad1719cdf2c4aab3 100644 (file)
--- a/lxcfs.c
+++ b/lxcfs.c
 /* lxcfs
  *
- * Copyright © 2014 Canonical, Inc
+ * Copyright © 2014-2016 Canonical, Inc
  * Author: Serge Hallyn <serge.hallyn@ubuntu.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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 <stdio.h>
+#include <alloca.h>
 #include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <fuse.h>
-#include <unistd.h>
-#include <errno.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <sched.h>
 #include <stdbool.h>
-#include <time.h>
-#include <string.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <libgen.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <wait.h>
+#include <linux/sched.h>
+#include <sys/epoll.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <linux/limits.h>
 
-#include <nih/alloc.h>
-#include <nih/string.h>
+#include "bindings.h"
+#include "config.h" // for VERSION
 
-#include "cgmanager.h"
+void *dlopen_handle;
 
-struct lxcfs_state {
-       /*
-        * a null-terminated, nih-allocated list of the mounted subsystems.  We
-        * detect this at startup.
-        */
-       char **subsystems;
-};
-#define LXCFS_DATA ((struct lxcfs_state *) fuse_get_context()->private_data)
+/* Functions to keep track of number of threads using the library */
 
-static bool is_privileged_over(pid_t pid, uid_t uid, uid_t victim)
+static int users_count;
+static pthread_mutex_t user_count_mutex = PTHREAD_MUTEX_INITIALIZER;
+static void lock_mutex(pthread_mutex_t *l)
 {
-       if (uid == victim)
-               return true;
-
-       /* check /proc/pid/uid_map */
-       return false;
-}
+       int ret;
 
-static bool perms_include(int fmode, mode_t req_mode)
-{
-       fprintf(stderr, "perms_include: checking whether %d includes %d\n",
-               fmode, req_mode);
-       return (fmode & req_mode) == req_mode;
+       if ((ret = pthread_mutex_lock(l)) != 0) {
+               lxcfs_error("returned:%d %s\n", ret, strerror(ret));
+               exit(1);
+       }
 }
 
-/*
- * check whether a fuse context may access a cgroup dir or file
- *
- * If file is not null, it is a cgroup file to check under cg.
- * If file is null, then we are checking perms on cg itself.
- *
- * For files we can check the mode of the list_keys result.
- * For cgroups, we must make assumptions based on the files under the
- * cgroup, because cgmanager doesn't tell us ownership/perms of cgroups
- * yet.
- */
-static bool fc_may_access(struct fuse_context *fc, const char *contrl, const char *cg, const char *file, mode_t mode)
+static void unlock_mutex(pthread_mutex_t *l)
 {
-       nih_local struct cgm_keys **list = NULL;
-       int i;
+       int ret;
 
-       if (!file)
-               file = "tasks";
-
-       if (*file == '/')
-               file++;
-
-       if (!cgm_list_keys(contrl, cg, &list))
-               return false;
-       for (i = 0; list[i]; i++) {
-               if (strcmp(list[i]->name, file) == 0) {
-                       struct cgm_keys *k = list[i];
-                       fprintf(stderr, "XXX fc_may_access: found %s\n", file);
-                       // check list[i]->uid, gid, mode against fc
-                       if (is_privileged_over(fc->pid, fc->uid, k->uid)) {
-                               if (perms_include(k->mode >> 6, mode))
-                                       return true;
-                       }
-                       if (fc->gid == k->gid) {
-                               if (perms_include(k->mode >> 3, mode))
-                                       return true;
-                       }
-                       return perms_include(k->mode, mode);
-               }
+       if ((ret = pthread_mutex_unlock(l)) != 0) {
+               lxcfs_error("returned:%d %s\n", ret, strerror(ret));
+               exit(1);
        }
+}
 
-       return false;
+static void users_lock(void)
+{
+       lock_mutex(&user_count_mutex);
 }
 
-/*
- * given /cgroup/freezer/a/b, return "freezer".  this will be nih-allocated
- * and needs to be nih_freed.
- */
-static char *pick_controller_from_path(struct fuse_context *fc, const char *path)
+static void users_unlock(void)
 {
-       const char *p1;
-       char *ret, *slash;
+       unlock_mutex(&user_count_mutex);
+}
 
-       if (strlen(path) < 9)
-               return NULL;
-       p1 = path+8;
-       ret = nih_strdup(NULL, p1);
-       if (!ret)
-               return ret;
-       slash = strstr(ret, "/");
-       if (slash)
-               *slash = '\0';
+static volatile sig_atomic_t need_reload;
 
-       /* verify that it is a subsystem */
-       char **list = LXCFS_DATA ? LXCFS_DATA->subsystems : NULL;
-       int i;
-       if (!list) {
-               nih_free(ret);
-               return NULL;
+/* do_reload - reload the dynamic library.  Done under
+ * lock and when we know the user_count was 0 */
+static void do_reload(void)
+{
+       char lxcfs_lib_path[PATH_MAX];
+       if (dlopen_handle) {
+               lxcfs_debug("%s\n", "Closing liblxcfs.so handle.");
+               dlclose(dlopen_handle);
+       }
+
+       /* First try loading using ld.so */
+       dlopen_handle = dlopen("liblxcfs.so", RTLD_LAZY);
+       if (dlopen_handle) {
+               lxcfs_debug("%s\n", "Successfully called dlopen() on liblxcfs.so.");
+               goto good;
        }
-       for (i = 0;  list[i];  i++) {
-               if (strcmp(list[i], ret) == 0)
-                       return ret;
+
+#ifdef LIBDIR
+       /* LIBDIR: autoconf will setup this MACRO. Default value is $PREFIX/lib */
+        snprintf(lxcfs_lib_path, PATH_MAX, "%s/lxcfs/liblxcfs.so", LIBDIR);
+#else
+        snprintf(lxcfs_lib_path, PATH_MAX, "/usr/local/lib/lxcfs/liblxcfs.so");
+#endif
+        dlopen_handle = dlopen(lxcfs_lib_path, RTLD_LAZY);
+       if (!dlopen_handle) {
+               lxcfs_error("Failed to open liblxcfs.so: %s.\n", dlerror());
+               _exit(1);
        }
-       nih_free(ret);
-       return NULL;
+
+good:
+       if (need_reload)
+               lxcfs_error("%s\n", "lxcfs: reloaded");
+       need_reload = 0;
 }
 
-/*
- * Find the start of cgroup in /cgroup/controller/the/cgroup/path
- * Note that the returned value may include files (keynames) etc
- */
-static const char *find_cgroup_in_path(const char *path)
+static void up_users(void)
 {
-       const char *p1;
-
-       if (strlen(path) < 9)
-               return NULL;
-       p1 = strstr(path+8, "/");
-       if (!p1)
-               return NULL;
-       return p1+1;
+       users_lock();
+       if (users_count == 0 && need_reload)
+               do_reload();
+       users_count++;
+       users_unlock();
 }
 
-static bool is_child_cgroup(const char *contr, const char *dir, const char *f)
+static void down_users(void)
 {
-       nih_local char **list = NULL;
-       int i;
+       users_lock();
+       users_count--;
+       users_unlock();
+}
 
-       if (!f)
-               return false;
-       if (*f == '/')
-               f++;
+static void reload_handler(int sig)
+{
+       need_reload = 1;
+}
 
-       if (!cgm_list_children(contr, dir, &list))
-               return false;
-       for (i = 0; list[i]; i++) {
-               if (strcmp(list[i], f) == 0)
-                       return true;
+/* Functions to run the library methods */
+static int do_cg_getattr(const char *path, struct stat *sb)
+{
+       int (*cg_getattr)(const char *path, struct stat *sb);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "cg_getattr");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       return false;
+       return cg_getattr(path, sb);
 }
 
-static struct cgm_keys *get_cgroup_key(const char *contr, const char *dir, const char *f)
+static int do_proc_getattr(const char *path, struct stat *sb)
 {
-       nih_local struct cgm_keys **list = NULL;
-       struct cgm_keys *k;
-       int i;
-
-       if (!f)
-               return NULL;
-       if (*f == '/')
-               f++;
-       if (!cgm_list_keys(contr, dir, &list))
-               return NULL;
-       for (i = 0; list[i]; i++) {
-               if (strcmp(list[i]->name, f) == 0) {
-                       k = NIH_MUST( nih_alloc(NULL, (sizeof(*k))) );
-                       k->name = NIH_MUST( nih_strdup(k, list[i]->name) );
-                       k->uid = list[i]->uid;
-                       k->gid = list[i]->gid;
-                       k->mode = list[i]->mode;
-                       return k;
-               }
+       int (*proc_getattr)(const char *path, struct stat *sb);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       proc_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "proc_getattr");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       return NULL;
+       return proc_getattr(path, sb);
 }
 
-static void get_cgdir_and_path(const char *cg, char **dir, char **file)
+static int do_cg_read(const char *path, char *buf, size_t size, off_t offset,
+               struct fuse_file_info *fi)
 {
-#if 0
-       nih_local char *cgcopy = NULL;
-       nih_local struct cgm_keys *k = NULL;
-       char *cgdir, *fpath = strrchr(cgroup, '/');
-       cgcopy = NIH_MUST( nih_strdup(NULL, cgroup) );
-       cgdir = dirname(cgcopy);
-#endif
-       char *p;
-
-       *dir = NIH_MUST( nih_strdup(NULL, cg) );
-       *file = strrchr(cg, '/');
-       if (!*file) {
-               *file = NULL;
-               return;
+       int (*cg_read)(const char *path, char *buf, size_t size, off_t offset,
+               struct fuse_file_info *fi);
+       char *error;
+
+       dlerror();    /* Clear any existing error */
+       cg_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_read");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
-       p = strrchr(*dir, '/');
-       *p = '\0';
+
+       return cg_read(path, buf, size, offset, fi);
 }
 
-/*
- * gettattr fn for anything under /cgroup
- */
-static int cg_getattr(const char *path, struct stat *sb)
+static int do_proc_read(const char *path, char *buf, size_t size, off_t offset,
+               struct fuse_file_info *fi)
 {
-       struct timespec now;
-       struct fuse_context *fc = fuse_get_context();
-       nih_local char * cgdir = NULL;
-       char *fpath = NULL, *path1, *path2;
-       nih_local struct cgm_keys *k = NULL;
-       const char *cgroup;
-       nih_local char *controller = NULL;
-
-
-       if (!fc)
-               return -EIO;
-
-       memset(sb, 0, sizeof(struct stat));
-
-       if (clock_gettime(CLOCK_REALTIME, &now) < 0)
-               return -EINVAL;
-
-       sb->st_uid = sb->st_gid = 0;
-       sb->st_atim = sb->st_mtim = sb->st_ctim = now;
-       sb->st_size = 0;
-
-       if (strcmp(path, "/cgroup") == 0) {
-               sb->st_mode = S_IFDIR | 00755;
-               sb->st_nlink = 2;
-               return 0;
-       }
-
-       controller = pick_controller_from_path(fc, path);
-       if (!controller)
-               return -EIO;
-       fprintf(stderr, "XXX getattr controller %s\n", controller);
-       cgroup = find_cgroup_in_path(path);
-       if (!cgroup) {
-               /* this is just /cgroup/controller, return it as a dir */
-               sb->st_mode = S_IFDIR | 00755;
-               sb->st_nlink = 2;
-               return 0;
+       int (*proc_read)(const char *path, char *buf, size_t size, off_t offset,
+               struct fuse_file_info *fi);
+       char *error;
+
+       dlerror();    /* Clear any existing error */
+       proc_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_read");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
-       
-       fprintf(stderr, "XXX getattr controller %s cgroup %s\n", controller, cgroup);
 
-       get_cgdir_and_path(cgroup, &cgdir, &fpath);
+       return proc_read(path, buf, size, offset, fi);
+}
 
-       if (!fpath) {
-               path1 = "/";
-               path2 = cgdir;
-       } else {
-               path1 = cgdir;
-               path2 = fpath;
+static int do_cg_write(const char *path, const char *buf, size_t size, off_t offset,
+            struct fuse_file_info *fi)
+{
+       int (*cg_write)(const char *path, const char *buf, size_t size, off_t offset,
+            struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_write = (int (*)(const char *, const char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_write");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       fprintf(stderr, "XXX gettattr: dir is %s, basename %s\n", path1, path2);
+       return cg_write(path, buf, size, offset, fi);
+}
 
-       /* check that cgcopy is either a child cgroup of cgdir, or listed in its keys.
-        * Then check that caller's cgroup is under path if fpath is a child
-        * cgroup, or cgdir if fpath is a file */
+static int do_cg_mkdir(const char *path, mode_t mode)
+{
+       int (*cg_mkdir)(const char *path, mode_t mode);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_mkdir = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_mkdir");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
 
-       if (is_child_cgroup(controller, path1, path2)) {
-               if (!fc_may_access(fc, controller, cgroup, NULL, O_RDONLY))
-                       return -EPERM;
+       return cg_mkdir(path, mode);
+}
 
-               sb->st_mode = S_IFDIR | 00755;   // TODO what to use?
-               // TODO - how to get uid, gid
-               sb->st_uid = sb->st_gid = 0;
-               sb->st_nlink = 2;
-               return 0;
+static int do_cg_chown(const char *path, uid_t uid, gid_t gid)
+{
+       int (*cg_chown)(const char *path, uid_t uid, gid_t gid);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_chown = (int (*)(const char *, uid_t, gid_t)) dlsym(dlopen_handle, "cg_chown");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       if ((k = get_cgroup_key(controller, path1, path2)) != NULL) {
-               if (!fc_may_access(fc, controller, path1, path2, O_RDONLY))
-                       return -EPERM;
-
-               fprintf(stderr, "XXX getattr mode on %s %s %s is %d\n", controller, path1, path2, k->mode);
+       return cg_chown(path, uid, gid);
+}
 
-               // TODO - convert uid, gid
-               sb->st_mode = S_IFREG | k->mode;
-               sb->st_uid = k->uid;
-               sb->st_gid = k->gid;
-               sb->st_nlink = 1;
-               return 0;
+static int do_cg_rmdir(const char *path)
+{
+       int (*cg_rmdir)(const char *path);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_rmdir = (int (*)(const char *path)) dlsym(dlopen_handle, "cg_rmdir");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       return -EINVAL;
+       return cg_rmdir(path);
 }
 
-static int cg_opendir(const char *path, struct fuse_file_info *fi)
+static int do_cg_chmod(const char *path, mode_t mode)
 {
-       return 0;
+       int (*cg_chmod)(const char *path, mode_t mode);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_chmod = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_chmod");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
+
+       return cg_chmod(path, mode);
 }
 
-/*
- * readdir function for anything under /cgroup
- */
-static int cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
+static int do_cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
                struct fuse_file_info *fi)
 {
-       struct fuse_context *fc = fuse_get_context();
-
-       if (!fc)
-               return -EIO;
+       int (*cg_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
+               struct fuse_file_info *fi);
+       char *error;
+
+       dlerror();    /* Clear any existing error */
+       cg_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_readdir");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
 
-       if (strcmp(path, "/cgroup") == 0) {
-               // get list of controllers
-               char **list = LXCFS_DATA ? LXCFS_DATA->subsystems : NULL;
-               int i;
+       return cg_readdir(path, buf, filler, offset, fi);
+}
 
-               if (!list)
-                       return -EIO;
-               /* TODO - collect the list of controllers at fuse_init */
-               for (i = 0;  list[i]; i++) {
-                       if (filler(buf, list[i], NULL, 0) != 0) {
-                               return -EIO;
-                       }
-               }
-               return 0;
+static int do_proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
+               struct fuse_file_info *fi)
+{
+       int (*proc_readdir)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
+               struct fuse_file_info *fi);
+       char *error;
+
+       dlerror();    /* Clear any existing error */
+       proc_readdir = (int (*)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_readdir");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       // 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;
-
-       controller = pick_controller_from_path(fc, path);
-       if (!controller)
-               return -EIO;
+       return proc_readdir(path, buf, filler, offset, fi);
+}
 
-       cgroup = find_cgroup_in_path(path);
-       if (!cgroup) {
-               /* this is just /cgroup/controller, return its contents */
-               cgroup = "/";
+static int do_cg_open(const char *path, struct fuse_file_info *fi)
+{
+       int (*cg_open)(const char *path, struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_open = (int (*)(const char *, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_open");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       if (!fc_may_access(fc, controller, cgroup, NULL, O_RDONLY))
-               return -EPERM;
+       return cg_open(path, fi);
+}
 
-       if (!cgm_list_keys(controller, cgroup, &list))
-               return -EINVAL;
-       for (i = 0; list[i]; i++) {
-               fprintf(stderr, "adding key %s\n", list[i]->name);
-               if (filler(buf, list[i]->name, NULL, 0) != 0) {
-                       return -EIO;
-               }
+static int do_cg_access(const char *path, int mode)
+{
+       int (*cg_access)(const char *path, int mode);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "cg_access");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
 
-       // now get the list of child cgroups
-       nih_local char **clist;
+       return cg_access(path, mode);
+}
 
-       if (!cgm_list_children(controller, cgroup, &clist))
-               return 0;
-       for (i = 0; clist[i]; i++) {
-               fprintf(stderr, "adding child %s\n", clist[i]);
-               if (filler(buf, clist[i], NULL, 0) != 0) {
-                       return -EIO;
-               }
+static int do_proc_open(const char *path, struct fuse_file_info *fi)
+{
+       int (*proc_open)(const char *path, struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       proc_open = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "proc_open");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
        }
-       return 0;
+
+       return proc_open(path, fi);
 }
 
-static int cg_releasedir(const char *path, struct fuse_file_info *fi)
+static int do_proc_access(const char *path, int mode)
 {
-       return 0;
+       int (*proc_access)(const char *path, int mode);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       proc_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "proc_access");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
+
+       return proc_access(path, mode);
 }
 
-/*
- * So far I'm not actually using cg_ops and proc_ops, but listing them
- * here makes it clearer who is supporting what.  Still I prefer to 
- * call the real functions and not cg_ops->getattr.
- */
-const struct fuse_operations cg_ops = {
-       .getattr = cg_getattr,
-       .readlink = NULL,
-       .getdir = NULL,
-       .mknod = NULL,
-       .mkdir = NULL,
-       .unlink = NULL,
-       .rmdir = NULL,
-       .symlink = NULL,
-       .rename = NULL,
-       .link = NULL,
-       .chmod = NULL,
-       .chown = NULL,
-       .truncate = NULL,
-       .utime = NULL,
-       .open = NULL,
-       .read = NULL,
-       .write = NULL,
-       .statfs = NULL,
-       .flush = NULL,
-       .release = NULL,
-       .fsync = NULL,
+static int do_cg_release(const char *path, struct fuse_file_info *fi)
+{
+       int (*cg_release)(const char *path, struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_release");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
 
-       .setxattr = NULL,
-       .getxattr = NULL,
-       .listxattr = NULL,
-       .removexattr = NULL,
+       return cg_release(path, fi);
+}
 
-       .opendir = cg_opendir,
-       .readdir = cg_readdir,
-       .releasedir = cg_releasedir,
+static int do_proc_release(const char *path, struct fuse_file_info *fi)
+{
+       int (*proc_release)(const char *path, struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       proc_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_release");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
 
-       .fsyncdir = NULL,
-       .init = NULL,
-       .destroy = NULL,
-       .access = NULL,
-       .create = NULL,
-       .ftruncate = NULL,
-       .fgetattr = NULL,
-};
+       return proc_release(path, fi);
+}
 
-static int proc_getattr(const char *path, struct stat *sb)
+static int do_cg_opendir(const char *path, struct fuse_file_info *fi)
 {
-       if (strcmp(path, "/proc") != 0)
-               return -EINVAL;
-       sb->st_mode = S_IFDIR | 00755;
-       sb->st_nlink = 2;
-       return 0;
-}
+       int (*cg_opendir)(const char *path, struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_opendir = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "cg_opendir");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
 
-const struct fuse_operations proc_ops = {
-       .getattr = proc_getattr,
-       .readlink = NULL,
-       .getdir = NULL,
-       .mknod = NULL,
-       .mkdir = NULL,
-       .unlink = NULL,
-       .rmdir = NULL,
-       .symlink = NULL,
-       .rename = NULL,
-       .link = NULL,
-       .chmod = NULL,
-       .chown = NULL,
-       .truncate = NULL,
-       .utime = NULL,
-       .open = NULL,
-       .read = NULL,
-       .write = NULL,
-       .statfs = NULL,
-       .flush = NULL,
-       .release = NULL,
-       .fsync = NULL,
+       return cg_opendir(path, fi);
+}
 
-       .setxattr = NULL,
-       .getxattr = NULL,
-       .listxattr = NULL,
-       .removexattr = NULL,
+static int do_cg_releasedir(const char *path, struct fuse_file_info *fi)
+{
+       int (*cg_releasedir)(const char *path, struct fuse_file_info *fi);
+       char *error;
+       dlerror();    /* Clear any existing error */
+       cg_releasedir = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_releasedir");
+       error = dlerror();
+       if (error != NULL) {
+               lxcfs_error("%s\n", error);
+               return -1;
+       }
 
-       .opendir = NULL,
-       .readdir = NULL,
-       .releasedir = NULL,
+       return cg_releasedir(path, fi);
+}
 
-       .fsyncdir = NULL,
-       .init = NULL,
-       .destroy = NULL,
-       .access = NULL,
-       .create = NULL,
-       .ftruncate = NULL,
-       .fgetattr = NULL,
-};
+/*
+ * FUSE ops for /
+ * these just delegate to the /proc and /cgroup ops as
+ * needed
+ */
 
 static int lxcfs_getattr(const char *path, struct stat *sb)
 {
+       int ret;
+       struct timespec now;
+
        if (strcmp(path, "/") == 0) {
+               if (clock_gettime(CLOCK_REALTIME, &now) < 0)
+                       return -EINVAL;
+               sb->st_uid = sb->st_gid = 0;
+               sb->st_atim = sb->st_mtim = sb->st_ctim = now;
+               sb->st_size = 0;
                sb->st_mode = S_IFDIR | 00755;
                sb->st_nlink = 2;
                return 0;
        }
+
        if (strncmp(path, "/cgroup", 7) == 0) {
-               return cg_getattr(path, sb);
+               up_users();
+               ret = do_cg_getattr(path, sb);
+               down_users();
+               return ret;
        }
-       if (strncmp(path, "/proc", 7) == 0) {
-               return proc_getattr(path, sb);
+       if (strncmp(path, "/proc", 5) == 0) {
+               up_users();
+               ret = do_proc_getattr(path, sb);
+               down_users();
+               return ret;
        }
-       return -EINVAL;
+       return -ENOENT;
 }
 
 static int lxcfs_opendir(const char *path, struct fuse_file_info *fi)
 {
+       int ret;
        if (strcmp(path, "/") == 0)
                return 0;
 
        if (strncmp(path, "/cgroup", 7) == 0) {
-               return cg_opendir(path, fi);
+               up_users();
+               ret = do_cg_opendir(path, fi);
+               down_users();
+               return ret;
        }
-       return -EINVAL;
+       if (strcmp(path, "/proc") == 0)
+               return 0;
+       return -ENOENT;
 }
 
 static int lxcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
                struct fuse_file_info *fi)
 {
+       int ret;
        if (strcmp(path, "/") == 0) {
-               if (filler(buf, "proc", NULL, 0) != 0 ||
-                               filler(buf, "cgroup", NULL, 0) != 0)
-                       return -EINVAL;
+               if (filler(buf, ".", NULL, 0) != 0 ||
+                   filler(buf, "..", NULL, 0) != 0 ||
+                   filler(buf, "proc", NULL, 0) != 0 ||
+                   filler(buf, "cgroup", NULL, 0) != 0)
+                       return -ENOMEM;
                return 0;
        }
        if (strncmp(path, "/cgroup", 7) == 0) {
-               return cg_readdir(path, buf, filler, offset, fi);
+               up_users();
+               ret = do_cg_readdir(path, buf, filler, offset, fi);
+               down_users();
+               return ret;
        }
-       return -EINVAL;
+       if (strcmp(path, "/proc") == 0) {
+               up_users();
+               ret = do_proc_readdir(path, buf, filler, offset, fi);
+               down_users();
+               return ret;
+       }
+       return -ENOENT;
+}
+
+static int lxcfs_access(const char *path, int mode)
+{
+       int ret;
+
+       if (strcmp(path, "/") == 0 && (mode & W_OK) == 0)
+               return 0;
+
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_access(path, mode);
+               down_users();
+               return ret;
+       }
+       if (strncmp(path, "/proc", 5) == 0) {
+               up_users();
+               ret = do_proc_access(path, mode);
+               down_users();
+               return ret;
+       }
+
+       return -EACCES;
 }
 
 static int lxcfs_releasedir(const char *path, struct fuse_file_info *fi)
 {
+       int ret;
        if (strcmp(path, "/") == 0)
                return 0;
        if (strncmp(path, "/cgroup", 7) == 0) {
-               return cg_releasedir(path, fi);
+               up_users();
+               ret = do_cg_releasedir(path, fi);
+               down_users();
+               return ret;
+       }
+       if (strcmp(path, "/proc") == 0)
+               return 0;
+       return -EINVAL;
+}
+
+static int lxcfs_open(const char *path, struct fuse_file_info *fi)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_open(path, fi);
+               down_users();
+               return ret;
+       }
+       if (strncmp(path, "/proc", 5) == 0) {
+               up_users();
+               ret = do_proc_open(path, fi);
+               down_users();
+               return ret;
+       }
+
+       return -EACCES;
+}
+
+static int lxcfs_read(const char *path, char *buf, size_t size, off_t offset,
+               struct fuse_file_info *fi)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_read(path, buf, size, offset, fi);
+               down_users();
+               return ret;
+       }
+       if (strncmp(path, "/proc", 5) == 0) {
+               up_users();
+               ret = do_proc_read(path, buf, size, offset, fi);
+               down_users();
+               return ret;
+       }
+
+       return -EINVAL;
+}
+
+int lxcfs_write(const char *path, const char *buf, size_t size, off_t offset,
+            struct fuse_file_info *fi)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_write(path, buf, size, offset, fi);
+               down_users();
+               return ret;
+       }
+
+       return -EINVAL;
+}
+
+static int lxcfs_flush(const char *path, struct fuse_file_info *fi)
+{
+       return 0;
+}
+
+static int lxcfs_release(const char *path, struct fuse_file_info *fi)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_release(path, fi);
+               down_users();
+               return ret;
        }
+       if (strncmp(path, "/proc", 5) == 0) {
+               up_users();
+               ret = do_proc_release(path, fi);
+               down_users();
+               return ret;
+       }
+
        return -EINVAL;
 }
 
-void *bb_init(struct fuse_conn_info *conn)
+static int lxcfs_fsync(const char *path, int datasync, struct fuse_file_info *fi)
+{
+       return 0;
+}
+
+int lxcfs_mkdir(const char *path, mode_t mode)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_mkdir(path, mode);
+               down_users();
+               return ret;
+       }
+
+       return -EPERM;
+}
+
+int lxcfs_chown(const char *path, uid_t uid, gid_t gid)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_chown(path, uid, gid);
+               down_users();
+               return ret;
+       }
+
+       if (strncmp(path, "/proc", 5) == 0)
+               return -EPERM;
+
+       return -ENOENT;
+}
+
+/*
+ * cat first does a truncate before doing ops->write.  This doesn't
+ * really make sense for cgroups.  So just return 0 always but do
+ * nothing.
+ */
+int lxcfs_truncate(const char *path, off_t newsize)
 {
-       return LXCFS_DATA;
+       if (strncmp(path, "/cgroup", 7) == 0)
+               return 0;
+       return -EPERM;
+}
+
+int lxcfs_rmdir(const char *path)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_rmdir(path);
+               down_users();
+               return ret;
+       }
+       return -EPERM;
+}
+
+int lxcfs_chmod(const char *path, mode_t mode)
+{
+       int ret;
+       if (strncmp(path, "/cgroup", 7) == 0) {
+               up_users();
+               ret = do_cg_chmod(path, mode);
+               down_users();
+               return ret;
+       }
+
+       if (strncmp(path, "/proc", 5) == 0)
+               return -EPERM;
+
+       return -ENOENT;
 }
 
 const struct fuse_operations lxcfs_ops = {
@@ -553,23 +703,25 @@ const struct fuse_operations lxcfs_ops = {
        .readlink = NULL,
        .getdir = NULL,
        .mknod = NULL,
-       .mkdir = NULL,
+       .mkdir = lxcfs_mkdir,
        .unlink = NULL,
-       .rmdir = NULL,
+       .rmdir = lxcfs_rmdir,
        .symlink = NULL,
        .rename = NULL,
        .link = NULL,
-       .chmod = NULL,
-       .chown = NULL,
-       .truncate = NULL,
+       .chmod = lxcfs_chmod,
+       .chown = lxcfs_chown,
+       .truncate = lxcfs_truncate,
        .utime = NULL,
-       .open = NULL,
-       .read = NULL,
-       .write = NULL,
+
+       .open = lxcfs_open,
+       .read = lxcfs_read,
+       .release = lxcfs_release,
+       .write = lxcfs_write,
+
        .statfs = NULL,
-       .flush = NULL,
-       .release = NULL,
-       .fsync = NULL,
+       .flush = lxcfs_flush,
+       .fsync = lxcfs_fsync,
 
        .setxattr = NULL,
        .getxattr = NULL,
@@ -583,21 +735,24 @@ const struct fuse_operations lxcfs_ops = {
        .fsyncdir = NULL,
        .init = NULL,
        .destroy = NULL,
-       .access = NULL,
+       .access = lxcfs_access,
        .create = NULL,
        .ftruncate = NULL,
        .fgetattr = NULL,
 };
 
-void usage(const char *me)
+static void usage()
 {
        fprintf(stderr, "Usage:\n");
        fprintf(stderr, "\n");
-       fprintf(stderr, "%s [FUSE and mount options] mountpoint\n", me);
+       fprintf(stderr, "lxcfs [-f|-d] [-p pidfile] mountpoint\n");
+       fprintf(stderr, "  -f running foreground by default; -d enable debug output \n");
+       fprintf(stderr, "  Default pidfile is %s/lxcfs.pid\n", RUNTIME_PATH);
+       fprintf(stderr, "lxcfs -h\n");
        exit(1);
 }
 
-bool is_help(char *w)
+static bool is_help(char *w)
 {
        if (strcmp(w, "-h") == 0 ||
                        strcmp(w, "--help") == 0 ||
@@ -607,25 +762,155 @@ bool is_help(char *w)
        return false;
 }
 
-int main(int argc, char *argv[])
+bool swallow_arg(int *argcp, char *argv[], char *which)
 {
-       int ret;
-       struct lxcfs_state *d;
+       int i;
 
-       if (argc < 2 || is_help(argv[1]))
-               usage(argv[0]);
+       for (i = 1; argv[i]; i++) {
+               if (strcmp(argv[i], which) != 0)
+                       continue;
+               for (; argv[i]; i++) {
+                       argv[i] = argv[i+1];
+               }
+               (*argcp)--;
+               return true;
+       }
+       return false;
+}
+
+bool swallow_option(int *argcp, char *argv[], char *opt, char **v)
+{
+       int i;
 
-       d = malloc(sizeof(*d));
-       if (!d)
+       for (i = 1; argv[i]; i++) {
+               if (!argv[i+1])
+                       continue;
+               if (strcmp(argv[i], opt) != 0)
+                       continue;
+               do {
+                       *v = strdup(argv[i+1]);
+               } while (!*v);
+               for (; argv[i+1]; i++) {
+                       argv[i] = argv[i+2];
+               }
+               (*argcp) -= 2;
+               return true;
+       }
+       return false;
+}
+
+static int set_pidfile(char *pidfile)
+{
+       int fd;
+       char buf[50];
+       struct flock fl;
+
+       fl.l_type = F_WRLCK;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = 0;
+       fl.l_len = 0;
+
+       fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+       if (fd == -1) {
+               fprintf(stderr, "Could not open pidfile %s: %m\n", pidfile);
                return -1;
+       }
 
-       if (!cgm_escape_cgroup())
-               fprintf(stderr, "WARNING: failed to escape to root cgroup\n");
+       if (fcntl(fd, F_SETLK, &fl) == -1) {
+               if (errno  == EAGAIN || errno == EACCES) {
+                       fprintf(stderr, "PID file '%s' is already locked.\n", pidfile);
+                       close(fd);
+                       return -1;
+               }
+               fprintf(stderr, "Warning; unable to lock PID file, proceeding.\n");
+       }
 
-       if (!cgm_get_controllers(&d->subsystems))
+       if (ftruncate(fd, 0) == -1) {
+               fprintf(stderr, "Error truncating PID file '%s': %m", pidfile);
+               close(fd);
                return -1;
+       }
+
+       snprintf(buf, 50, "%ld\n", (long) getpid());
+       if (write(fd, buf, strlen(buf)) != strlen(buf)) {
+               fprintf(stderr, "Error writing to PID file '%s': %m", pidfile);
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
 
-       ret = fuse_main(argc, argv, &lxcfs_ops, d);
+int main(int argc, char *argv[])
+{
+       int ret = EXIT_FAILURE;
+       int pidfd = -1;
+       char *pidfile = NULL, *v = NULL;
+       size_t pidfile_len;
+       bool debug = false;
+       /*
+        * what we pass to fuse_main is:
+        * argv[0] -s [-f|-d] -o allow_other,directio argv[1] NULL
+        */
+       int nargs = 5, cnt = 0;
+       char *newargv[6];
+
+       /* accomodate older init scripts */
+       swallow_arg(&argc, argv, "-s");
+       swallow_arg(&argc, argv, "-f");
+       debug = swallow_arg(&argc, argv, "-d");
+       if (swallow_option(&argc, argv, "-o", &v)) {
+               if (strcmp(v, "allow_other") != 0) {
+                       fprintf(stderr, "Warning: unexpected fuse option %s\n", v);
+                       exit(EXIT_FAILURE);
+               }
+               free(v);
+               v = NULL;
+       }
+       if (swallow_option(&argc, argv, "-p", &v))
+               pidfile = v;
 
-       return ret;
+       if (argc == 2  && strcmp(argv[1], "--version") == 0) {
+               fprintf(stderr, "%s\n", VERSION);
+               exit(EXIT_SUCCESS);
+       }
+       if (argc != 2 || is_help(argv[1]))
+               usage();
+
+       do_reload();
+       if (signal(SIGUSR1, reload_handler) == SIG_ERR) {
+               fprintf(stderr, "Error setting USR1 signal handler: %m\n");
+               goto out;
+       }
+
+       newargv[cnt++] = argv[0];
+        if (debug) {
+                newargv[cnt++] = "-d";
+        } else {
+                newargv[cnt++] = "-f";
+        }
+       newargv[cnt++] = "-o";
+       newargv[cnt++] = "allow_other,direct_io,entry_timeout=0.5,attr_timeout=0.5";
+       newargv[cnt++] = argv[1];
+       newargv[cnt++] = NULL;
+
+       if (!pidfile) {
+               pidfile_len = strlen(RUNTIME_PATH) + strlen("/lxcfs.pid") + 1;
+               pidfile = alloca(pidfile_len);
+               snprintf(pidfile, pidfile_len, "%s/lxcfs.pid", RUNTIME_PATH);
+       }
+       if ((pidfd = set_pidfile(pidfile)) < 0)
+               goto out;
+
+       if (!fuse_main(nargs, newargv, &lxcfs_ops, NULL))
+               ret = EXIT_SUCCESS;
+
+out:
+       if (dlopen_handle)
+               dlclose(dlopen_handle);
+       if (pidfile)
+               unlink(pidfile);
+       if (pidfd > 0)
+               close(pidfd);
+       exit(ret);
 }