libfuse/lib/helper.c

257 lines
5.7 KiB
C
Raw Normal View History

2001-11-21 18:03:39 +08:00
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
#include "fuse.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <errno.h>
2002-04-18 22:41:48 +08:00
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <sys/wait.h>
2001-11-21 18:03:39 +08:00
2002-10-25 19:40:14 +08:00
#define FUSE_MOUNTED_ENV "_FUSE_MOUNTED"
#define FUSE_UMOUNT_CMD_ENV "_FUSE_UNMOUNT_CMD"
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
#define FUSERMOUNT_PROG "fusermount"
2001-11-21 18:03:39 +08:00
static void usage(char *progname)
{
fprintf(stderr,
"usage: %s mountpoint [options] \n"
"Options:\n"
" -d enable debug output\n"
" -s disable multithreaded operation\n"
" -h print help\n",
progname);
exit(1);
}
2002-10-25 19:40:14 +08:00
static char umount_cmd[1024];
static int fuse_fd;
2001-11-21 18:03:39 +08:00
2002-10-25 19:40:14 +08:00
static void fuse_unmount()
2001-11-21 18:03:39 +08:00
{
2002-10-25 19:40:14 +08:00
close(fuse_fd);
if(umount_cmd[0] != '\0')
system(umount_cmd);
2001-11-21 18:03:39 +08:00
}
/* return value:
* >= 0 => fd
* -1 => error
*/
2002-10-25 19:40:14 +08:00
static int receive_fd(int fd)
{
2002-04-18 22:41:48 +08:00
struct msghdr msg;
struct iovec iov;
char buf[1];
int rv;
int connfd = -1;
char ccmsg[CMSG_SPACE(sizeof(connfd))];
struct cmsghdr *cmsg;
iov.iov_base = buf;
iov.iov_len = 1;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* old BSD implementations should use msg_accrights instead of
* msg_control; the interface is different. */
msg.msg_control = ccmsg;
msg.msg_controllen = sizeof(ccmsg);
while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
2002-04-18 22:41:48 +08:00
if (rv == -1) {
perror("recvmsg");
return -1;
}
if(!rv) {
/* EOF */
fprintf(stderr, "got EOF\n");
return -1;
}
2002-04-18 22:41:48 +08:00
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg->cmsg_type == SCM_RIGHTS) {
fprintf(stderr, "got control message of unknown type %d\n",
cmsg->cmsg_type);
return -1;
}
return *(int*)CMSG_DATA(cmsg);
}
2002-10-25 19:40:14 +08:00
int fuse_mount(const char *mountpoint, const char *args[])
2002-04-19 23:57:02 +08:00
{
2002-10-25 19:40:14 +08:00
const char *mountprog = FUSERMOUNT_PROG;
2002-04-18 22:41:48 +08:00
int fds[2], pid;
2002-10-25 19:40:14 +08:00
int res;
int rv;
snprintf(umount_cmd, sizeof(umount_cmd) - 1, "%s -u %s", mountprog,
mountpoint);
2002-04-19 23:57:02 +08:00
2002-10-25 19:40:14 +08:00
res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
if(res == -1) {
perror("fuse: socketpair() failed");
2002-04-18 22:41:48 +08:00
return -1;
}
2002-10-25 19:40:14 +08:00
2002-04-18 22:41:48 +08:00
pid = fork();
2002-10-25 19:40:14 +08:00
if(pid == -1) {
perror("fuse: fork() failed");
2002-04-18 22:41:48 +08:00
close(fds[0]);
close(fds[1]);
return -1;
}
2002-10-25 19:40:14 +08:00
2002-04-19 23:57:02 +08:00
if(pid == 0) {
2002-10-25 19:40:14 +08:00
char env[10];
char **newargv;
int numargs = 0;
int actr;
int i;
if(args != NULL)
while(args[numargs] != NULL)
numargs ++;
newargv = (char **) malloc((1 + numargs + 2) * sizeof(char *));
actr = 0;
newargv[actr++] = strdup(mountprog);
for(i = 0; i < numargs; i++)
newargv[actr++] = strdup(args[i]);
newargv[actr++] = strdup(mountpoint);
newargv[actr++] = NULL;
2002-04-19 23:57:02 +08:00
close(fds[1]);
2002-10-25 19:40:14 +08:00
fcntl(fds[0], F_SETFD, 0);
snprintf(env, sizeof(env), "%i", fds[0]);
setenv(FUSE_COMMFD_ENV, env, 1);
execvp(mountprog, newargv);
perror("fuse: failed to exec fusermount");
2002-04-19 23:57:02 +08:00
exit(1);
2002-04-18 22:41:48 +08:00
}
2002-04-19 23:57:02 +08:00
close(fds[0]);
2002-10-25 19:40:14 +08:00
rv = receive_fd(fds[1]);
close(fds[1]);
waitpid(pid, NULL, 0); /* bury zombie */
2002-04-19 23:57:02 +08:00
return rv;
2002-04-18 22:41:48 +08:00
}
2001-11-21 18:03:39 +08:00
static void exit_handler()
{
exit(0);
}
static void set_signal_handlers()
{
struct sigaction sa;
sa.sa_handler = exit_handler;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) == -1 ||
sigaction(SIGINT, &sa, NULL) == -1 ||
sigaction(SIGTERM, &sa, NULL) == -1) {
perror("Cannot set exit signal handlers");
exit(1);
}
sa.sa_handler = SIG_IGN;
if(sigaction(SIGPIPE, &sa, NULL) == -1) {
perror("Cannot set ignored signals");
exit(1);
}
}
void fuse_main(int argc, char *argv[], const struct fuse_operations *op)
{
2002-10-25 19:40:14 +08:00
int argctr = 1;
2001-11-21 18:03:39 +08:00
int flags;
int multithreaded;
struct fuse *fuse;
2002-10-25 19:40:14 +08:00
char *isreexec = getenv(FUSE_MOUNTED_ENV);
2001-11-21 18:03:39 +08:00
2002-10-25 19:40:14 +08:00
if(isreexec == NULL) {
if(argc < 2 || argv[1][0] == '-')
usage(argv[0]);
fuse_fd = fuse_mount(argv[1], NULL);
if(fuse_fd == -1)
exit(1);
argctr++;
}
else {
char *tmpstr;
/* Old (obsolescent) way of doing the mount:
fusermount [options] mountpoint [program [args ...]]
fusermount execs this program and passes the control file
descriptor dup()-ed to stdin */
fuse_fd = 0;
tmpstr = getenv(FUSE_UMOUNT_CMD_ENV);
if(tmpstr != NULL)
strncpy(umount_cmd, tmpstr, sizeof(umount_cmd) - 1);
}
2001-11-21 18:03:39 +08:00
atexit(fuse_unmount);
set_signal_handlers();
flags = 0;
multithreaded = 1;
for(; argctr < argc && argv[argctr][0] == '-'; argctr ++) {
switch(argv[argctr][1]) {
case 'd':
flags |= FUSE_DEBUG;
break;
case 's':
multithreaded = 0;
break;
case 'h':
usage(argv[0]);
break;
default:
fprintf(stderr, "invalid option: %s\n", argv[argctr]);
exit(1);
}
}
if(argctr != argc) {
fprintf(stderr, "missing or surplus argument\n");
exit(1);
}
2002-10-25 19:40:14 +08:00
fuse = fuse_new(fuse_fd, flags, op);
2001-11-21 18:03:39 +08:00
if(multithreaded)
fuse_loop_mt(fuse);
else
fuse_loop(fuse);
}