summaryrefslogtreecommitdiffstats
path: root/liblock
diff options
context:
space:
mode:
Diffstat (limited to 'liblock')
-rw-r--r--liblock/.gitignore3
-rw-r--r--liblock/Makefile.am45
-rw-r--r--liblock/configure.in217
-rw-r--r--liblock/liblock.h108
-rw-r--r--liblock/lockdaemon.c374
-rw-r--r--liblock/lockfcntl.c35
-rw-r--r--liblock/lockflock.c35
-rw-r--r--liblock/locklockf.c45
-rw-r--r--liblock/lockmail.c186
-rw-r--r--liblock/lockmail.sgml204
-rw-r--r--liblock/locktest.c84
-rw-r--r--liblock/mail.c503
-rw-r--r--liblock/mail.h108
13 files changed, 1947 insertions, 0 deletions
diff --git a/liblock/.gitignore b/liblock/.gitignore
new file mode 100644
index 0000000..0504b2b
--- /dev/null
+++ b/liblock/.gitignore
@@ -0,0 +1,3 @@
+/lockmail
+/lockmail.1
+/lockmail.html
diff --git a/liblock/Makefile.am b/liblock/Makefile.am
new file mode 100644
index 0000000..333de47
--- /dev/null
+++ b/liblock/Makefile.am
@@ -0,0 +1,45 @@
+#
+# Copyright 1998 - 2002 Double Precision, Inc. See COPYING for
+# distribution information.
+#
+
+
+noinst_LTLIBRARIES=liblock.la
+noinst_PROGRAMS=lockmail
+
+if USE_FCNTL
+fcntl=lockfcntl.c
+else
+fcntl=
+endif
+
+if USE_FLOCK
+flock=lockflock.c
+else
+flock=
+endif
+
+if USE_LOCKF
+lockf=locklockf.c
+else
+lockf=
+endif
+
+liblock_la_SOURCES=$(fcntl) $(flock) $(lockf) liblock.h lockdaemon.c \
+ mail.h mail.c
+
+lockmail_SOURCES=lockmail.c
+lockmail_DEPENDENCIES=liblock.la ../numlib/libnumlib.la
+lockmail_LDADD=liblock.la ../numlib/libnumlib.la
+lockmail_LDFLAGS=-static
+
+noinst_DATA=lockmail.html lockmail.1
+EXTRA_DIST=lockfcntl.c lockflock.c locklockf.c locktest.c $(noinst_DATA)
+
+if HAVE_SGML
+lockmail.html: lockmail.sgml ../docbook/sgml2html
+ ../docbook/sgml2html lockmail.sgml lockmail.html
+
+lockmail.1: lockmail.sgml ../docbook/sgml2man
+ ../docbook/sgml2man lockmail.sgml lockmail.1
+endif
diff --git a/liblock/configure.in b/liblock/configure.in
new file mode 100644
index 0000000..4d2907d
--- /dev/null
+++ b/liblock/configure.in
@@ -0,0 +1,217 @@
+dnl
+dnl Copyright 1998 - 2004 Double Precision, Inc. See COPYING for
+dnl distribution information.
+
+AC_PREREQ(2.59)
+AC_INIT(liblock, 0.60, courier-users@lists.sourceforge.net)
+
+>confdefs.h # Kill PACKAGE_ macros
+
+AC_CONFIG_SRCDIR([liblock.h])
+AC_CONFIG_AUX_DIR(../..)
+AM_CONFIG_HEADER([config.h])
+AM_INIT_AUTOMAKE([foreign no-define])
+
+CFLAGS="-I.. -I${srcdir}/.. $CFLAGS"
+
+dnl Checks for programs.
+AC_PROG_AWK
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_LIBTOOL
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_CHECK_HEADERS(errno.h fcntl.h sys/fcntl.h sys/ioctl.h sys/file.h unistd.h sysexits.h)
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+
+AC_C_CONST
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIGNAL
+AC_SYS_LARGEFILE
+
+AC_CACHE_CHECK( [for off64_t], liblock_cv_hasoff64t,
+
+liblock_cv_hasoff64t="no"
+AC_TRY_COMPILE([
+#include <sys/types.h>
+],[
+off64_t n;
+
+ n=0;
+], liblock_cv_hasoff64t="yes")
+
+)
+
+if test "$liblock_cv_hasoff64t" = "yes"
+then
+ LL_OFFSET_TYPE=off64_t
+else
+ LL_OFFSET_TYPE=off_t
+fi
+AC_DEFINE_UNQUOTED(LL_OFFSET_TYPE,$LL_OFFSET_TYPE,
+ [ Either off64_t or off_t ])
+
+AC_CACHE_CHECK( [for flock_t], liblock_cv_hasflock_t,
+liblock_cv_hasflock_t="no"
+AC_TRY_COMPILE([
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/types.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+],[
+flock_t t;
+], liblock_cv_hasflock_t="yes"))
+
+if test "$liblock_cv_hasflock_t" = "yes"
+then
+ AC_DEFINE_UNQUOTED(HAS_FLOCK_T,1,[ Whether flock_t is defined ])
+fi
+
+dnl Checks for library functions.
+
+AC_CHECK_FUNCS(getdtablesize setpgrp sysconf)
+AC_CHECK_FUNC(setpgrp,
+ [
+ AC_FUNC_SETPGRP
+ ]
+)
+
+AC_CACHE_CHECK( [for fcntl], liblock_cv_hasfcntl,
+liblock_cv_hasfcntl="no"
+AC_TRY_LINK([
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/lockfcntl.c"
+],[
+int n;
+], liblock_cv_hasfcntl="yes"))
+if test "$liblock_cv_hasfcntl" = "yes"
+then
+ AC_DEFINE_UNQUOTED(HAS_FCNTL,1,
+ [ Whether fcntl() available ])
+fi
+
+AC_CACHE_CHECK( [for flock], liblock_cv_hasflock,
+liblock_cv_hasflock="no"
+AC_TRY_LINK([
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/lockflock.c"
+],[
+int n;
+], liblock_cv_hasflock="yes"))
+if test "$liblock_cv_hasflock" = "yes"
+then
+ AC_DEFINE_UNQUOTED(HAS_FLOCK,1,
+ [ Whether flock() is available ])
+fi
+
+AC_CACHE_CHECK( [for lockf], liblock_cv_haslockf,
+liblock_cv_haslockf="no"
+AC_TRY_LINK([
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locklockf.c"
+],[
+int n;
+], liblock_cv_haslockf="yes"))
+if test "$liblock_cv_haslockf" = "yes"
+then
+ AC_DEFINE_UNQUOTED(HAS_LOCKF,1,
+ [ Whether lockf() is available ])
+fi
+
+AC_MSG_CHECKING(for locking method)
+AC_ARG_WITH(locking-method, [], lockmethod="$withval", lockmethod="")
+
+case x$lockmethod in
+xfcntl)
+ AC_TRY_RUN([
+#define USE_FCNTL 1
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locktest.c"
+], , AC_MSG_ERROR(fcntl test failed.),
+ :
+ )
+ ;;
+x)
+ AC_TRY_RUN([
+#define USE_FCNTL 1
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locktest.c"
+], lockmethod=fcntl, ,AC_MSG_ERROR(--with-locking-method option required))
+ ;;
+esac
+
+case x$lockmethod in
+xlockf)
+ AC_TRY_RUN([
+#define USE_LOCKF 1
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locktest.c"
+], , AC_MSG_ERROR(lockf test failed.),
+ :
+ )
+ ;;
+x)
+ AC_TRY_RUN([
+#define USE_LOCKF 1
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locktest.c"
+], lockmethod=lockf, , AC_MSG_ERROR(--with-locking-method option required))
+ ;;
+esac
+
+
+case x$lockmethod in
+xflock)
+ AC_TRY_RUN([
+#define USE_FLOCK 1
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locktest.c"
+], , AC_MSG_ERROR(flock test failed.),
+ :
+ )
+ ;;
+x)
+ AC_TRY_RUN([
+#define USE_FLOCK 1
+#define LL_OFFSET_TYPE $LL_OFFSET_TYPE
+#include "${srcdir}/locktest.c"
+], lockmethod=flock, , AC_MSG_ERROR(--with-locking-method option required))
+ ;;
+esac
+
+case x$lockmethod in
+xlockf)
+ ;;
+xflock)
+ ;;
+xfcntl)
+ ;;
+*)
+ AC_MSG_ERROR(must specify --with-locking-method option)
+ ;;
+esac
+
+AC_MSG_RESULT($lockmethod)
+
+AM_CONDITIONAL(USE_FCNTL, test "$lockmethod" = "fcntl")
+AM_CONDITIONAL(USE_FLOCK, test "$lockmethod" = "flock")
+AM_CONDITIONAL(USE_LOCKF, test "$lockmethod" = "lockf")
+
+AM_CONDITIONAL(HAVE_SGML, test -d ${srcdir}/../docbook)
+
+AC_DEFINE_UNQUOTED(liblock_config_h,1, [liblock/config.h has been read])
+
+AC_OUTPUT(Makefile)
diff --git a/liblock/liblock.h b/liblock/liblock.h
new file mode 100644
index 0000000..db43bd4
--- /dev/null
+++ b/liblock/liblock.h
@@ -0,0 +1,108 @@
+
+#ifndef liblock_h
+#define liblock_h
+
+/*
+** Copyright 1998 - 1999 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if HAVE_CONFIG_H
+#include "liblock/config.h"
+#endif
+#include <sys/types.h>
+
+#define ll_whence_start 0
+#define ll_whence_curpos 1
+#define ll_whence_end 2
+
+#define ll_readlock 0
+#define ll_writelock 4
+#define ll_unlock 8
+#define ll_wait 16
+
+int ll_lockfd(int, /* File descriptor */
+ int, /* ll_ bitmask */
+ LL_OFFSET_TYPE, /* Start */
+ LL_OFFSET_TYPE); /* Length */
+
+
+/* Some useful macros: ll_lock_ex - exclusive lock on a file,
+ ll_lock_ex_test - attempt an exclusive lock on a file
+ ll_unlock_ex - unlock a file
+*/
+
+#define ll_lock_ex(f) \
+ ll_lockfd( (f), ll_writelock|ll_whence_start|ll_wait, 0, 0)
+
+#define ll_lock_ex_test(f) \
+ ll_lockfd( (f), ll_writelock|ll_whence_start, 0, 0)
+
+#define ll_unlock_ex(f) \
+ ll_lockfd( (f), ll_unlock|ll_whence_start, 0, 0)
+
+
+/*
+** Value-added: functions that reliably start and stop a daemon process,
+** permitting only one daemon process running. Utilizes a lock file, and a
+** pidfile.
+*/
+
+int ll_daemon_start(const char *lockfile);
+void ll_daemon_started(const char *pidfile, int fd);
+int ll_daemon_resetio();
+int ll_daemon_stop(const char *lockfile, const char *pidfile);
+int ll_daemon_restart(const char *lockfile, const char *pidfile);
+
+/*
+ The basic scenario
+
+main()
+{
+ if ((fd=ll_daemon_start(lockfilename)) < 0)
+ {
+ error(); exit(1);
+ }
+
+ ... Some custom initialization here ...
+
+ ll_daemon_started(pidfile, fd);
+
+ ll_daemon_resetio(); ... this one is optional
+}
+
+To stop this daemon:
+
+ll_daemon_stop (lockfilename, pidfile)
+
+
+ll_daemon_start attempts to start a daemon process going. It does only
+a partial setup. If it detects that the daemon process is already
+running, it itself does an exit(0), not returning to the parent.
+
+If there was a failure starting a daemon process, -1 is return, else
+we return a transparent file descriptor, which will have to be passed as
+the secodn argument to ll_daemon_started().
+
+When ll_daemon_start returns, we're already running in a partially set-up
+daemon process. The setup isn't complete just yet. The parent function
+can perform any other custom initialization. If initialization fails,
+the parent function can simply exit. Otherwise, if the initialization
+completes, ll_daemon_started must be called in order to save this daemon
+process's pid in the pid file (2nd arg must be the return from ll_daemon_start.
+
+To stop a daemon process, simply call ll_daemon_stop. Nothing too
+sophisticated here.
+
+To send the daemon process a SIGHUP, call ll_daemon_restart.
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/liblock/lockdaemon.c b/liblock/lockdaemon.c
new file mode 100644
index 0000000..af0027b
--- /dev/null
+++ b/liblock/lockdaemon.c
@@ -0,0 +1,374 @@
+/*
+** Copyright 2000-2007 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#include "config.h"
+#include "liblock.h"
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <sys/types.h>
+#include "../numlib/numlib.h"
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#define exit(_a_) _exit(_a_)
+
+
+static int start1(const char *, int);
+
+#define CONSOLE "/dev/null"
+
+int ll_daemon_start(const char *lockfile)
+{
+pid_t p;
+int pipefd[2];
+char c;
+int i;
+
+ /*
+ ** Close any open file descriptors.
+ */
+
+ for (i=3; i < 256; i++)
+ close(i);
+
+ /*
+ ** We fork, and set up a pipe from the child process. If we read
+ ** a single 0 byte from the pipe, it means that the child has
+ ** successfully initialized, and will return to main, so we exit(0).
+ ** If we do not read a single 0 byte from the pipe, it means that
+ ** there was an initialization error, so we return -1 to main.
+ */
+
+ if (pipe(pipefd) < 0)
+ {
+ perror("pipe");
+ return (-1);
+ }
+
+ if ((p=fork()) == -1)
+ {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ perror("fork");
+ return (-1);
+ }
+
+ if (p == 0)
+ {
+ close(pipefd[0]);
+
+ /*
+ ** We fork once more, so that the daemon process will not
+ ** be the child process of anyone.
+ */
+
+ p=fork();
+ if (p == -1)
+ {
+ perror("fork");
+ exit(0);
+ }
+ if (p)
+ exit(0);
+
+ /*
+ ** Continue initialization in start1()
+ */
+ return (start1(lockfile, pipefd[1]));
+ }
+
+ close(pipefd[1]);
+ if (read(pipefd[0], &c, 1) <= 0)
+ c=1;
+ close(pipefd[0]);
+ if (c == 0)
+ exit (0); /* Successful start of daemon */
+ errno=EAGAIN;
+ return (-1);
+}
+
+static int start1(const char *lockfile, int fd)
+{
+int lockfd, maxfd;
+
+#if HAVE_SETPGRP
+#if SETPGRP_VOID
+ setpgrp();
+#else
+ setpgrp(0, 0);
+#endif
+#endif
+#ifdef TIOCNOTTY
+
+ {
+ int fd=open("/dev/tty", O_RDWR);
+
+ if (fd >= 0)
+ {
+ ioctl(fd, TIOCNOTTY, 0);
+ close(fd);
+ }
+ }
+#endif
+
+
+ /* Attempt to obtain a lock */
+
+ lockfd=open(lockfile, O_RDWR|O_CREAT, 0600);
+
+ if (lockfd < 0)
+ {
+ /* Perhaps an upgraded daemon runs under new uid? */
+
+ unlink(lockfile);
+ lockfd=open(lockfile, O_RDWR|O_CREAT, 0600);
+ }
+
+#if HAVE_GETDTABLESIZE
+ maxfd=getdtablesize()-1;
+#elif defined(OPEN_MAX)
+ maxfd=OPEN_MAX-1;
+#elif HAVE_SYSCONF && defined(_SC_OPEN_MAX)
+ if ((maxfd=sysconf(_SC_OPEN_MAX)) < 0)
+ maxfd=63;
+ else if (maxfd > 0)
+ maxfd--;
+#else
+ maxfd=63;
+#endif
+
+ if (lockfd < 0 || dup2(lockfd, maxfd) != maxfd)
+ {
+ perror(lockfile);
+ exit(1);
+ }
+
+ close(lockfd);
+ lockfd=maxfd;
+
+#ifdef FD_CLOEXEC
+ if (fcntl(lockfd, F_SETFD, FD_CLOEXEC) < 0)
+ {
+ perror("fcntl");
+ close(lockfd);
+ exit(1);
+ }
+#endif
+
+ if (ll_lock_ex_test(lockfd))
+ {
+ if (write(fd, "", 1) != 1)
+ exit(1); /* Shouldn't happen */
+
+ close(fd);
+ exit (0); /* Already running, pretend success */
+ }
+
+ /*
+ ** Return >0 to main, so it can continue main's setup.
+ */
+
+ return (fd);
+}
+
+int ll_daemon_resetio()
+{
+int i;
+
+ close(0);
+ if (open("/dev/null", O_RDONLY) != 0)
+ return (-1);
+
+ close(1);
+ i=open(CONSOLE, O_WRONLY);
+ if (i < 0) i=open("/dev/null", O_WRONLY);
+ if (i != 1) return (-1);
+
+ close(2);
+ i=open(CONSOLE, O_WRONLY);
+ if (i < 0) i=open("/dev/null", O_WRONLY);
+ if (i != 2) return (-1);
+ return (0);
+}
+
+void ll_daemon_started(const char *pidfile, int fd)
+{
+char buf[NUMBUFSIZE+1];
+char *p=strcat(libmail_str_pid_t(getpid(), buf), "\n");
+FILE *fp;
+
+ unlink(pidfile);
+ if ((fp=fopen(pidfile, "w")) == NULL ||
+ fprintf(fp, "%s", p) < 0 || fflush(fp) < 0 || fclose(fp))
+ {
+ perror(pidfile);
+ exit(1);
+ }
+
+ if (write(fd, "", 1) != 1) /* Signal waiting parent */
+ exit(1); /* Shouldn't happen */
+ close(fd);
+}
+
+static void stop1(const char *, const char *);
+
+int ll_daemon_stop(const char *lockfile, const char *pidfile)
+{
+pid_t p, p2;
+int waitstat;
+
+ /*
+ ** We fork, and the child process attempts to stop the daemon,
+ ** then communicates the success to us, via its exit code.
+ */
+
+ signal(SIGCHLD, SIG_DFL);
+ if ((p=fork()) == -1)
+ {
+ perror("fork");
+ return (1);
+ }
+ if (p == 0) stop1(lockfile, pidfile);
+
+ while ((p2=wait(&waitstat)) != p)
+ ;
+
+ if (WIFEXITED(waitstat))
+ return (WEXITSTATUS(waitstat));
+ return (0);
+}
+
+/*
+** The child process forks too. The parent process goes in a loop,
+** trying to kill the daemon process.
+**
+** The child process attempts to lock the lock file. When it
+** succeeds, it exits. When the child process exits, the parent
+** process kills itself.
+*/
+
+static RETSIGTYPE sigexit(int signum)
+{
+ kill(getpid(), SIGKILL);
+#if RETSIGTYPE != void
+ return (0);
+#endif
+}
+
+static void stop1(const char *lockfile, const char *pidfile)
+{
+int lockfd;
+pid_t p;
+
+ if ((lockfd=open(lockfile, O_RDWR|O_CREAT, 0600)) < 0)
+ {
+ perror(lockfile);
+ exit(1);
+ }
+
+ if ( ll_lock_ex_test(lockfd) == 0) /* No daemon process running */
+ {
+ close(lockfd);
+ exit (0); /* That was easy! */
+ }
+
+ signal(SIGCHLD, sigexit);
+
+ if ((p=fork()) == -1)
+ {
+ perror("fork");
+ exit(1);
+ }
+
+ if (p) /* Parent - first sends a SIGTERM, then a SIGKILL */
+ {
+ int signum=SIGTERM;
+
+ close(lockfd);
+ for (;; sleep(10))
+ {
+ FILE *fp;
+ int c;
+
+ if ((fp=fopen(pidfile, "r")) == NULL)
+ continue;
+
+ p=0;
+
+ while ((c=getc(fp)) != EOF && c != '\n')
+ {
+ if (isdigit(c))
+ p=p*10+(c-'0');
+ }
+
+ fclose(fp);
+ if (p)
+ kill(p, signum);
+ signum=SIGKILL;
+ }
+ }
+
+ if (ll_lock_ex(lockfd))
+ perror("lock");
+ close(lockfd);
+ exit(0);
+}
+
+int ll_daemon_restart(const char *lockfile, const char *pidfile)
+{
+int lockfd;
+pid_t p;
+FILE *fp;
+int c;
+
+ if ((lockfd=open(lockfile, O_RDWR|O_CREAT, 0600)) < 0)
+ {
+ perror(lockfile);
+ return (1);
+ }
+
+ if ( ll_lock_ex_test(lockfd) == 0) /* No daemon process running */
+ {
+ close(lockfd);
+ return (0); /* That was easy! */
+ }
+ close(lockfd);
+
+ if ((fp=fopen(pidfile, "r")) == NULL)
+ return (0);
+
+ p=0;
+
+ while ((c=getc(fp)) != EOF && c != '\n')
+ {
+ if (isdigit(c))
+ p=p*10+(c-'0');
+ }
+
+ fclose(fp);
+ if (p)
+ kill(p, SIGHUP);
+ return (0);
+}
+
diff --git a/liblock/lockfcntl.c b/liblock/lockfcntl.c
new file mode 100644
index 0000000..23be7aa
--- /dev/null
+++ b/liblock/lockfcntl.c
@@ -0,0 +1,35 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/types.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#include "liblock.h"
+
+int ll_lockfd(int fd, int ltype, LL_OFFSET_TYPE start, LL_OFFSET_TYPE len)
+{
+#if HAS_FLOCK_T
+flock_t ft;
+#else
+struct flock ft;
+#endif
+
+ ft.l_type=ltype & ll_unlock ? F_UNLCK:
+ ltype & ll_writelock ? F_WRLCK:F_RDLCK;
+ ft.l_whence=ltype & ll_whence_curpos ? 1:
+ ltype & ll_whence_end ? 2:0;
+ ft.l_start=start;
+ ft.l_len=len;
+
+ return (fcntl(fd, (ltype & ll_unlock) == 0 && (ltype & ll_wait)
+ ? F_SETLKW:F_SETLK, &ft));
+}
diff --git a/liblock/lockflock.c b/liblock/lockflock.c
new file mode 100644
index 0000000..0e53aa3
--- /dev/null
+++ b/liblock/lockflock.c
@@ -0,0 +1,35 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/types.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#include <errno.h>
+#include "liblock.h"
+
+int ll_lockfd(int fd, int ltype, LL_OFFSET_TYPE start, LL_OFFSET_TYPE len)
+{
+ if (start || len
+ || (ltype & ll_whence_curpos)
+ || (ltype & ll_whence_end))
+ {
+ errno=EINVAL;
+ return (-1);
+ }
+
+ return (flock(fd, ltype & ll_unlock ? LOCK_UN:
+ (ltype & ll_writelock ? LOCK_EX:LOCK_SH) |
+ (ltype & ll_wait ? 0:LOCK_NB)));
+}
diff --git a/liblock/locklockf.c b/liblock/locklockf.c
new file mode 100644
index 0000000..63c0a98
--- /dev/null
+++ b/liblock/locklockf.c
@@ -0,0 +1,45 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/types.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "liblock.h"
+
+int ll_lockfd(int fd, int ltype, LL_OFFSET_TYPE start, LL_OFFSET_TYPE len)
+{
+off_t p;
+
+ if (ltype & ll_whence_curpos)
+ p=lseek(fd, start, SEEK_CUR);
+ else if (ltype && ll_whence_end)
+ p=lseek(fd, start, SEEK_END);
+ else p=lseek(fd, start, SEEK_SET);
+
+ if (p < 0) return (-1);
+
+ if (lockf(fd, ltype & ll_unlock ? F_ULOCK:
+ ltype & ll_wait ? F_LOCK:F_TLOCK, len))
+ {
+ lseek(fd, p, SEEK_SET);
+ return (-1);
+ }
+ lseek(fd, SEEK_SET, p);
+ return (0);
+}
diff --git a/liblock/lockmail.c b/liblock/lockmail.c
new file mode 100644
index 0000000..a2c81ee
--- /dev/null
+++ b/liblock/lockmail.c
@@ -0,0 +1,186 @@
+/*
+** Copyright 2002-2006 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#include "config.h"
+#include "liblock.h"
+#include "mail.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#if HAVE_SYSEXITS_H
+#include <sysexits.h>
+#endif
+
+#ifndef EX_TEMPFAIL
+#define EX_TEMPFAIL 75
+#endif
+
+
+#define NTRIES_DEFAULT 60
+#define DELAY 5
+
+static int sig=0;
+
+static int catch(int signum)
+{
+ sig=1;
+ signal(SIGHUP, (RETSIGTYPE (*)(int))catch);
+ signal(SIGTERM, (RETSIGTYPE (*)(int))catch);
+ signal(SIGINT, (RETSIGTYPE (*)(int))catch);
+ return 0;
+}
+
+static int caught()
+{
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ return sig;
+}
+
+int main(int argc, char **argv)
+{
+ char **argvec;
+ int n;
+ int fd;
+ pid_t pid, pid2;
+ int waitstat;
+ struct ll_mail *p;
+ int ntries=NTRIES_DEFAULT;
+ int readonly=0;
+ int optchar;
+
+ while ((optchar=getopt(argc, argv, "+rt:")) != -1)
+ switch (optchar) {
+ case 'r':
+ readonly=1;
+ break;
+ case 't':
+ ntries=atoi(optarg);
+ if (ntries < 0)
+ {
+ fprintf(stderr, "%s: invalid argument to -t\n",
+ argv[0]);
+ exit(EX_TEMPFAIL);
+ }
+ ntries= (ntries / DELAY) + 1;
+ break;
+ default:
+ exit(1);
+ }
+
+ if (argc - optind < 2)
+ {
+ fprintf(stderr, "Usage: %s [-r] [-t time] mailfile program [args]...\n",
+ argv[0]);
+ exit(1);
+ }
+
+ if ((argvec=malloc(sizeof(char *) * (argc - optind + 1))) == NULL)
+ {
+ perror("malloc");
+ exit (1);
+ }
+
+ for (n=optind+1; n<argc; n++)
+ argvec[n-optind - 1]=argv[n];
+
+ argvec[n-optind-1]=NULL;
+
+ /* Create the mail file, if it doesn't exist */
+
+ if ((n=open(argv[optind], O_RDWR|O_CREAT, 0600)) >= 0)
+ close(n);
+
+ signal(SIGUSR2, SIG_IGN);
+ signal(SIGCHLD, SIG_DFL);
+
+ p=NULL;
+
+ for (n=0; n<ntries; sleep(DELAY), n++)
+ {
+ if (p)
+ ll_mail_free(p);
+
+ if ((p=ll_mail_alloc(argv[optind])) == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ signal(SIGHUP, (RETSIGTYPE (*)(int))catch);
+ signal(SIGTERM, (RETSIGTYPE (*)(int))catch);
+ signal(SIGINT, (RETSIGTYPE (*)(int))catch);
+
+ if (ll_mail_lock(p) < 0)
+ {
+ if (errno == ENOENT)
+ break; /* Mail file gone? */
+ if (caught())
+ break;
+ continue;
+ }
+
+ if ((fd=ll_mail_open(p)) < 0)
+ {
+ if (!readonly || (fd=ll_mail_open_ro(p)) < 0)
+ {
+ if (caught())
+ break;
+ continue;
+ }
+ }
+
+ if ((pid=fork()) < 0)
+ {
+ perror("fork");
+ exit(1);
+ }
+
+ if (pid == 0)
+ {
+ setgid(getgid());
+ setuid(getuid());
+
+ (void)caught();
+ execvp(argvec[0], argvec);
+
+ perror(argvec[0]);
+ exit(1);
+ }
+
+ while ((pid2=wait(&waitstat)) != pid)
+ ;
+
+ ll_mail_free(p);
+
+ if (WIFEXITED(waitstat))
+ exit(WEXITSTATUS(waitstat));
+ exit(EX_TEMPFAIL);
+ }
+ if (p)
+ ll_mail_free(p);
+ exit(EX_TEMPFAIL);
+ return (0);
+}
+
diff --git a/liblock/lockmail.sgml b/liblock/lockmail.sgml
new file mode 100644
index 0000000..11d7ed7
--- /dev/null
+++ b/liblock/lockmail.sgml
@@ -0,0 +1,204 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<!-- Copyright 2002-2007 Double Precision, Inc. See COPYING for -->
+<!-- distribution information. -->
+<refentry>
+ <info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info>
+
+ <refmeta>
+ <refentrytitle>lockmail</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class='manual'>Double Precision, Inc.</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>lockmail</refname>
+ <refpurpose>create mail lock files</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command moreinfo="none">lockmail</command>
+ <arg choice="opt" rep="norepeat">-r</arg>
+ <arg choice="opt" rep="norepeat">-t <replaceable>timeout</replaceable></arg>
+ <arg choice="req" rep="norepeat"><replaceable>lockfile</replaceable></arg>
+ <arg choice="req" rep="norepeat"><replaceable>program</replaceable></arg>
+ <arg rep="repeat" choice="opt">argument</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+<command moreinfo="none">lockmail</command> is a helper utility for working with mailbox
+files.
+Mailbox files must be locked to prevent other applications from modifying the
+mailbox at the same time.
+Different system use different locking conventions.
+<command moreinfo="none">lockmail</command> uses two of the most common locking mechanisms
+in use, which should work reliably on most systems.</para>
+
+ <para>
+<replaceable>lockfile</replaceable> is the pathname to an existing mailbox
+file.
+By default, <command moreinfo="none">lockmail</command> tries to lock the mailbox every
+five seconds (if the mailbox is already locked), and will give up after
+three minutes.
+After the mailbox is successfully locked, <command moreinfo="none">lockmail</command> runs
+<replaceable>program</replaceable> as a child process, with any optional
+<replaceable>argument</replaceable>s.
+When <replaceable>program</replaceable> terminates, <command moreinfo="none">lockmail</command>
+removes the mailbox lock, and terminates itself.</para>
+ </refsect1>
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-r</term>
+ <listitem>
+ <para>
+If a regular lock fails, try a read-only lock.
+Use this option to lock mailbox files in a read-only directory.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-t <replaceable>timeout</replaceable></term>
+ <listitem>
+ <para>
+If the lock attempt fails, try again for up to
+<replaceable>timeout</replaceable> seconds.
+The actual timeout is rounded up to the next five second interval
+(a lock attempt is tried every five seconds).</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para>
+This section briefly describes the locking mechanism used by
+<command moreinfo="none">lockmail</command>.
+<command moreinfo="none">lockmail</command> uses three different locking conventions in
+order to maximize compatibility with other mail software:
+C-Client folder locks, dot-locks, and file locks.
+</para>
+
+ <refsect2>
+ <title>C-Client folder locks</title>
+ <para>
+Mail software based on the <literal moreinfo="none">C-Client</literal> library creates
+lock files named
+<filename moreinfo="none">/tmp/.<replaceable>dddddd</replaceable>.<replaceable>iiiiii</replaceable></filename>.
+Here, <replaceable>dddddd</replaceable> and <replaceable>iiiiii</replaceable>
+are the device number and the inode number of the mailbox file
+(the <structfield>st_dev</structfield> and <structfield>st_ino</structfield>
+fields in the inode), in hexadecimal.
+If the process ID saved in the C-Client folder lock file is not valid,
+<command moreinfo="none">lockmail</command> concludes that it's a stale lock file, and
+will remove it.</para>
+
+ <note>
+ <para>
+A race condition exists where a <literal moreinfo="none">C-Client</literal> process is
+killed after it creates a lock file, but before saving its process ID in the
+lock file.
+The race window is very small, but it exists.
+The <literal moreinfo="none">C-Client</literal> library does not appear to ever clear out
+the lock file.</para>
+
+ <para>
+<command moreinfo="none">lockmail</command>
+attempts to resolve this race condition by deleting zero-length lock files
+that are at least five minutes old.</para>
+ </note>
+ </refsect2>
+
+ <refsect2>
+ <title>dot-locks</title>
+ <para>
+<command moreinfo="none">lockmail</command>
+also creates, and honors dot-lock files.
+Dot-lock files are first created as temporary files, then linked to
+<filename moreinfo="none"><replaceable>lockfile</replaceable>.lock</filename>.
+The link operation fails if the dot-lock file already exists.
+<command moreinfo="none">lockmail</command>
+uses an enhanced method of dot-locking, where its process ID, and the name
+of the server where <command moreinfo="none">lockmail</command> is running is also saved
+in its dot-lock file.
+If the operation fails due to an existing dot-lock file that was created
+by another <command moreinfo="none">lockmail</command> process on the same server, and the
+process ID no longer exists, this stale dot-lock file is removed immediately.
+In all other situations a dot-lock file older than five minutes is considered
+stale, and removed.
+</para>
+
+ <note>
+ <para>
+A failure to create a dot-lock file is silently ignored if the reason for
+the failure is because
+<command moreinfo="none">lockmail</command>
+does not have the write permission in the dot-lock file's directory.
+The incoming mail spool directory (usually
+<filename moreinfo="none">/var/mail</filename>)
+typically does not have global write permissions, so the attempt to
+create the dot-lock file in the spool directory will fail, and
+<command moreinfo="none">lockmail</command>
+will be content with using file-locking only.
+</para>
+ </note>
+ </refsect2>
+ <refsect2>
+ <title>File locks</title>
+
+ <para>
+The final locking mechanism
+<command moreinfo="none">lockmail</command>
+uses is the operating system's file locking facility.
+If
+<command moreinfo="none">lockmail</command>
+fails to obtain all three locks,
+<command moreinfo="none">lockmail</command>
+will sleep for five seconds and try again.
+The only exception is a failure to create a dot-lock because of no write
+access to the dot-lock file's directory, which is ignored.
+If
+<command moreinfo="none">lockmail</command>
+still fails to obtain all required locks in the amount of time specified
+by the <option>-t</option> option (or its default value),
+<command moreinfo="none">lockmail</command> will terminate with the
+<literal moreinfo="none">EX_TEMPFAIL</literal> exit code.
+</para>
+
+ <para>
+<command moreinfo="none">lockmail</command>
+runs <replaceable>program</replaceable> after obtaining the last file
+lock, waits until <replaceable>program</replaceable> terminates, and
+releases all locks.
+<replaceable>program</replaceable> must terminate before any of the locks
+obtained by <command moreinfo="none">lockmail</command> expire, and are considered stale.
+<command moreinfo="none">lockmail</command> will then terminate with the same exit code
+as <replaceable>program</replaceable>.</para>
+ </refsect2>
+ </refsect1>
+ <refsect1>
+ <title>EXIT STATUS</title>
+
+ <para>
+<command moreinfo="none">lockmail</command> terminates with the same exit status as
+<replaceable>program</replaceable>
+<command moreinfo="none">lockmail</command> terminates with the <literal moreinfo="none">EX_TEMPFAIL</literal>
+exit status if it was unable to obtain a lock, or if
+<replaceable>program</replaceable>
+was killed by a signal.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>
+<ulink url="maildrop.html"><citerefentry><refentrytitle>maildrop</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>,
+<citerefentry><refentrytitle>sendmail</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+</para>
+ </refsect1>
+</refentry>
diff --git a/liblock/locktest.c b/liblock/locktest.c
new file mode 100644
index 0000000..596d28a
--- /dev/null
+++ b/liblock/locktest.c
@@ -0,0 +1,84 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+/* $Id */
+
+#include "liblock.h"
+#if USE_FCNTL
+#include "lockfcntl.c"
+#endif
+#if USE_FLOCK
+#include "lockflock.c"
+#endif
+#if USE_LOCKF
+#include "locklockf.c"
+#endif
+#include <signal.h>
+#include <stdlib.h>
+
+int main()
+{
+int fd[2];
+pid_t p;
+int s;
+int f;
+
+ signal(SIGCHLD, SIG_DFL);
+ if (pipe(fd))
+ {
+ perror("pipe");
+ return (1);
+ }
+
+ if ((p=fork()) == (pid_t)-1)
+ {
+ perror("fork");
+ return (1);
+ }
+
+ if (p == 0)
+ {
+ char c;
+
+ close(fd[1]);
+ read(fd[0], &c, 1);
+ close(fd[0]);
+
+ if ((f=open("conftest.lock", O_RDWR|O_CREAT, 0644)) < 0)
+ {
+ perror("open");
+ exit(1);
+ }
+ alarm(5);
+
+ if (ll_lockfd(f, ll_writelock, 0, 0))
+ {
+ close(f);
+ exit(0);
+ }
+ close(f);
+ exit(1);
+ }
+
+ if ((f=open("conftest.lock", O_RDWR|O_CREAT, 0644)) < 0)
+ {
+ perror("open");
+ exit(1);
+ }
+
+ if (ll_lockfd(f, ll_writelock, 0, 0))
+ {
+ perror("lock");
+ close(f);
+ exit(1);
+ }
+ close(fd[1]);
+ close(fd[0]);
+ while (wait(&s) != p)
+ ;
+ if (s == 0)
+ exit(0);
+ exit(1);
+}
diff --git a/liblock/mail.c b/liblock/mail.c
new file mode 100644
index 0000000..82d380c
--- /dev/null
+++ b/liblock/mail.c
@@ -0,0 +1,503 @@
+/*
+** Copyright 2006 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#include "config.h"
+#include "liblock.h"
+#include "mail.h"
+#include "../numlib/numlib.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+
+struct ll_mail *ll_mail_alloc(const char *filename)
+{
+ struct ll_mail *p=(struct ll_mail *)malloc(sizeof(struct ll_mail));
+
+ if (!p)
+ return NULL;
+
+ if ((p->file=strdup(filename)) == NULL)
+ {
+ free(p);
+ return NULL;
+ }
+
+ p->cclientfd= -1;
+ p->cclientfile=NULL;
+
+ p->dotlock=NULL;
+
+ return p;
+}
+
+#define IDBUFSIZE 512
+
+/*
+** For extra credit, we mark our territory.
+*/
+
+static void getid(char *idbuf)
+{
+ libmail_str_pid_t(getpid(), idbuf);
+
+ while (*idbuf)
+ idbuf++;
+
+ *idbuf++=':';
+
+ idbuf[IDBUFSIZE-NUMBUFSIZE-10]=0;
+
+ if (gethostname(idbuf, IDBUFSIZE-NUMBUFSIZE-10) < 0)
+ strcpy(idbuf, "localhost");
+}
+
+static int writeid(char *idbuf, int fd)
+{
+ int l=strlen(idbuf);
+
+ while (l)
+ {
+ int n=write(fd, idbuf, l);
+
+ if (n <= 0)
+ return (-1);
+
+ l -= n;
+ idbuf += n;
+ }
+ return 0;
+}
+
+static int readid(char *p, int fd)
+{
+ int l=IDBUFSIZE-1;
+
+ while (l)
+ {
+ int n=read(fd, p, l);
+
+ if (n < 0)
+ return (-1);
+
+ if (n == 0)
+ break;
+
+ p += n;
+ l -= n;
+ }
+ *p=0;
+ return 0;
+}
+
+static pid_t getpidid(char *idbuf, char *myidbuf)
+{
+ pid_t p=atol(idbuf);
+
+ if ((idbuf=strchr(idbuf, ':')) == NULL ||
+ (myidbuf=strchr(myidbuf, ':')) == NULL ||
+ strcmp(idbuf, myidbuf))
+ return 0;
+
+ return p;
+}
+
+int ll_mail_lock(struct ll_mail *p)
+{
+ struct stat stat_buf;
+ char idbuf[IDBUFSIZE];
+ char idbuf2[IDBUFSIZE];
+
+ char fn[NUMBUFSIZE*2 + 20];
+ char *f;
+ int fd;
+
+ getid(idbuf);
+
+ if (p->cclientfd >= 0)
+ return 0;
+
+ if (stat(p->file, &stat_buf) < 0)
+ return -1;
+
+ if (snprintf(fn, sizeof(fn), "/tmp/.%lx.%lx",
+ (unsigned long)stat_buf.st_dev,
+ (unsigned long)stat_buf.st_ino) < 0)
+ {
+ errno=ENOSPC;
+ return (-1);
+ }
+
+ if ((f=strdup(fn)) == NULL)
+ return (-1);
+
+ /* We do things a bit differently. First, try O_EXCL */
+
+ if ((fd=open(f, O_RDWR|O_CREAT|O_EXCL, 0644)) >= 0)
+ {
+ struct stat stat_buf2;
+
+ if (ll_lockfd(fd, ll_writelock, ll_whence_start, 0) < 0 ||
+ fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 ||
+ writeid(idbuf, fd) < 0)
+ {
+ /* This shouldn't happen */
+
+ close(fd);
+ free(f);
+ return (-1);
+ }
+
+ /* Rare race condition: */
+
+ if (fstat(fd, &stat_buf) < 0 ||
+ lstat(f, &stat_buf2) < 0 ||
+ stat_buf.st_dev != stat_buf2.st_dev ||
+ stat_buf.st_ino != stat_buf2.st_ino)
+ {
+ errno=EAGAIN;
+ close(fd);
+ free(f);
+ return (-1);
+ }
+
+ p->cclientfd=fd;
+ p->cclientfile=f;
+ return 0;
+ }
+
+ /*
+ ** An existing lockfile. See if it's tagged with another
+ ** pid on this server, which no longer exists.
+ */
+
+ if ((fd=open(f, O_RDONLY)) >= 0)
+ {
+ pid_t p=-1;
+
+ if (readid(idbuf2, fd) == 0 &&
+ (p=getpidid(idbuf2, idbuf)) != 0 &&
+ kill(p, 0) < 0 && errno == ESRCH)
+ {
+ errno=EAGAIN;
+ close(fd);
+ unlink(f); /* Don't try again right away */
+ free(f);
+ return (-1);
+ }
+
+ /* If we can't lock, someone must have it open, game over. */
+
+ if (p == getpid() /* It's us! */
+
+ || ll_lockfd(fd, ll_readlock, ll_whence_start, 0) < 0)
+ {
+ errno=EEXIST;
+ close(fd);
+ free(f);
+ return (-1);
+ }
+
+ close(fd);
+ }
+
+ /* Stale 0-length lockfiles are blown away after 5 mins */
+
+ if (lstat(f, &stat_buf) == 0 && stat_buf.st_size == 0 &&
+ stat_buf.st_mtime + 300 < time(NULL))
+ {
+ errno=EAGAIN;
+ unlink(f);
+ free(f);
+ return (-1);
+ }
+
+ errno=EAGAIN;
+ free(f);
+ return (-1);
+}
+
+/* Try to create a dot-lock */
+
+static int try_dotlock(const char *tmpfile,
+ const char *dotlock,
+ char *idbuf);
+
+static int try_mail_dotlock(const char *dotlock, char *idbuf)
+{
+ char timebuf[NUMBUFSIZE];
+ char pidbuf[NUMBUFSIZE];
+ char *tmpname;
+ int rc;
+
+ libmail_str_time_t(time(NULL), timebuf);
+ libmail_str_pid_t(getpid(), pidbuf);
+
+ tmpname=malloc(strlen(dotlock) + strlen(timebuf) + strlen(pidbuf) +
+ strlen(idbuf) + 10);
+
+ if (!tmpname)
+ return -1;
+
+ strcpy(tmpname, dotlock);
+ strcat(tmpname, ".");
+ strcat(tmpname, timebuf);
+ strcat(tmpname, ".");
+ strcat(tmpname, pidbuf);
+ strcat(tmpname, ".");
+ strcat(tmpname, strchr(idbuf, ':')+1);
+
+ rc=try_dotlock(tmpname, dotlock, idbuf);
+ free(tmpname);
+ return (rc);
+}
+
+static int try_dotlock(const char *tmpname,
+ const char *dotlock,
+ char *idbuf)
+{
+ struct stat stat_buf;
+
+ int fd;
+
+ fd=open(tmpname, O_RDWR | O_CREAT, 0644);
+
+ if (fd < 0)
+ return (-1);
+
+ if (writeid(idbuf, fd))
+ {
+ close(fd);
+ unlink(tmpname);
+ return (-1);
+ }
+ close(fd);
+
+ if (link(tmpname, dotlock) < 0 || stat(tmpname, &stat_buf) ||
+ stat_buf.st_nlink != 2)
+ {
+ if (errno != EEXIST)
+ errno=EIO;
+
+ unlink(tmpname);
+ return (-1);
+ }
+ unlink(tmpname);
+ return (0);
+}
+
+static void dotlock_exists(const char *dotlock, char *myidbuf,
+ int timeout)
+{
+ char idbuf[IDBUFSIZE];
+ struct stat stat_buf;
+ int fd;
+
+ if ((fd=open(dotlock, O_RDONLY)) >= 0)
+ {
+ pid_t p;
+
+ /*
+ ** Where the locking process is on the same server,
+ ** the decision is easy: does the process still exist,
+ ** or not?
+ */
+
+ if (readid(idbuf, fd) == 0 && (p=getpidid(idbuf, myidbuf)))
+ {
+ if (p == getpid() /* Possibly recycled PID */
+ || (kill(p, 0) < 0 && errno == ESRCH))
+ {
+ close(fd);
+ if (unlink(dotlock) == 0)
+ errno=EAGAIN;
+ else
+ errno=EEXIST;
+ return;
+ }
+ }
+ else if (timeout > 0 && fstat(fd, &stat_buf) >= 0 &&
+ stat_buf.st_mtime < time(NULL) - timeout)
+ {
+ close(fd);
+
+ if (unlink(dotlock) == 0)
+ errno=EAGAIN;
+ else
+ errno=EEXIST;
+ return;
+ }
+
+ close(fd);
+ }
+
+ errno=EEXIST;
+}
+
+static int ll_mail_open_do(struct ll_mail *p, int ro)
+{
+ char *dotlock;
+ char myidbuf[IDBUFSIZE];
+ int save_errno;
+ int fd;
+
+ getid(myidbuf);
+
+ if (p->dotlock) /* Already locked */
+ {
+ fd=open(p->file, ro ? O_RDONLY:O_RDWR);
+
+ if (fd >= 0 &&
+ (ll_lockfd(fd, ro ? ll_readlock:ll_writelock, 0, 0) < 0 ||
+ fcntl(fd, F_SETFD, FD_CLOEXEC) < 0))
+ {
+ close(fd);
+ fd= -1;
+ }
+ return fd;
+ }
+
+ if ((dotlock=malloc(strlen(p->file)+sizeof(".lock"))) == NULL)
+ return -1;
+
+ strcat(strcpy(dotlock, p->file), ".lock");
+
+ if (try_mail_dotlock(dotlock, myidbuf) == 0)
+ {
+ fd=open(p->file, ro ? O_RDONLY:O_RDWR);
+
+ if (fd >= 0 &&
+ (ll_lockfd(fd, ro ? ll_readlock:ll_writelock, 0, 0) ||
+ fcntl(fd, F_SETFD, FD_CLOEXEC) < 0))
+ {
+ close(fd);
+ fd= -1;
+ }
+
+ p->dotlock=dotlock;
+ return fd;
+ }
+
+ save_errno=errno;
+
+ /*
+ ** Last fallback: for EEXIST, a read-only lock should suffice
+ ** In all other instances, we'll fallback to read/write or read-only
+ ** flock as last resort.
+ */
+
+ if ((errno == EEXIST && ro) || errno == EPERM || errno == EACCES)
+ {
+ fd=open(p->file, ro ? O_RDONLY:O_RDWR);
+
+ if (fd >= 0)
+ {
+ if (ll_lockfd(fd, ro ? ll_readlock:ll_writelock,
+ 0, 0) == 0 &&
+ fcntl(fd, F_SETFD, FD_CLOEXEC) == 0)
+ {
+ free(dotlock);
+ return fd;
+ }
+ close(fd);
+ }
+ }
+
+ /*
+ ** If try_dotlock blew up for anything other than EEXIST, we don't
+ ** know what the deal is, so punt.
+ */
+
+ if (save_errno != EEXIST)
+ {
+ free(dotlock);
+ return (-1);
+ }
+
+ dotlock_exists(dotlock, myidbuf, 300);
+ free(dotlock);
+ return (-1);
+}
+
+int ll_mail_open_ro(struct ll_mail *p)
+{
+ return ll_mail_open_do(p, 1);
+}
+
+int ll_mail_open(struct ll_mail *p)
+{
+ return ll_mail_open_do(p, 0);
+}
+
+void ll_mail_free(struct ll_mail *p)
+{
+ char myid[IDBUFSIZE];
+ char idbuf[IDBUFSIZE];
+
+ getid(myid);
+
+ if (p->cclientfd >= 0)
+ {
+ if (lseek(p->cclientfd, 0L, SEEK_SET) == 0 &&
+ readid(idbuf, p->cclientfd) == 0 &&
+ strcmp(myid, idbuf) == 0)
+ {
+ if (ftruncate(p->cclientfd, 0) >= 0)
+ unlink(p->cclientfile);
+ }
+ close(p->cclientfd);
+ free(p->cclientfile);
+ }
+
+ if (p->dotlock)
+ {
+ int fd=open(p->dotlock, O_RDONLY);
+
+ if (fd >= 0)
+ {
+ if (readid(idbuf, fd) == 0 &&
+ strcmp(myid, idbuf) == 0)
+ {
+ close(fd);
+ unlink(p->dotlock);
+ free(p->dotlock);
+ free(p->file);
+ free(p);
+ return;
+ }
+ close(fd);
+ }
+
+ free(p->dotlock);
+ }
+ free(p->file);
+ free(p);
+}
+
+int ll_dotlock(const char *dotlock, const char *tmpfile,
+ int timeout)
+{
+ char myidbuf[IDBUFSIZE];
+
+ getid(myidbuf);
+
+ if (try_dotlock(tmpfile, dotlock, myidbuf))
+ {
+ if (errno == EEXIST)
+ dotlock_exists(dotlock, myidbuf, timeout);
+ return -1;
+ }
+ return 0;
+}
+
+
diff --git a/liblock/mail.h b/liblock/mail.h
new file mode 100644
index 0000000..8980a0b
--- /dev/null
+++ b/liblock/mail.h
@@ -0,0 +1,108 @@
+
+#ifndef liblockmail_h
+#define liblockmail_h
+
+/*
+** Copyright 2002 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /*
+ ** Functions for mbox mail locks
+ */
+
+struct ll_mail {
+ char *file; /* File being locked */
+
+ /* c-client type lock */
+
+ int cclientfd;
+ char *cclientfile;
+
+
+ /* dotlock */
+
+ char *dotlock;
+};
+
+struct ll_mail *ll_mail_alloc(const char *filename);
+
+/*
+** Create a c-client type lock. NOTE: c-clients will ping you with SIGUSR2,
+** which must be ignored for this implementation.
+** Returns: 0 - ok, < 0 - error.
+**
+** An error return from ll_mail_lock carries some additional context in
+** errno:
+**
+** errno == EAGAIN: potential race condition. The current lock holder MIGHT
+** have just terminated. The caller should sleep for AT LEAST 5 seconds, then
+** try again.
+**
+** errno == EEXIST: another process on this server DEFINITELY has the lock.
+**
+** Implementations might choose to wait and try again on EEXIST as well.
+*/
+
+int ll_mail_lock(struct ll_mail *);
+
+/*
+** Open the mail file, read/write (creating a dot-lock).
+** Returns: >= 0 - file descriptor, < 0 - error (if EPERM, try ll_open_ro).
+**
+** errno == EEXIST: another process appears to hold a dot-lock.
+**
+** errno == EAGAIN: We just blew away a stale dotlock, should try again
+** in at least five seconds. Should NOT get two EAGAIN's in a row.
+*/
+
+int ll_mail_open(struct ll_mail *);
+
+/*
+** Open in read-only mode.
+*/
+
+int ll_mail_open_ro(struct ll_mail *);
+
+/*
+** Release all locks, deallocate structure. NOTE: file descriptor from
+** ll_mail_open(_ro)? is NOT closed, it's your responsibility to do that.
+*/
+
+void ll_mail_free(struct ll_mail *);
+
+/*
+** As long as we have the logic done already, here's a generic dot-locking
+** function.
+**
+** dotlock - the actual filename of a dotlock file.
+** tmpfile - the filename of a temporary file to create first.
+** timeout - optional timeout.
+**
+** Return code: 0: dotlock is created. Just unlink(dotlock) when you're done.
+**
+** -1 error. Check errno:
+**
+** EEXIST - dotlock is locked
+**
+** EAGAIN - dotlock is stale (dotlock created on this machine, and the
+** process no longer exists, or dotlock created on another
+** machine, and timeout argument was > 0, and the dotlock's
+** timestamp was older than timeout seconds.
+**
+** E????? - something's broken.
+**
+*/
+
+int ll_dotlock(const char *dotlock, const char *tmpfile,
+ int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif