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