X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=lxcfs.c;h=76b3ea26f9ca2166f61e5c4de849b007b0240ca7;hb=8437cc6cb6b424cdb81db35915b7914ef934e736;hp=c53906c914fad2a0b7c179d2d67348f8e350195d;hpb=8dcde249026d5d5fe61133eb53642eb152798569;p=mirror_lxcfs.git diff --git a/lxcfs.c b/lxcfs.c index c53906c..76b3ea2 100644 --- a/lxcfs.c +++ b/lxcfs.c @@ -8,28 +8,30 @@ #define FUSE_USE_VERSION 26 -#include +#include #include +#include +#include #include #include -#include -#include -#include -#include -#include -#include #include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include #include -#include +#include +#include +#include -#include "config.h" // for VERSION #include "bindings.h" +#include "config.h" // for VERSION void *dlopen_handle; @@ -42,7 +44,7 @@ static void lock_mutex(pthread_mutex_t *l) 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); } } @@ -52,7 +54,7 @@ static void unlock_mutex(pthread_mutex_t *l) 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); } } @@ -67,29 +69,87 @@ static void users_unlock(void) unlock_mutex(&user_count_mutex); } +static pthread_t loadavg_pid = 0; + +/* Returns zero on success */ +static int start_loadavg(void) { + char *error; + pthread_t (*load_daemon)(int); + + 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); + return -1; + } + loadavg_pid = load_daemon(1); + if (loadavg_pid == 0) + return -1; + + return 0; +} + +/* Returns zero on success */ +static int stop_loadavg(void) { + char *error; + int (*stop_load_daemon)(pthread_t); + + stop_load_daemon = (int (*)(pthread_t)) dlsym(dlopen_handle, "stop_load_daemon"); + error = dlerror(); + if (error != NULL) { + lxcfs_error("stop_load_daemon error: %s\n", error); + return -1; + } + + if (stop_load_daemon(loadavg_pid) != 0) + return -1; + + return 0; +} + static volatile sig_atomic_t need_reload; /* do_reload - reload the dynamic library. Done under * 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 (loadavg_pid > 0) + stop_loadavg(); + + 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\n"); + lxcfs_error("Failed to open liblxcfs.so: %s.\n", dlerror()); _exit(1); } good: + if (loadavg_pid > 0) + start_loadavg(); + if (need_reload) - fprintf(stderr, "lxcfs: reloaded\n"); + lxcfs_error("%s\n", "lxcfs: reloaded"); need_reload = 0; } @@ -123,7 +183,7 @@ static int do_cg_getattr(const char *path, struct stat *sb) 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; } @@ -138,7 +198,7 @@ static int do_proc_getattr(const char *path, struct stat *sb) 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; } @@ -156,7 +216,7 @@ static int do_cg_read(const char *path, char *buf, size_t size, off_t offset, 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; } @@ -174,7 +234,7 @@ static int do_proc_read(const char *path, char *buf, size_t size, off_t offset, 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; } @@ -191,7 +251,7 @@ static int do_cg_write(const char *path, const char *buf, size_t size, off_t off 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; } @@ -206,7 +266,7 @@ static int do_cg_mkdir(const char *path, mode_t mode) 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; } @@ -221,7 +281,7 @@ static int do_cg_chown(const char *path, uid_t uid, gid_t gid) 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; } @@ -236,7 +296,7 @@ static int do_cg_rmdir(const char *path) 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; } @@ -251,7 +311,7 @@ static int do_cg_chmod(const char *path, mode_t mode) 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; } @@ -269,7 +329,7 @@ static int do_cg_readdir(const char *path, void *buf, fuse_fill_dir_t filler, of 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; } @@ -287,7 +347,7 @@ static int do_proc_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 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; } @@ -302,13 +362,28 @@ static int do_cg_open(const char *path, struct fuse_file_info *fi) 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; } return cg_open(path, fi); } +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; + } + + return cg_access(path, mode); +} + static int do_proc_open(const char *path, struct fuse_file_info *fi) { int (*proc_open)(const char *path, struct fuse_file_info *fi); @@ -317,13 +392,28 @@ static int do_proc_open(const char *path, struct fuse_file_info *fi) 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; } return proc_open(path, fi); } +static int do_proc_access(const char *path, int mode) +{ + 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); +} + static int do_cg_release(const char *path, struct fuse_file_info *fi) { int (*cg_release)(const char *path, struct fuse_file_info *fi); @@ -332,7 +422,7 @@ static int do_cg_release(const char *path, struct fuse_file_info *fi) 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; } @@ -347,7 +437,7 @@ static int do_proc_release(const char *path, struct fuse_file_info *fi) 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; } @@ -362,7 +452,7 @@ static int do_cg_opendir(const char *path, struct fuse_file_info *fi) 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; } @@ -377,7 +467,7 @@ static int do_cg_releasedir(const char *path, struct fuse_file_info *fi) 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; } @@ -393,11 +483,19 @@ static int do_cg_releasedir(const char *path, struct fuse_file_info *fi) 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) { up_users(); ret = do_cg_getattr(path, sb); @@ -410,7 +508,7 @@ static int lxcfs_getattr(const char *path, struct stat *sb) down_users(); return ret; } - return -EINVAL; + return -ENOENT; } static int lxcfs_opendir(const char *path, struct fuse_file_info *fi) @@ -435,9 +533,11 @@ static int lxcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, of { 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) { @@ -452,7 +552,30 @@ static int lxcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, of down_users(); return ret; } - return -EINVAL; + 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) @@ -487,7 +610,7 @@ static int lxcfs_open(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, @@ -563,7 +686,7 @@ int lxcfs_mkdir(const char *path, mode_t mode) return ret; } - return -EINVAL; + return -EPERM; } int lxcfs_chown(const char *path, uid_t uid, gid_t gid) @@ -576,7 +699,10 @@ 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; } /* @@ -588,7 +714,7 @@ int lxcfs_truncate(const char *path, off_t newsize) { if (strncmp(path, "/cgroup", 7) == 0) return 0; - return -EINVAL; + return -EPERM; } int lxcfs_rmdir(const char *path) @@ -600,7 +726,7 @@ int lxcfs_rmdir(const char *path) down_users(); return ret; } - return -EINVAL; + return -EPERM; } int lxcfs_chmod(const char *path, mode_t mode) @@ -612,7 +738,11 @@ 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 = { @@ -652,19 +782,21 @@ const struct fuse_operations lxcfs_ops = { .fsyncdir = NULL, .init = NULL, .destroy = NULL, - .access = NULL, + .access = lxcfs_access, .create = NULL, .ftruncate = NULL, .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); } @@ -678,7 +810,7 @@ static bool is_help(char *w) return false; } -void swallow_arg(int *argcp, char *argv[], char *which) +bool swallow_arg(int *argcp, char *argv[], char *which) { int i; @@ -689,8 +821,9 @@ void swallow_arg(int *argcp, char *argv[], char *which) argv[i] = argv[i+1]; } (*argcp)--; - return; + return true; } + return false; } bool swallow_option(int *argcp, char *argv[], char *opt, char **v) @@ -714,127 +847,6 @@ bool swallow_option(int *argcp, char *argv[], char *opt, char **v) return false; } -static bool mkdir_p(const char *dir, mode_t mode) -{ - const char *tmp = dir; - const char *orig = dir; - char *makeme; - - do { - dir = tmp + strspn(tmp, "/"); - tmp = dir + strcspn(dir, "/"); - makeme = strndup(orig, dir - orig); - if (!makeme) - return false; - if (mkdir(makeme, mode) && errno != EEXIST) { - fprintf(stderr, "failed to create directory '%s': %s", - makeme, strerror(errno)); - free(makeme); - return false; - } - free(makeme); - } while(tmp != dir); - - return true; -} - -static bool umount_if_mounted(void) -{ - if (umount2(basedir, MNT_DETACH) < 0 && errno != EINVAL) { - fprintf(stderr, "failed to umount %s: %s\n", basedir, - strerror(errno)); - return false; - } - return true; -} - -static bool setup_cgfs_dir(void) -{ - if (!mkdir_p(basedir, 0700)) { - fprintf(stderr, "Failed to create lxcfs cgdir\n"); - return false; - } - if (!umount_if_mounted()) { - fprintf(stderr, "Failed to clean up old lxcfs cgdir\n"); - return false; - } - if (mount("tmpfs", basedir, "tmpfs", 0, "size=100000,mode=700") < 0) { - fprintf(stderr, "Failed to mount tmpfs for private controllers\n"); - return false; - } - return true; -} - -static bool do_mount_cgroup(char *controller) -{ - char *target; - size_t len; - int ret; - - len = strlen(basedir) + strlen(controller) + 2; - target = alloca(len); - ret = snprintf(target, len, "%s/%s", basedir, controller); - if (ret < 0 || ret >= len) - return false; - if (mkdir(target, 0755) < 0 && errno != EEXIST) - return false; - if (mount(controller, target, "cgroup", 0, controller) < 0) { - fprintf(stderr, "Failed mounting cgroup %s\n", controller); - return false; - } - return true; -} - -static bool do_mount_cgroups(void) -{ - bool ret; - FILE *f; - char *line = NULL; - size_t len = 0; - - if ((f = fopen("/proc/self/cgroup", "r")) == NULL) { - fprintf(stderr, "Error opening /proc/self/cgroup: %s\n", strerror(errno)); - return false; - } - - while (getline(&line, &len, f) != -1) { - char *p, *p2; - - p = strchr(line, ':'); - if (!p) - goto out; - *(p++) = '\0'; - - p2 = strrchr(p, ':'); - if (!p2) - goto out; - *p2 = '\0'; - - if (!do_mount_cgroup(p)) - goto out; - } - ret = true; - -out: - free(line); - fclose(f); - return ret; -} - -static bool cgfs_setup_controllers(void) -{ - if (!setup_cgfs_dir()) { - return false; - } - - if (!do_mount_cgroups()) { - fprintf(stderr, "Failed to set up cgroup mounts\n"); - return false; - } - - return true; -} - static int set_pidfile(char *pidfile) { int fd; @@ -848,7 +860,7 @@ static int set_pidfile(char *pidfile) 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; } @@ -879,12 +891,15 @@ static int set_pidfile(char *pidfile) int main(int argc, char *argv[]) { - int ret = -1, pidfd; - char *pidfile = NULL, *v = NULL; + int ret = EXIT_FAILURE; + int pidfd = -1; + char *pidfile = NULL, *saveptr = NULL, *token = NULL, *v = NULL; size_t pidfile_len; + bool debug = false, nonempty = false; + bool load_use = false; /* * 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]; @@ -892,10 +907,22 @@ int main(int argc, char *argv[]) /* 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(1); + /* 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; @@ -905,27 +932,30 @@ int main(int argc, char *argv[]) if (argc == 2 && strcmp(argv[1], "--version") == 0) { fprintf(stderr, "%s\n", VERSION); - exit(0); + exit(EXIT_SUCCESS); } if (argc != 2 || is_help(argv[1])) - usage(argv[0]); + usage(); do_reload(); if (signal(SIGUSR1, reload_handler) == SIG_ERR) { fprintf(stderr, "Error setting USR1 signal handler: %m\n"); - exit(1); + goto out; } 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 (!cgfs_setup_controllers()) - goto out; - if (!pidfile) { pidfile_len = strlen(RUNTIME_PATH) + strlen("/lxcfs.pid") + 1; pidfile = alloca(pidfile_len); @@ -934,12 +964,20 @@ int main(int argc, char *argv[]) if ((pidfd = set_pidfile(pidfile)) < 0) goto out; - ret = fuse_main(nargs, newargv, &lxcfs_ops, NULL); + if (load_use && start_loadavg() != 0) + goto out; - dlclose(dlopen_handle); - unlink(pidfile); - close(pidfd); + if (!fuse_main(nargs, newargv, &lxcfs_ops, NULL)) + ret = EXIT_SUCCESS; + if (load_use) + stop_loadavg(); out: - return ret; + if (dlopen_handle) + dlclose(dlopen_handle); + if (pidfile) + unlink(pidfile); + if (pidfd > 0) + close(pidfd); + exit(ret); }