#include <sys/epoll.h>
#include <sys/mount.h>
#include <sys/socket.h>
+#include <linux/limits.h>
#include "bindings.h"
#include "config.h" // for VERSION
int ret;
if ((ret = pthread_mutex_lock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
+ lxcfs_error("returned:%d %s\n", ret, strerror(ret));
exit(1);
}
}
int ret;
if ((ret = pthread_mutex_unlock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
+ lxcfs_error("returned:%d %s\n", ret, strerror(ret));
exit(1);
}
}
* lock and when we know the user_count was 0 */
static void do_reload(void)
{
- if (dlopen_handle)
+ 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)
+ if (dlopen_handle) {
+ lxcfs_debug("%s\n", "Successfully called dlopen() on liblxcfs.so.");
goto good;
+ }
- dlopen_handle = dlopen("/usr/lib/lxcfs/liblxcfs.so", RTLD_LAZY);
+#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) {
- fprintf(stderr, "Failed to open liblxcfs: %s.\n", dlerror());
+ lxcfs_error("Failed to open liblxcfs.so: %s.\n", dlerror());
_exit(1);
}
good:
if (need_reload)
- fprintf(stderr, "lxcfs: reloaded\n");
+ lxcfs_error("%s\n", "lxcfs: reloaded");
need_reload = 0;
}
cg_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "cg_getattr");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_getattr: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_getattr = (int (*)(const char *, struct stat *)) dlsym(dlopen_handle, "proc_getattr");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_getattr: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_read");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_read: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_read = (int (*)(const char *, char *, size_t, off_t, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_read");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_read: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
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) {
- fprintf(stderr, "cg_write: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_mkdir = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_mkdir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_mkdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_chown = (int (*)(const char *, uid_t, gid_t)) dlsym(dlopen_handle, "cg_chown");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_chown: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_rmdir = (int (*)(const char *path)) dlsym(dlopen_handle, "cg_rmdir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_rmdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_chmod = (int (*)(const char *, mode_t)) dlsym(dlopen_handle, "cg_chmod");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_chmod: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
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) {
- fprintf(stderr, "cg_readdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
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) {
- fprintf(stderr, "proc_readdir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_open = (int (*)(const char *, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_open");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_open: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "cg_access");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_access: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_open = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "proc_open");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_open: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_access = (int (*)(const char *, int mode)) dlsym(dlopen_handle, "proc_access");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_access: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_release");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_release: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
proc_release = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "proc_release");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "proc_release: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_opendir = (int (*)(const char *path, struct fuse_file_info *fi)) dlsym(dlopen_handle, "cg_opendir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_opendir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
cg_releasedir = (int (*)(const char *path, struct fuse_file_info *)) dlsym(dlopen_handle, "cg_releasedir");
error = dlerror();
if (error != NULL) {
- fprintf(stderr, "cg_releasedir: %s\n", error);
+ lxcfs_error("%s\n", error);
return -1;
}
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) {
- 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) {
down_users();
return ret;
}
- return -EINVAL;
+ return -ENOENT;
}
static int lxcfs_access(const char *path, int mode)
{
int ret;
- if (strcmp(path, "/") == 0 && access(path, R_OK) == 0)
+ if (strcmp(path, "/") == 0 && (mode & W_OK) == 0)
return 0;
if (strncmp(path, "/cgroup", 7) == 0) {
return ret;
}
- return -EINVAL;
+ return -EACCES;
}
static int lxcfs_releasedir(const char *path, struct fuse_file_info *fi)
return ret;
}
- return -EINVAL;
+ return -EACCES;
}
static int lxcfs_read(const char *path, char *buf, size_t size, off_t offset,
return ret;
}
- return -EINVAL;
+ return -EPERM;
}
int lxcfs_chown(const char *path, uid_t uid, gid_t gid)
return ret;
}
- return -EINVAL;
+ if (strncmp(path, "/proc", 5) == 0)
+ return -EPERM;
+
+ return -ENOENT;
}
/*
{
if (strncmp(path, "/cgroup", 7) == 0)
return 0;
- return -EINVAL;
+ return -EPERM;
}
int lxcfs_rmdir(const char *path)
down_users();
return ret;
}
- return -EINVAL;
+ return -EPERM;
}
int lxcfs_chmod(const char *path, mode_t mode)
down_users();
return ret;
}
- return -EINVAL;
+
+ if (strncmp(path, "/proc", 5) == 0)
+ return -EPERM;
+
+ return -ENOENT;
}
const struct fuse_operations lxcfs_ops = {
.fgetattr = NULL,
};
-static void usage(const char *me)
+static void usage()
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\n");
- fprintf(stderr, "%s [-p pidfile] mountpoint\n", me);
+ fprintf(stderr, "lxcfs [-f|-d] -l [-p pidfile] mountpoint\n");
+ fprintf(stderr, " -f running foreground by default; -d enable debug output \n");
+ fprintf(stderr, " -l use loadavg \n");
fprintf(stderr, " Default pidfile is %s/lxcfs.pid\n", RUNTIME_PATH);
- fprintf(stderr, "%s -h\n", me);
+ fprintf(stderr, "lxcfs -h\n");
exit(1);
}
return false;
}
-void swallow_arg(int *argcp, char *argv[], char *which)
+bool swallow_arg(int *argcp, char *argv[], char *which)
{
int i;
argv[i] = argv[i+1];
}
(*argcp)--;
- return;
+ return true;
}
+ return false;
}
bool swallow_option(int *argcp, char *argv[], char *opt, char **v)
fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
- fprintf(stderr, "Could not open pidfile %s: %m", pidfile);
+ fprintf(stderr, "Could not open pidfile %s: %m\n", pidfile);
return -1;
}
{
int ret = EXIT_FAILURE;
int pidfd = -1;
- char *pidfile = NULL, *v = NULL;
+ char *pidfile = NULL, *saveptr = NULL, *token = NULL, *v = NULL;
size_t pidfile_len;
+ bool debug = false, nonempty = false;
+ pthread_t pid;
+ int s = 0;
+ char *error;
+ bool load_use = false;
+ pthread_t (*load_daemon)(int);
+ void (*load_free)(void);
/*
* what we pass to fuse_main is:
- * argv[0] -s -f -o allow_other,directio argv[1] NULL
+ * 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_arg(&argc, argv, "-l")) {
+ load_use = true;
+ }
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);
+ /* Parse multiple values */
+ for (; (token = strtok_r(v, ",", &saveptr)); v = NULL) {
+ if (strcmp(token, "allow_other") == 0) {
+ /* Noop. this is the default. Always enabled. */
+ } else if (strcmp(token, "nonempty") == 0) {
+ nonempty = true;
+ } else {
+ free(v);
+ fprintf(stderr, "Warning: unexpected fuse option %s\n", v);
+ exit(EXIT_FAILURE);
+ }
}
free(v);
v = NULL;
exit(EXIT_SUCCESS);
}
if (argc != 2 || is_help(argv[1]))
- usage(argv[0]);
+ usage();
do_reload();
if (signal(SIGUSR1, reload_handler) == SIG_ERR) {
}
newargv[cnt++] = argv[0];
- newargv[cnt++] = "-f";
+ 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";
+ if (nonempty)
+ newargv[cnt++] = "allow_other,direct_io,entry_timeout=0.5,attr_timeout=0.5,nonempty";
+ else
+ newargv[cnt++] = "allow_other,direct_io,entry_timeout=0.5,attr_timeout=0.5";
newargv[cnt++] = argv[1];
newargv[cnt++] = NULL;
if ((pidfd = set_pidfile(pidfile)) < 0)
goto out;
+ if (load_use == true) {
+ dlerror(); /* Clear any existing error */
+
+ load_daemon = (pthread_t (*)(int)) dlsym(dlopen_handle, "load_daemon");
+ error = dlerror();
+ if (error != NULL) {
+ lxcfs_error("load_daemon fails:%s\n", error);
+ goto out;
+ }
+ pid = load_daemon(1);
+ if (pid == 0)
+ goto out;
+ }
if (!fuse_main(nargs, newargv, &lxcfs_ops, NULL))
ret = EXIT_SUCCESS;
+ if (load_use == true) {
+ s = pthread_cancel(pid);
+ if (s == 0) {
+ s = pthread_join(pid, NULL); /* Make sure sub thread has been canceled. */
+ if (s != 0) {
+ lxcfs_error("%s\n", "load_free error!");
+ goto out;
+ }
+ dlerror(); /* Clear any existing error */
+
+ load_free = (void (*)(void)) dlsym(dlopen_handle, "load_free");
+ error = dlerror();
+ if (error != NULL) {
+ lxcfs_error("load_free error: %s\n", error);
+ goto out;
+ }
+ load_free();
+ } else {
+ lxcfs_error("%s\n", "load_free error!");
+ }
+ }
out:
if (dlopen_handle)