diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2006-09-10 20:53:36 +0000 |
---|---|---|
committer | Miklos Szeredi <miklos@szeredi.hu> | 2006-09-10 20:53:36 +0000 |
commit | 0c59ebfc9b811c60fcf808a5de33320eeeb394af (patch) | |
tree | 3f906a752cc13a2f46b177b6a8fdcd9df2503309 /util | |
parent | 349bdda3524368ee30dc92cafb6914717017fcdd (diff) |
ulockmgr
Diffstat (limited to 'util')
-rw-r--r-- | util/.cvsignore | 1 | ||||
-rw-r--r-- | util/Makefile.am | 5 | ||||
-rw-r--r-- | util/ulockmgr_server.c | 347 |
3 files changed, 352 insertions, 1 deletions
diff --git a/util/.cvsignore b/util/.cvsignore index 87461c8..d7a4699 100644 --- a/util/.cvsignore +++ b/util/.cvsignore @@ -3,4 +3,5 @@ Makefile .deps .libs fusermount +ulockmgr_server fuse_ioslave diff --git a/util/Makefile.am b/util/Makefile.am index c07440a..8e8db2a 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -1,9 +1,12 @@ ## Process this file with automake to produce Makefile.in -bin_PROGRAMS = fusermount +bin_PROGRAMS = fusermount ulockmgr_server fusermount_SOURCES = fusermount.c +ulockmgr_server_SOURCES = ulockmgr_server.c +ulockmgr_server_LDFLAGS = -pthread + install-exec-hook: -chown root $(DESTDIR)$(bindir)/fusermount -chmod u+s $(DESTDIR)$(bindir)/fusermount diff --git a/util/ulockmgr_server.c b/util/ulockmgr_server.c new file mode 100644 index 0000000..a2d6863 --- /dev/null +++ b/util/ulockmgr_server.c @@ -0,0 +1,347 @@ +/* + ulockmgr_server: Userspace Lock Manager Server + Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +/* #define DEBUG 1 */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <pthread.h> +#include <stdint.h> +#include <errno.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +struct message { + int intr; + pthread_t thr; + int cmd; + int fd; + struct flock lock; + int error; +}; + +struct fd_store { + struct fd_store *next; + int fd; + int origfd; + int finished; +}; + +struct owner { + struct fd_store *fds; + pthread_mutex_t lock; +}; + +struct req_data { + struct owner *o; + int cfd; + struct fd_store *f; + struct message msg; +}; + +#define MAX_SEND_FDS 2 + +static int receive_message(int sock, void *buf, size_t buflen, int *fdp, + int numfds) +{ + struct msghdr msg; + struct iovec iov; + char ccmsg[CMSG_SPACE(sizeof(int)) * MAX_SEND_FDS]; + struct cmsghdr *cmsg; + int res; + + assert(numfds <= MAX_SEND_FDS); + iov.iov_base = buf; + iov.iov_len = buflen; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ccmsg; + msg.msg_controllen = sizeof(ccmsg); + + res = recvmsg(sock, &msg, MSG_WAITALL); + if (!res) + return 0; + if (res == -1) { + perror("ulockmgr_server: recvmsg"); + return -1; + } + if ((size_t) res != buflen) { + fprintf(stderr, "ulockmgr_server: short message received\n"); + return -1; + } + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg) { + if (!cmsg->cmsg_type == SCM_RIGHTS) { + fprintf(stderr, "ulockmgr_server: unknown control message %d\n", + cmsg->cmsg_type); + return -1; + } + memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * numfds); + } + return res; +} + +static int closefrom(int minfd) +{ + DIR *dir = opendir("/proc/self/fd"); + if (dir) { + int dfd = dirfd(dir); + struct dirent *ent; + while ((ent = readdir(dir))) { + char *end; + int fd = strtol(ent->d_name, &end, 10); + if (ent->d_name[0] && !end[0] && fd >= minfd && fd != dfd) + close(fd); + } + closedir(dir); + } + return 0; +} + +static void send_reply(int cfd, struct message *msg) +{ + int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL); + if (res == -1) + perror("ulockmgr_server: sending reply"); +#ifdef DEBUG + fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error); +#endif +} + +static void *process_request(void *d_) +{ + struct req_data *d = d_; + int res; + + assert(d->msg.cmd == F_SETLKW); + res = fcntl(d->f->fd, F_SETLK, &d->msg.lock); + if (res == -1 && errno == EAGAIN) { + d->msg.error = EAGAIN; + d->msg.thr = pthread_self(); + send_reply(d->cfd, &d->msg); + res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock); + } + d->msg.error = (res == -1) ? errno : 0; + pthread_mutex_lock(&d->o->lock); + d->f->finished = 1; + pthread_mutex_unlock(&d->o->lock); + send_reply(d->cfd, &d->msg); + close(d->cfd); + free(d); + + return NULL; +} + +static void process_message(struct owner *o, struct message *msg, int cfd, + int fd) +{ + struct fd_store *f; + struct req_data *d; + pthread_t tid; + int res; + +#ifdef DEBUG + fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n", + msg->cmd, msg->lock.l_type, msg->lock.l_whence, msg->lock.l_start, + msg->lock.l_len); +#endif + + if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK && + msg->lock.l_start == 0 && msg->lock.l_len == 0) { + struct fd_store **fp; + + for (fp = &o->fds; *fp;) { + struct fd_store *f = *fp; + if (f->origfd == msg->fd && f->finished) { + close(f->fd); + *fp = f->next; + free(f); + } else + fp = &f->next; + } + close(fd); + + msg->error = 0; + send_reply(cfd, msg); + close(cfd); + return; + } + + f = malloc(sizeof(struct fd_store)); + if (!f) { + msg->error = ENOLCK; + send_reply(cfd, msg); + close(cfd); + return; + } + + f->fd = fd; + f->origfd = msg->fd; + + if (msg->cmd == F_GETLK || msg->cmd == F_SETLK || + msg->lock.l_type == F_UNLCK) { + res = fcntl(f->fd, msg->cmd, &msg->lock); + msg->error = (res == -1) ? errno : 0; + send_reply(cfd, msg); + close(cfd); + f->next = o->fds; + o->fds = f; + f->finished = 1; + return; + } + + d = malloc(sizeof(struct req_data)); + if (!d) { + msg->error = ENOLCK; + send_reply(cfd, msg); + close(cfd); + free(f); + return; + } + + d->o = o; + d->cfd = cfd; + d->f = f; + d->msg = *msg; + res = pthread_create(&tid, NULL, process_request, d); + if (res) { + msg->error = ENOLCK; + send_reply(cfd, msg); + close(cfd); + free(d); + free(f); + return; + } + + f->next = o->fds; + o->fds = f; + pthread_detach(tid); +} + +static void sigusr1_handler(int sig) +{ + (void) sig; + /* Nothing to do */ +} + +static void process_owner(int cfd) +{ + struct owner o; + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = sigusr1_handler; + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGUSR1, &sa, NULL) == -1) { + perror("ulockmgr_server: cannot set sigusr1 signal handler"); + exit(1); + } + + memset(&o, 0, sizeof(struct owner)); + pthread_mutex_init(&o.lock, NULL); + while (1) { + struct message msg; + int rfds[2]; + int res; + + res = receive_message(cfd, &msg, sizeof(msg), rfds, 2); + if (!res) + break; + if (res == -1) + exit(1); + + if (msg.intr) + pthread_kill(msg.thr, SIGUSR1); + else { + pthread_mutex_lock(&o.lock); + process_message(&o, &msg, rfds[0], rfds[1]); + pthread_mutex_unlock(&o.lock); + } + } + if (o.fds) + fprintf(stderr, "ulockmgr_server: open file descriptors on exit\n"); +} + +int main(int argc, char *argv[]) +{ + int nullfd; + char *end; + int cfd; + sigset_t empty; + + if (argc != 2 || !argv[1][0]) + goto out_inval; + + cfd = strtol(argv[1], &end, 10); + if (*end) + goto out_inval; + + if (daemon(0, 1) == -1) { + perror("ulockmgr_server: daemon"); + exit(1); + } + + sigemptyset(&empty); + sigprocmask(SIG_SETMASK, &empty, NULL); + + if (dup2(cfd, 4) == -1) { + perror("ulockmgr_server: dup2"); + exit(1); + } + cfd = 4; + nullfd = open("/dev/null", O_RDWR); + dup2(nullfd, 0); + dup2(nullfd, 1); + close(3); + closefrom(5); + while (1) { + char c; + int sock; + int pid; + int res = receive_message(cfd, &c, sizeof(c), &sock, 1); + if (!res) + break; + if (res == -1) + exit(1); + + pid = fork(); + if (pid == -1) { + perror("ulockmgr_server: fork"); + close(sock); + continue; + } + if (pid == 0) { + close(cfd); + pid = fork(); + if (pid == -1) { + perror("ulockmgr_server: fork"); + _exit(1); + } + if (pid == 0) + process_owner(sock); + _exit(0); + } + waitpid(pid, NULL, 0); + close(sock); + } + return 0; + + out_inval: + fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]); + return 1; +} |