Mercurial > hg > Applications > mh
diff zotnet/mts/lock.c @ 0:bce86c4163a3
Initial revision
author | kono |
---|---|
date | Mon, 18 Apr 2005 23:46:02 +0900 |
parents | |
children | a6481689f99c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zotnet/mts/lock.c Mon Apr 18 23:46:02 2005 +0900 @@ -0,0 +1,682 @@ +/* lock.c - universal locking routines */ +#ifndef lint +static char ident[] = "@(#)$Id$"; +#endif +/* compile-time priority: + * MAILLOCK or LIBLOCKFILE use if defined + * LOCKF use if defined + * FCNTL use if SYS5 defined and LOCKF not defined + * FLOCK use if BSD42 defined and LOCKF and SYS5 not defined + */ + +#ifdef MMDFONLY +#define LOCKONLY +#endif + +#include "../h/mh.h" +#include <stdio.h> +#ifndef LOCKONLY +#include "../h/strings.h" +#include "mts.h" +#else /* LOCKONLY */ +#include "strings.h" +#ifdef MMDFONLY +#include "mmdfonly.h" +#include "mts.h" +#else /* not MMDFONLY */ +#include "lockonly.h" +#endif /* not MMDFONLY */ +#endif /* LOCKONLY */ +#include <sys/types.h> +#include <sys/stat.h> +#ifdef SVR4 +#ifndef LOCKF +#define LOCKF +#endif +#include <unistd.h> +#endif /* SVR4 */ +#ifdef LOCKF +#include <sys/errno.h> +#include <sys/file.h> +#ifndef F_ULOCK +#ifdef UNISTD +#include <unistd.h> +#else /* UNISTD */ +#include <sys/fcntl.h> +#endif /* UNISTD */ +#endif +#endif /* LOCKF */ +#if defined(_AIX) || defined(AUX) +#include <sys/file.h> +#endif +#if defined(__386BSD__) || defined(BSD44) +#include <fcntl.h> +#endif +#ifdef _AIX +#include <sys/time.h> +#include <time.h> +#else +#ifdef BSD42 +#include <sys/time.h> +#else /* BSD42 */ +#include <time.h> +#endif /* BSD42 */ +#endif + +#ifdef SYS5 +#define u_short ushort +#define u_long ulong +#endif + + +#if defined(SYS5) && !defined(_AIX) +#define index strchr +#define rindex strrchr +#endif +#ifdef BSD42 +#define FLOCK /* LOCKF will override this, if defined */ +#endif + +#ifdef __CYGWIN32__ +#include <errno.h> +#endif +extern int errno; + +#ifdef LOCKONLY +#ifndef MMDFONLY +char *lockldir = "/usr/spool/locks"; +#endif /* not MMDFONLY */ +#endif /* LOCKONLY */ + +#ifdef MAILLOCK +/* Both "mts.h" and <maillock.h> defines MAILDIR */ +#undef MAILDIR +#include <maillock.h> +static int is_default_spool(); +#undef LIBLOCKFILE +#else /* MAILLOCK */ +#ifdef LIBLOCKFILE +#define LOCK_LIBLOCKFILE 1 +#define LOCK_B_LKOPEN 2 +char *lockname_from_fd(); +#endif +#endif /* MAILLOCK */ + +static int b_lkopen(), lockit(), f_lkopen(); +static lockname(), timerON(), timerOFF(); + +time_t time (); +char *mktemp (); + +/* */ + +int lkopen (file, access) +register char *file; +register int access; +{ + mts_init ("mts"); + switch (lockstyle) { + case LOK_UNIX: +#ifdef MAILLOCK + if (is_default_spool (file)) + return f_lkopen (file, access); + /* else fall */ +#else /* MAILLOCK */ +#if defined(FLOCK) || defined(LOCKF) || defined(FCNTL) || defined(LIBLOCKFILE) + return f_lkopen (file, access); +#endif +#endif /* MAILLOCK */ + + default: + return b_lkopen (file, access); + } +} + +/* */ + +static int b_lkopen (file, access) +register char *file; +register int access; +{ + register int i, + j; + time_t curtime; + char curlock[BUFSIZ], + tmplock[BUFSIZ]; + struct stat st; + + if (stat (file, &st) == NOTOK) + return NOTOK; + lockname (curlock, tmplock, file, (int) st.st_dev, (int) st.st_ino); + + for (i = 0;;) + switch (lockit (tmplock, curlock)) { + case OK: + if ((i = open (file, access)) == NOTOK) { + j = errno; + (void) unlink (curlock); + errno = j; + } +#ifdef LIBLOCKFILE + timerON (curlock, i, LOCK_B_LKOPEN); +#else + timerON (curlock, i); +#endif + return i; + + case NOTOK: + if (stat (curlock, &st) == NOTOK) { + if (i++ > 5) + return NOTOK; + sleep (5); + break; + } + + i = 0; + (void) time (&curtime); + if (curtime < st.st_ctime + 60L) + sleep (5); + else + (void) unlink (curlock); + break; + } +} + + +static int lockit (tmp, file) +register char *tmp, + *file; +{ + register int fd; + + if ((fd = creat (tmp, 0400)) == NOTOK) + return NOTOK; +#if defined(hpux) || defined(ncr) + write(fd, "MH lock\n",8); +#endif /* hpux */ + (void) close (fd); + + fd = link (tmp, file); + (void) unlink (tmp); + + return (fd != NOTOK ? OK : NOTOK); +} + +/* */ + +static lockname (curlock, tmplock, file, dev, ino) +register char *curlock, + *tmplock, + *file; +register int dev, + ino; +{ + register char *bp, + *cp; + + bp = curlock; + if ((cp = rindex (file, '/')) == NULL || *++cp == 0) + cp = file; + if (lockldir == NULL || *lockldir == 0) { + if (cp != file) { + (void) sprintf (bp, "%.*s", cp - file, file); + bp += strlen (bp); + } + } + else { + (void) sprintf (bp, "%s/", lockldir); + bp += strlen (bp); + } + + switch (lockstyle) { + case LOK_BELL: + default: + (void) sprintf (bp, "%s.lock", cp); + break; + + case LOK_MMDF: + (void) sprintf (bp, "LCK%05d.%05d", dev, ino); + break; + } + + if (tmplock) { + if ((cp = rindex (curlock, '/')) == NULL || *++cp == 0) + (void) strcpy (tmplock, ",LCK.XXXXXX"); + else + (void) sprintf (tmplock, "%.*s,LCK.XXXXXX", + cp - curlock, curlock); + (void) unlink (mktemp (tmplock)); + } +} + +/* */ + +#if defined(BSD42) || defined(SVR4) +#include <sys/file.h> +#if defined(SUN40) || defined(SVR4) +#include <sys/fcntl.h> +#endif +#else +#ifdef FCNTL +#include <fcntl.h> +#endif +#endif + +#ifdef MAILLOCK + +static int f_lkopen (file, access) +register char *file; +register int access; +{ + register int fd; + + if (maillock (getusr (), 5) == L_SUCCESS) { + if ((fd = open (file, access | O_NDELAY)) == NOTOK) + return NOTOK; + return fd; + } + + return NOTOK; +} + +#else /* not MAILLOCK */ +#ifdef LIBLOCKFILE +#include <lockfile.h> +#include <limits.h> + +static int f_lkopen (file, access) +register char *file; +register int access; +{ + int r, fd; + char mlockfile[PATH_MAX]; + + snprintf(mlockfile, PATH_MAX, "%s.lock", file); + r = lockfile_create(mlockfile, 5, 0); + if (r != 0) + return NOTOK; + fd = open(file, access | O_NDELAY); + if (fd == -1) + return NOTOK; + /* we have to set up a timer so we can touch the lockfile occasionally */ + timerON(mlockfile, fd, LOCK_LIBLOCKFILE); + /* NB: it's OK to pass mlockfile as timerON immediately copies it + * and we then use the copy in all lockfile_foo() operations. + */ + return fd; +} + +#else /* not LIBLOCKFILE */ +#if defined(FLOCK) || defined(LOCKF) || defined(FCNTL) + +static int f_lkopen (file, access) +register char *file; +register int access; +{ + register int fd, + i, + j; +#ifdef FCNTL + struct flock buf; +#endif /* FCNTL */ + + for (i = 0; i < 5; i++) { +#if defined(LOCKF) || defined(FCNTL) + j = access; + access &= ~O_APPEND; /* make sure we open at the beginning */ + if ((access & 03) == O_RDONLY) { + /* We MUST have write permission or lockf/fcntl() won't work */ + /* (Stupid eh?) */ + access &= ~O_RDONLY; + access |= O_RDWR; + } +#endif /* LOCKF || FCNTL */ + if ((fd = open (file, access | O_NDELAY)) == NOTOK) + return NOTOK; +#ifndef LOCKF +#ifndef FLOCK +#ifndef FCNTL + /* should be an error? */ +#else /* FCNTL */ + buf.l_type = F_WRLCK; + buf.l_whence = 0; + buf.l_start = 0; + buf.l_len = 0; + if (fcntl (fd, F_SETLK, &buf) != NOTOK) + return fd; +#endif +#else /* FLOCK */ + if (flock (fd, LOCK_EX | LOCK_NB) != NOTOK) + return fd; +#endif +#else /* LOCKF */ + if (lockf (fd, F_TLOCK, 0L) != NOTOK) { + /* see if we should be at the end */ + if (j & O_APPEND) +#ifdef SVR4 + lseek (fd, (off_t)0, SEEK_END); +#else + lseek (fd, (off_t)0, L_XTND); +#endif + return fd; + } + /* Fix errno - lockf screws it */ + if (errno == EACCES) + errno = EWOULDBLOCK; +#endif + j = errno; + (void) close (fd); + + sleep (5); + } + + (void) close (fd); + errno = j; + return NOTOK; +} +#endif /* FLOCK || LOCKF || FCNTL */ +#endif /* not LIBLOCKFILE */ +#endif /* not MAILLOCK */ + +/* */ + +/* ARGSUSED */ + +int lkclose (fd, file) +register int fd; +register char *file; +{ + char curlock[BUFSIZ]; + struct stat st; +#ifdef FCNTL + struct flock buf; +#endif + + if (fd == NOTOK) + return OK; + switch (lockstyle) { + case LOK_UNIX: +#ifndef MAILLOCK +#ifndef LIBLOCKFILE +#ifndef LOCKF +#ifndef FLOCK +#ifndef FCNTL + /* should be an error? */ +#else /* FCNTL */ + buf.l_type = F_UNLCK; + buf.l_whence = 0; + buf.l_start = 0; + buf.l_len = 0; + fcntl(fd, F_SETLK, &buf); + break; +#endif +#else /* FLOCK */ + flock (fd, LOCK_UN); + break; +#endif +#else /* LOCKF */ + lseek (fd, (off_t)0, L_SET); /* make sure we unlock the whole thing */ + lockf (fd, F_ULOCK, 0L); + break; +#endif +#else /* LIBLOCKFILE */ + { + char *n = lockname_from_fd(fd); + if (!n) /* shouldn't happen: would be program bug */ + return NOTOK; + lockfile_remove(n); + timerOFF(fd); + break; + } +#endif +#else /* MAILLOCK */ + if (is_default_spool (file)) { + mailunlock (); + break; + } + /* else fall */ +#endif + + default: + if (fstat (fd, &st) != NOTOK) { + lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino); + (void) unlink (curlock); + timerOFF (fd); + } + } + + return (close (fd)); +} + + +/* */ + +FILE *lkfopen (file, mode) +register char *file, + *mode; +{ + register int fd; + register FILE *fp; + + if ((fd = lkopen (file, strcmp (mode, "r") ? 2 : 0)) == NOTOK) + return NULL; + + if ((fp = fdopen (fd, mode)) == NULL) { + (void) close (fd); + return NULL; + } + + return fp; +} + + +/* ARGSUSED */ + +int lkfclose (fp, file) +register FILE *fp; +register char *file; +{ + char curlock[BUFSIZ]; + struct stat st; +#ifdef FCNTL + struct flock buf; +#endif + + if (fp == NULL) + return OK; + + switch (lockstyle) { + case LOK_UNIX: +#ifndef MAILLOCK +#ifndef LIBLOCKFILE +#ifndef LOCKF +#ifndef FLOCK +#ifndef FCNTL + /* should be an error? */ +#else /* FCNTL */ + buf.l_type = F_UNLCK; + buf.l_whence = 0; + buf.l_start = 0; + buf.l_len = 0; + fcntl(fileno(fp), F_SETLK, &buf); + break; +#endif +#else /* FLOCK */ + flock (fileno(fp), LOCK_UN); + break; +#endif +#else /* LOCKF */ + fseek (fp, 0L, 0); /* make sure we unlock the whole thing */ + lockf (fileno(fp), F_ULOCK, 0L); + break; +#endif +#else /* LIBLOCKFILE */ + { + char *n = lockname_from_fd(fileno(fp)); + if (!n) /* shouldn't happen */ + return NOTOK; + lockfile_remove(n); + timerOFF(fileno(fp)); + break; + } +#endif +#else /* MAILLOCK */ + if (is_default_spool (file)) { + mailunlock (); + break; + } + /* else fall */ +#endif + + default: + if (fstat (fileno (fp), &st) != NOTOK) { + lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino); + (void) unlink (curlock); +#ifdef LIBLOCKFILE + /* shouldn't we timerOFF(fileno(fp))? We do in lkclose() -- PMM */ +#else + timerOFF (fileno (fp)); +#endif + } + } + + return (fclose (fp)); +} + +/* */ + +#include <signal.h> + +#define NSECS ((unsigned) 20) + + +struct lock { +#ifdef LIBLOCKFILE + int l_locktype; +#endif + int l_fd; + char *l_lock; + struct lock *l_next; +}; +#define NULLP ((struct lock *) 0) + +static struct lock *l_top = NULLP; + +#ifdef MAILLOCK +static int is_default_spool (file) +register char *file; +{ + static char *default_spool = NULLCP; + + if (! default_spool) + default_spool = concat (MAILDIR, getusr (), NULLCP); + return strcmp(file, default_spool) == 0; +} + +#else /* MAILLOCK */ +#ifdef LIBLOCKFILE +/* simple routine to allow us to get the filename given an fd. + * Returns NULL if the fd isn't valid. + */ +char *lockname_from_fd(fd) +int fd; +{ + struct lock *pp; + + for (pp = l_top; pp; pp = pp -> l_next) + if (pp->l_fd == fd) + return pp->l_lock; + + return NULL; +} +#endif /* LIBLOCKFILE */ +#endif /* MAILLOCK */ + +/* ARGSUSED */ + +static TYPESIG alrmser (sig) +int sig; +{ + register int j; + register char *cp; + register struct lock *lp; + +#ifndef BSD42 + (void) signal (SIGALRM, alrmser); +#endif /* BSD42 */ + + for (lp = l_top; lp; lp = lp -> l_next) +#ifdef LIBLOCKFILE + if (lp->l_locktype == LOCK_LIBLOCKFILE) + lockfile_touch(lp->l_lock); + else +#endif + if (*(cp = lp -> l_lock) && (j = creat (cp, 0400)) != NOTOK) + (void) close (j); + + (void) alarm (NSECS); +} + +/* */ + +#ifdef LIBLOCKFILE +static timerON (lock, fd, ltype) +int ltype; +#else +static timerON (lock, fd) +#endif +char *lock; +int fd; +{ + register struct lock *lp; + + if ((lp = (struct lock *) malloc ((unsigned) (sizeof *lp))) == NULLP) + return; /* XXX */ + + lp -> l_fd = fd; +#ifdef LIBLOCKFILE + lp -> l_locktype = ltype; +#endif + if ((lp -> l_lock = malloc ((unsigned) (strlen (lock) + 1))) == NULLCP) { + free ((char *) lp); + return; /* XXX */ + } + (void) strcpy (lp -> l_lock, lock); + lp -> l_next = NULLP; + + if (l_top) + lp -> l_next = l_top; + else { + (void) signal (SIGALRM, alrmser);/* perhaps SIGT{STP,TIN,TOU} */ + (void) alarm (NSECS); + } + l_top = lp; +} + + +static timerOFF (fd) +int fd; +{ + register struct lock *pp, + *lp; + + (void) alarm (0); + + if (l_top) { + for (pp = lp = l_top; lp; pp = lp, lp = lp -> l_next) + if (lp -> l_fd == fd) + break; + if (lp) { + if (lp == l_top) + l_top = lp -> l_next; + else + pp -> l_next = lp -> l_next; + + free (lp -> l_lock); + free ((char *) lp); + } + } + + if (l_top) + (void) alarm (NSECS); +}