ulockmgr
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;
+}