diff options
Diffstat (limited to 'lib/ulockmgr.c')
-rw-r--r-- | lib/ulockmgr.c | 445 |
1 files changed, 0 insertions, 445 deletions
diff --git a/lib/ulockmgr.c b/lib/ulockmgr.c deleted file mode 100644 index 78f6859..0000000 --- a/lib/ulockmgr.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - libulockmgr: Userspace Lock Manager Library - Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu> - - This program can be distributed under the terms of the GNU LGPLv2. - See the file COPYING.LIB -*/ - -/* #define DEBUG 1 */ - -#include "config.h" -#include "ulockmgr.h" -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <pthread.h> -#include <errno.h> -#include <assert.h> -#include <signal.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/wait.h> - -struct message { - unsigned intr : 1; - unsigned nofd : 1; - pthread_t thr; - int cmd; - int fd; - struct flock lock; - int error; -}; - -struct fd_store { - struct fd_store *next; - int fd; - int inuse; -}; - -struct owner { - struct owner *next; - struct owner *prev; - struct fd_store *fds; - void *id; - size_t id_len; - int cfd; -}; - -static pthread_mutex_t ulockmgr_lock; -static int ulockmgr_cfd = -1; -static struct owner owner_list = { .next = &owner_list, .prev = &owner_list }; - -#define MAX_SEND_FDS 2 - -static void list_del_owner(struct owner *owner) -{ - struct owner *prev = owner->prev; - struct owner *next = owner->next; - prev->next = next; - next->prev = prev; -} - -static void list_add_owner(struct owner *owner, struct owner *next) -{ - struct owner *prev = next->prev; - owner->next = next; - owner->prev = prev; - prev->next = owner; - next->prev = owner; -} - -/* - * There's a bug in the linux kernel (< 2.6.22) recv() implementation - * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return - * zero, even if data was available. Retrying the recv will return - * the data in this case. - */ -static int do_recv(int sock, void *buf, size_t len, int flags) -{ - int res = recv(sock, buf, len, flags); - if (res == 0) - res = recv(sock, buf, len, flags); - - return res; -} - -static int ulockmgr_send_message(int sock, void *buf, size_t buflen, - int *fdp, int numfds) -{ - struct msghdr msg; - struct cmsghdr *p_cmsg; - struct iovec vec; - size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)]; - int res; - - assert(numfds <= MAX_SEND_FDS); - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - p_cmsg = CMSG_FIRSTHDR(&msg); - p_cmsg->cmsg_level = SOL_SOCKET; - p_cmsg->cmsg_type = SCM_RIGHTS; - p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds); - memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds); - msg.msg_controllen = p_cmsg->cmsg_len; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &vec; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - vec.iov_base = buf; - vec.iov_len = buflen; - res = sendmsg(sock, &msg, MSG_NOSIGNAL); - if (res == -1) { - perror("libulockmgr: sendmsg"); - return -1; - } - if ((size_t) res != buflen) { - fprintf(stderr, "libulockmgr: sendmsg short\n"); - return -1; - } - return 0; -} - -static int ulockmgr_start_daemon(void) -{ - int sv[2]; - int res; - char tmp[64]; - - res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); - if (res == -1) { - perror("libulockmgr: socketpair"); - return -1; - } - snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]); - res = system(tmp); - close(sv[0]); - if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) { - close(sv[1]); - return -1; - } - ulockmgr_cfd = sv[1]; - return 0; -} - -static struct owner *ulockmgr_new_owner(const void *id, size_t id_len) -{ - int sv[2]; - int res; - char c = 'm'; - struct owner *o; - - if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1) - return NULL; - - o = calloc(1, sizeof(struct owner) + id_len); - if (!o) { - fprintf(stderr, "libulockmgr: failed to allocate memory\n"); - return NULL; - } - o->id = o + 1; - o->id_len = id_len; - res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); - if (res == -1) { - perror("libulockmgr: socketpair"); - goto out_free; - } - res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1); - close(sv[0]); - if (res == -1) { - close(ulockmgr_cfd); - ulockmgr_cfd = -1; - goto out_close; - } - - o->cfd = sv[1]; - memcpy(o->id, id, id_len); - list_add_owner(o, &owner_list); - - return o; - -out_close: - close(sv[1]); -out_free: - free(o); - return NULL; -} - -static int ulockmgr_send_request(struct message *msg, const void *id, - size_t id_len) -{ - int sv[2]; - int cfd; - struct owner *o; - struct fd_store *f = NULL; - struct fd_store *newf = NULL; - struct fd_store **fp; - int fd = msg->fd; - int cmd = msg->cmd; - int res; - int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK && - msg->lock.l_start == 0 && msg->lock.l_len == 0); - - for (o = owner_list.next; o != &owner_list; o = o->next) - if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0) - break; - - if (o == &owner_list) - o = NULL; - - if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK) - o = ulockmgr_new_owner(id, id_len); - - if (!o) { - if (cmd == F_GETLK) { - res = fcntl(msg->fd, F_GETLK, &msg->lock); - return (res == -1) ? -errno : 0; - } else if (msg->lock.l_type == F_UNLCK) - return 0; - else - return -ENOLCK; - } - - if (unlockall) - msg->nofd = 1; - else { - for (fp = &o->fds; *fp; fp = &(*fp)->next) { - f = *fp; - if (f->fd == fd) { - msg->nofd = 1; - break; - } - } - } - - if (!msg->nofd) { - newf = f = calloc(1, sizeof(struct fd_store)); - if (!f) { - fprintf(stderr, "libulockmgr: failed to allocate memory\n"); - return -ENOLCK; - } - } - - res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); - if (res == -1) { - perror("libulockmgr: socketpair"); - free(newf); - return -ENOLCK; - } - - cfd = sv[1]; - sv[1] = msg->fd; - res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv, - msg->nofd ? 1 : 2); - close(sv[0]); - if (res == -1) { - free(newf); - close(cfd); - return -EIO; - } - - if (newf) { - newf->fd = msg->fd; - newf->next = o->fds; - o->fds = newf; - } - if (f) - f->inuse++; - - res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL); - if (res == -1) { - perror("libulockmgr: recv"); - msg->error = EIO; - } else if (res != sizeof(struct message)) { - fprintf(stderr, "libulockmgr: recv short\n"); - msg->error = EIO; - } else if (cmd == F_SETLKW && msg->error == EAGAIN) { - pthread_mutex_unlock(&ulockmgr_lock); - while (1) { - sigset_t old; - sigset_t unblock; - int errno_save; - - sigemptyset(&unblock); - sigaddset(&unblock, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &unblock, &old); - res = do_recv(cfd, msg, sizeof(struct message), - MSG_WAITALL); - errno_save = errno; - pthread_sigmask(SIG_SETMASK, &old, NULL); - if (res == sizeof(struct message)) - break; - else if (res >= 0) { - fprintf(stderr, "libulockmgr: recv short\n"); - msg->error = EIO; - break; - } else if (errno_save != EINTR) { - errno = errno_save; - perror("libulockmgr: recv"); - msg->error = EIO; - break; - } - msg->intr = 1; - res = send(o->cfd, msg, sizeof(struct message), - MSG_NOSIGNAL); - if (res == -1) { - perror("libulockmgr: send"); - msg->error = EIO; - break; - } - if (res != sizeof(struct message)) { - fprintf(stderr, "libulockmgr: send short\n"); - msg->error = EIO; - break; - } - } - pthread_mutex_lock(&ulockmgr_lock); - - } - if (f) - f->inuse--; - close(cfd); - if (unlockall) { - for (fp = &o->fds; *fp;) { - f = *fp; - if (f->fd == fd && !f->inuse) { - *fp = f->next; - free(f); - } else - fp = &f->next; - } - if (!o->fds) { - list_del_owner(o); - close(o->cfd); - free(o); - } - /* Force OK on unlock-all, since it _will_ succeed once the - owner is deleted */ - msg->error = 0; - } - - return -msg->error; -} - -#ifdef DEBUG -static uint32_t owner_hash(const unsigned char *id, size_t id_len) -{ - uint32_t h = 0; - size_t i; - for (i = 0; i < id_len; i++) - h = ((h << 8) | (h >> 24)) ^ id[i]; - - return h; -} -#endif - -static int ulockmgr_canonicalize(int fd, struct flock *lock) -{ - off_t offset; - if (lock->l_whence == SEEK_CUR) { - offset = lseek(fd, 0, SEEK_CUR); - if (offset == (off_t) -1) - return -errno; - } else if (lock->l_whence == SEEK_END) { - struct stat stbuf; - int res = fstat(fd, &stbuf); - if (res == -1) - return -errno; - - offset = stbuf.st_size; - } else - offset = 0; - - lock->l_whence = SEEK_SET; - lock->l_start += offset; - - if (lock->l_start < 0) - return -EINVAL; - - if (lock->l_len < 0) { - lock->l_start += lock->l_len; - if (lock->l_start < 0) - return -EINVAL; - lock->l_len = -lock->l_len; - } - if (lock->l_len && lock->l_start + lock->l_len - 1 < 0) - return -EINVAL; - - return 0; -} - -int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner, - size_t owner_len) -{ - int err; - struct message msg; - sigset_t old; - sigset_t block; - - if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW) - return -EINVAL; - - if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK && - lock->l_type != F_UNLCK) - return -EINVAL; - - if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR && - lock->l_whence != SEEK_END) - return -EINVAL; - -#ifdef DEBUG - fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n", - cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len, - owner_hash(owner, owner_len)); -#endif - - /* Unlock should never block anyway */ - if (cmd == F_SETLKW && lock->l_type == F_UNLCK) - cmd = F_SETLK; - - memset(&msg, 0, sizeof(struct message)); - msg.cmd = cmd; - msg.fd = fd; - msg.lock = *lock; - err = ulockmgr_canonicalize(fd, &msg.lock); - if (err) - return err; - - sigemptyset(&block); - sigaddset(&block, SIGUSR1); - pthread_sigmask(SIG_BLOCK, &block, &old); - pthread_mutex_lock(&ulockmgr_lock); - err = ulockmgr_send_request(&msg, owner, owner_len); - pthread_mutex_unlock(&ulockmgr_lock); - pthread_sigmask(SIG_SETMASK, &old, NULL); - if (!err && cmd == F_GETLK) { - if (msg.lock.l_type == F_UNLCK) - lock->l_type = F_UNLCK; - else - *lock = msg.lock; - } - - return err; -} |