diff options
Diffstat (limited to 'liblock')
| -rw-r--r-- | liblock/.gitignore | 3 | ||||
| -rw-r--r-- | liblock/Makefile.am | 45 | ||||
| -rw-r--r-- | liblock/configure.in | 217 | ||||
| -rw-r--r-- | liblock/liblock.h | 108 | ||||
| -rw-r--r-- | liblock/lockdaemon.c | 374 | ||||
| -rw-r--r-- | liblock/lockfcntl.c | 35 | ||||
| -rw-r--r-- | liblock/lockflock.c | 35 | ||||
| -rw-r--r-- | liblock/locklockf.c | 45 | ||||
| -rw-r--r-- | liblock/lockmail.c | 186 | ||||
| -rw-r--r-- | liblock/lockmail.sgml | 204 | ||||
| -rw-r--r-- | liblock/locktest.c | 84 | ||||
| -rw-r--r-- | liblock/mail.c | 503 | ||||
| -rw-r--r-- | liblock/mail.h | 108 |
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 |
