libfuse/lib/helper.c

318 lines
6.7 KiB
C
Raw Normal View History

2001-11-21 18:03:39 +08:00
/*
2007-12-12 22:25:40 +08:00
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
2001-11-21 18:03:39 +08:00
2007-12-12 22:25:40 +08:00
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
2001-11-21 18:03:39 +08:00
*/
2006-01-07 18:14:34 +08:00
#include "config.h"
2005-08-15 21:19:07 +08:00
#include "fuse_i.h"
#include "fuse_misc.h"
2005-12-10 01:41:42 +08:00
#include "fuse_opt.h"
2006-01-07 02:29:40 +08:00
#include "fuse_lowlevel.h"
2001-11-21 18:03:39 +08:00
#include <stdio.h>
#include <stdlib.h>
2005-12-10 01:41:42 +08:00
#include <stddef.h>
2001-11-21 18:03:39 +08:00
#include <unistd.h>
2002-12-05 22:23:01 +08:00
#include <string.h>
2001-11-21 18:03:39 +08:00
#include <limits.h>
2006-06-06 17:48:30 +08:00
#include <errno.h>
#include <sys/param.h>
2001-11-21 18:03:39 +08:00
2005-12-10 01:41:42 +08:00
enum {
2007-12-12 22:25:40 +08:00
KEY_HELP,
KEY_HELP_NOHEADER,
KEY_VERSION,
2005-12-10 01:41:42 +08:00
};
struct helper_opts {
2007-12-12 22:25:40 +08:00
int singlethread;
int foreground;
int nodefault_subtype;
char *mountpoint;
2005-12-10 01:41:42 +08:00
};
#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
static const struct fuse_opt fuse_helper_opts[] = {
2008-02-09 01:46:57 +08:00
FUSE_HELPER_OPT("-d", foreground),
FUSE_HELPER_OPT("debug", foreground),
FUSE_HELPER_OPT("-f", foreground),
FUSE_HELPER_OPT("-s", singlethread),
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
2007-12-12 22:25:40 +08:00
FUSE_OPT_END
2005-12-10 01:41:42 +08:00
};
2006-01-07 18:14:34 +08:00
static void usage(const char *progname)
{
2013-07-26 22:20:28 +08:00
printf("usage: %s mountpoint [options]\n\n", progname);
printf("general options:\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n");
2006-01-07 18:14:34 +08:00
}
static void helper_help(void)
{
2013-07-26 22:20:28 +08:00
printf("FUSE options:\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
"\n");
2006-01-07 18:14:34 +08:00
}
static void helper_version(void)
{
2013-07-26 22:20:28 +08:00
printf("FUSE library version: %s\n", PACKAGE_VERSION);
2006-01-07 18:14:34 +08:00
}
2005-12-15 07:25:00 +08:00
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
2007-12-12 22:25:40 +08:00
struct fuse_args *outargs)
2005-01-04 20:45:54 +08:00
{
2007-12-12 22:25:40 +08:00
struct helper_opts *hopts = data;
switch (key) {
case KEY_HELP:
usage(outargs->argv[0]);
/* fall through */
case KEY_HELP_NOHEADER:
helper_help();
return fuse_opt_add_arg(outargs, "-h");
case KEY_VERSION:
helper_version();
return 1;
case FUSE_OPT_KEY_NONOPT:
if (!hopts->mountpoint) {
char mountpoint[PATH_MAX];
if (realpath(arg, mountpoint) == NULL) {
fprintf(stderr,
"fuse: bad mount point `%s': %s\n",
arg, strerror(errno));
return -1;
}
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
} else {
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
return -1;
}
default:
/* Pass through unknown options */
2007-12-12 22:25:40 +08:00
return 1;
}
2006-01-07 02:29:40 +08:00
}
2005-12-10 01:41:42 +08:00
static int add_default_subtype(const char *progname, struct fuse_args *args)
2006-01-07 02:29:40 +08:00
{
2007-12-12 22:25:40 +08:00
int res;
char *subtype_opt;
const char *basename = strrchr(progname, '/');
if (basename == NULL)
basename = progname;
else if (basename[1] != '\0')
basename++;
subtype_opt = (char *) malloc(strlen(basename) + 64);
if (subtype_opt == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
sprintf(subtype_opt, "-osubtype=%s", basename);
res = fuse_opt_add_arg(args, subtype_opt);
free(subtype_opt);
return res;
2004-07-24 01:16:29 +08:00
}
2006-01-07 02:29:40 +08:00
int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
2007-12-12 22:25:40 +08:00
int *multithreaded, int *foreground)
2004-07-24 01:16:29 +08:00
{
2007-12-12 22:25:40 +08:00
int res;
struct helper_opts hopts;
memset(&hopts, 0, sizeof(hopts));
res = fuse_opt_parse(args, &hopts, fuse_helper_opts,
fuse_helper_opt_proc);
if (res == -1)
return -1;
if (!hopts.nodefault_subtype) {
res = add_default_subtype(args->argv[0], args);
if (res == -1)
goto err;
}
if (mountpoint)
*mountpoint = hopts.mountpoint;
else
free(hopts.mountpoint);
if (multithreaded)
*multithreaded = !hopts.singlethread;
if (foreground)
*foreground = hopts.foreground;
return 0;
err:
free(hopts.mountpoint);
return -1;
2004-10-21 17:35:10 +08:00
}
2006-03-17 23:05:40 +08:00
int fuse_daemonize(int foreground)
2006-01-07 16:30:41 +08:00
{
2007-12-12 22:25:40 +08:00
if (!foreground) {
int nullfd;
libfuse/fuse_daemonize: wait until daemon child process is ready (#55) Mounting a FUSE file system remotely using SSH in combination with pseudo-terminal allocation (-t), results in "Transport endpoint is not connected" errors when trying to access the file system contents. For example: # ssh -t root@localhost "cmsfs-fuse /dev/disk/by-path/ccw-0.0.0190 /CMSFS" Connection to localhost closed. # ls /CMSFS ls: cannot access '/CMSFS': Transport endpoint is not connected The cmsfs-fuse main program (which can also be any other FUSE file system) calls into the fuse_main() libfuse library function. The fuse_main() function later calls fuse_daemonize() to fork the daemon process to handle the FUSE file system I/O. The fuse_daemonize() function calls fork() as usual. The child proceeds with setsid() and then redirecting its file descriptors to /dev/null etc. The parent process, simply exits. The child's functions and the parent's exit creates a subtle race. This is seen with an SSH connection. The SSH command above calls cmsfs-fuse on an allocated pseudo-terminal device (-t option). If the parent exits, SSH receives the command completion and closes the connection, that means, it closes the master side of the pseudo-terminal. This causes a HUP signal being sent to the process group on the pseudo-terminal. At this point in time, the child might not have completed the setsid() call and, hence, becomes terminated. Note that fuse daemon sets up its signal handlers after fuse_daemonize() has completed. Even if the child has the chance to disassociate from its parent process group to become it's own process group with setsid(), the child still has the pseudo-terminal opened as stdin, stdout, and stderr. So the pseudo-terminal still behave as controlling terminal and might cause a SIGHUP at closing the the master side. To solve the problem, the parent has to wait until the child (the fuse daemon process) has completed its processing, that means, has become its own process group with setsid() and closed any file descriptors pointing to the pseudo-terminal. Closes: #27 Reported-by: Ofer Baruch <oferba@il.ibm.com> Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com> Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
2016-06-21 02:12:23 +08:00
int waiter[2];
char completed;
if (pipe(waiter)) {
perror("fuse_daemonize: pipe");
return -1;
}
/*
* demonize current process by forking it and killing the
* parent. This makes current process as a child of 'init'.
*/
switch(fork()) {
case -1:
perror("fuse_daemonize: fork");
return -1;
case 0:
break;
default:
libfuse/fuse_daemonize: wait until daemon child process is ready (#55) Mounting a FUSE file system remotely using SSH in combination with pseudo-terminal allocation (-t), results in "Transport endpoint is not connected" errors when trying to access the file system contents. For example: # ssh -t root@localhost "cmsfs-fuse /dev/disk/by-path/ccw-0.0.0190 /CMSFS" Connection to localhost closed. # ls /CMSFS ls: cannot access '/CMSFS': Transport endpoint is not connected The cmsfs-fuse main program (which can also be any other FUSE file system) calls into the fuse_main() libfuse library function. The fuse_main() function later calls fuse_daemonize() to fork the daemon process to handle the FUSE file system I/O. The fuse_daemonize() function calls fork() as usual. The child proceeds with setsid() and then redirecting its file descriptors to /dev/null etc. The parent process, simply exits. The child's functions and the parent's exit creates a subtle race. This is seen with an SSH connection. The SSH command above calls cmsfs-fuse on an allocated pseudo-terminal device (-t option). If the parent exits, SSH receives the command completion and closes the connection, that means, it closes the master side of the pseudo-terminal. This causes a HUP signal being sent to the process group on the pseudo-terminal. At this point in time, the child might not have completed the setsid() call and, hence, becomes terminated. Note that fuse daemon sets up its signal handlers after fuse_daemonize() has completed. Even if the child has the chance to disassociate from its parent process group to become it's own process group with setsid(), the child still has the pseudo-terminal opened as stdin, stdout, and stderr. So the pseudo-terminal still behave as controlling terminal and might cause a SIGHUP at closing the the master side. To solve the problem, the parent has to wait until the child (the fuse daemon process) has completed its processing, that means, has become its own process group with setsid() and closed any file descriptors pointing to the pseudo-terminal. Closes: #27 Reported-by: Ofer Baruch <oferba@il.ibm.com> Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com> Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
2016-06-21 02:12:23 +08:00
read(waiter[0], &completed, sizeof(completed));
_exit(0);
}
if (setsid() == -1) {
perror("fuse_daemonize: setsid");
2007-12-12 22:25:40 +08:00
return -1;
}
(void) chdir("/");
nullfd = open("/dev/null", O_RDWR, 0);
if (nullfd != -1) {
(void) dup2(nullfd, 0);
(void) dup2(nullfd, 1);
(void) dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
libfuse/fuse_daemonize: wait until daemon child process is ready (#55) Mounting a FUSE file system remotely using SSH in combination with pseudo-terminal allocation (-t), results in "Transport endpoint is not connected" errors when trying to access the file system contents. For example: # ssh -t root@localhost "cmsfs-fuse /dev/disk/by-path/ccw-0.0.0190 /CMSFS" Connection to localhost closed. # ls /CMSFS ls: cannot access '/CMSFS': Transport endpoint is not connected The cmsfs-fuse main program (which can also be any other FUSE file system) calls into the fuse_main() libfuse library function. The fuse_main() function later calls fuse_daemonize() to fork the daemon process to handle the FUSE file system I/O. The fuse_daemonize() function calls fork() as usual. The child proceeds with setsid() and then redirecting its file descriptors to /dev/null etc. The parent process, simply exits. The child's functions and the parent's exit creates a subtle race. This is seen with an SSH connection. The SSH command above calls cmsfs-fuse on an allocated pseudo-terminal device (-t option). If the parent exits, SSH receives the command completion and closes the connection, that means, it closes the master side of the pseudo-terminal. This causes a HUP signal being sent to the process group on the pseudo-terminal. At this point in time, the child might not have completed the setsid() call and, hence, becomes terminated. Note that fuse daemon sets up its signal handlers after fuse_daemonize() has completed. Even if the child has the chance to disassociate from its parent process group to become it's own process group with setsid(), the child still has the pseudo-terminal opened as stdin, stdout, and stderr. So the pseudo-terminal still behave as controlling terminal and might cause a SIGHUP at closing the the master side. To solve the problem, the parent has to wait until the child (the fuse daemon process) has completed its processing, that means, has become its own process group with setsid() and closed any file descriptors pointing to the pseudo-terminal. Closes: #27 Reported-by: Ofer Baruch <oferba@il.ibm.com> Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com> Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
2016-06-21 02:12:23 +08:00
/* Propagate completion of daemon initializatation */
completed = 1;
write(waiter[1], &completed, sizeof(completed));
close(waiter[0]);
close(waiter[1]);
} else {
(void) chdir("/");
2007-12-12 22:25:40 +08:00
}
return 0;
2006-01-07 16:30:41 +08:00
}
static struct fuse *fuse_setup(int argc, char *argv[],
2012-07-20 01:02:42 +08:00
const struct fuse_operations *op, size_t op_size,
char **mountpoint, int *multithreaded, void *user_data)
2004-10-21 17:35:10 +08:00
{
2007-12-12 22:25:40 +08:00
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
struct fuse *fuse;
int foreground;
int res;
res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground);
if (res == -1)
return NULL;
2012-07-20 01:02:42 +08:00
ch = fuse_mount(*mountpoint, &args);
2007-12-12 22:25:40 +08:00
if (!ch) {
fuse_opt_free_args(&args);
goto err_free;
}
2012-07-20 01:02:42 +08:00
fuse = fuse_new(ch, &args, op, op_size, user_data);
2007-12-12 22:25:40 +08:00
fuse_opt_free_args(&args);
if (fuse == NULL)
goto err_unmount;
res = fuse_daemonize(foreground);
if (res == -1)
goto err_unmount;
res = fuse_set_signal_handlers(fuse_get_session(fuse));
if (res == -1)
goto err_unmount;
return fuse;
err_unmount:
2012-07-20 01:02:42 +08:00
fuse_unmount(*mountpoint, ch);
2007-12-12 22:25:40 +08:00
if (fuse)
fuse_destroy(fuse);
err_free:
free(*mountpoint);
return NULL;
2004-10-21 17:35:10 +08:00
}
static void fuse_teardown(struct fuse *fuse, char *mountpoint)
2004-10-21 17:35:10 +08:00
{
2007-12-12 22:25:40 +08:00
struct fuse_session *se = fuse_get_session(fuse);
struct fuse_chan *ch = fuse_session_chan(se);
2007-12-12 22:25:40 +08:00
fuse_remove_signal_handlers(se);
2012-07-20 01:02:42 +08:00
fuse_unmount(mountpoint, ch);
2007-12-12 22:25:40 +08:00
fuse_destroy(fuse);
free(mountpoint);
2004-10-21 17:35:10 +08:00
}
2012-07-20 01:02:42 +08:00
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
size_t op_size, void *user_data)
2004-10-21 17:35:10 +08:00
{
2007-12-12 22:25:40 +08:00
struct fuse *fuse;
char *mountpoint;
int multithreaded;
int res;
2012-07-20 01:02:42 +08:00
fuse = fuse_setup(argc, argv, op, op_size, &mountpoint,
&multithreaded, user_data);
2007-12-12 22:25:40 +08:00
if (fuse == NULL)
return 1;
if (multithreaded)
res = fuse_loop_mt(fuse);
else
res = fuse_loop(fuse);
2012-07-20 01:02:42 +08:00
fuse_teardown(fuse, mountpoint);
2007-12-12 22:25:40 +08:00
if (res == -1)
return 1;
return 0;
2001-11-21 18:03:39 +08:00
}
int fuse_version(void)
{
2007-12-12 22:25:40 +08:00
return FUSE_VERSION;
}
const char *fuse_pkgversion(void)
{
return PACKAGE_VERSION;
}