[Raw Msg Headers][Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

V7 mailbox locking for mailbox



Here is some code to use V7 mailbox locking.  We needed this because
we wanted zmailer to work correctly with vendor-supplied MUAs.

To use it, define V7LOCK= in your hostenv/* files.


*** 1.8	1993/02/21 23:10:10
--- mailbox.c	1993/03/03 19:39:59
***************
*** 205,210 ****
--- 205,212 ----
  #define	MAXPATHLEN 1024
  #endif
  
+ char myhostname[MAXHOSTNAMELEN+1];
+ 
  int
  main(argc, argv)
  	int argc;
***************
*** 285,290 ****
--- 287,294 ----
  		maildirs[1] = NULL;
  	}
  
+ 	(void) getmyhostname(myhostname, sizeof myhostname);
+ 
  	if (logfile != NULL) {
  		if ((fd = open(logfile, O_CREAT|O_APPEND|O_WRONLY, 0644)) < 0)
  			(void) fprintf(stderr,
***************
*** 549,567 ****
  	}
  	if ((st.st_mode & S_IFMT) != S_IFREG)
  		/* don't lock non-files */;
! 	else
! #ifdef	USE_NFSMBOX
! 	if (lock(file) != 0)
! #else	/* !USE_NFSMBOX */
  #ifdef	USE_LOCKF
! 	if (lockf(fdmail, F_LOCK, 0) < 0)
  #else	/* !USE_LOCKF */
! 	if (flock(fdmail, LOCK_EX) < 0)
  #endif	/* USE_LOCKF */
! #endif	/* USE_NFSMBOX */
! 	{
! 		DIAGNOSTIC(rp, EX_TEMPFAIL, "can't lock \"%s\"", file);
! 		return;
  	}
  #if	defined(BIFF) || defined(RBIFF)
  	if (nbp != NULL)
--- 553,573 ----
  	}
  	if ((st.st_mode & S_IFMT) != S_IFREG)
  		/* don't lock non-files */;
! 	else {
! 		if (lock(file) != 0)
! 		{
! 			DIAGNOSTIC(rp, EX_TEMPFAIL, "can't lock \"%s\"", file);
! 			return;
! 		}
  #ifdef	USE_LOCKF
! 		if (lockf(fdmail, F_LOCK, 0) < 0)
  #else	/* !USE_LOCKF */
! 		if (flock(fdmail, LOCK_EX) < 0)
  #endif	/* USE_LOCKF */
! 		{
! 			DIAGNOSTIC(rp, EX_TEMPFAIL, "can't lock \"%s\"", file);
! 			return;
! 		}
  	}
  #if	defined(BIFF) || defined(RBIFF)
  	if (nbp != NULL)
***************
*** 568,583 ****
  		nbp->offset = lseek(fdmail, 0L, L_XTND);
  #endif	/* BIFF || RBIFF */
  	fp = putmail(rp, fdmail, messagefd, timestring, file, dp);
! 	if ((st.st_mode & S_IFMT) == S_IFREG)
! #ifdef	USE_NFSMBOX
! 		(void) unlock(file);
! #else	/* !USE_NFSMBOX */
  #ifdef	USE_LOCKF
  		(void) lockf(fdmail, F_ULOCK, 0);/* XX: does this really work?*/
  #else	/* !USE_LOCKF */
  		(void) flock(fdmail, LOCK_UN);
  #endif	/* USE_LOCKF */
! #endif	/* USE_NFSMBOX */
  	setrootuid(rp);
  	if (fp != NULL) {
  		(void) fclose(fp);	/* this closes fdmail */
--- 574,587 ----
  		nbp->offset = lseek(fdmail, 0L, L_XTND);
  #endif	/* BIFF || RBIFF */
  	fp = putmail(rp, fdmail, messagefd, timestring, file, dp);
! 	if ((st.st_mode & S_IFMT) == S_IFREG) {
  #ifdef	USE_LOCKF
  		(void) lockf(fdmail, F_ULOCK, 0);/* XX: does this really work?*/
  #else	/* !USE_LOCKF */
  		(void) flock(fdmail, LOCK_UN);
  #endif	/* USE_LOCKF */
! 		(void) unlock(file);
! 	}
  	setrootuid(rp);
  	if (fp != NULL) {
  		(void) fclose(fp);	/* this closes fdmail */
*** 1.1	1993/03/03 18:36:24
--- lock.c	1993/03/11 21:58:51
***************
*** 2,5 ****
--- 2,259 ----
  
  #ifdef	USE_NFSMBOX
  #include "../../support/nfslock/nfslock.c"
+ #define GOT_LOCK
  #endif	/* USE_NFSMBOX */
+ 
+ #ifdef USE_V7LOCK
+ #define GOT_LOCK
+ 
+ /*
+  * V7 locking code adapted from the Deliver program,
+  * by Chip Salzenberg <chip@tct.com>
+  */
+ 
+ #include "hostenv.h"
+ #include <sys/stat.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include NDIR_H
+ 
+ extern int errno;
+ 
+ extern char *emalloc();
+ extern char myhostname[];
+ 
+ /*
+  * Local functions.
+  */
+ 
+ static char *dotlock_name();
+ 
+ /*
+  * Local data.
+  */
+ 
+ #define MIN_NAMESIZE 8		/* Minimum reasonable filename size limit. */
+ 
+ /*
+  * Lock a mailbox by name.
+  */
+ 
+ int
+ lock(name)
+ char *name;
+ {
+     char *dotlock;
+ 
+     if ((dotlock = dotlock_name(name)) == NULL
+ 	|| create_lockfile(dotlock) < 0)
+ 	return -1;
+ 
+     return 0;
+ }
+ 
+ /*
+  * Unlock a mailbox by name.
+  */
+ 
+ int
+ unlock(name)
+ char *name;
+ {
+     char *dotlock;
+ 
+     if ((dotlock = dotlock_name(name)) == NULL
+ 	|| remove_lockfile(dotlock) < 0)
+ 	return -1;
+ 
+     return 0;
+ }
+ 
+ /*
+  * Return the name of the appropriate ".lock" file for a mailbox.
+  */
+ 
+ static char *
+ dotlock_name(name)
+ char *name;
+ {
+     static char *lname = NULL;
+     static unsigned lsize = 0;
+     char *p;
+     char *basename();
+     unsigned n, maxlen;
+ 
+     n = strlen(name);
+     if (lsize < n + 8)
+     {
+ 	if (lname)
+ 	    free(lname);
+ 	lsize = n + 32;
+ 	lname = emalloc(lsize);
+     }
+ 
+     /* Figure maximum filename length in the given directory. */
+ 
+     maxlen = MAXNAMLEN;
+ 
+ 
+     /* We want as much of `basename.lock' as will fit in maxlen characters. */
+ 
+     (void) strcpy(lname, name);
+     p = basename(lname);
+     if ((n = strlen(p)) > (maxlen - 5))
+ 	n = maxlen - 5;
+     (void) strcpy(p + n, ".lock");
+ 
+     return lname;
+ }
+ 
+ /*
+  * Create a lockfile.
+  */
+ 
+ int
+ create_lockfile(name)
+ char *name;
+ {
+     static char *othername;
+     struct stat st1, st2;
+     int fd, tries, errno_save;
+     char *unique();
+ 
+     if (othername)
+ 	free(othername);
+     if ((othername = unique(name)) == NULL)
+ 	return -1;
+     if ((fd = creat(othername, 0)) == -1)
+ 	return -1;
+     (void) close(fd);
+ 
+     for (tries = 0; tries < 10; ++tries)
+     {
+ 	if (tries)
+ 	    sleep(3);
+ 
+ 	/*
+ 	 * KLUDGE ALERT
+ 	 * NFS can report failure on successful operations if those
+ 	 * operations are unrepeatable.  Such is link().  Therefore,
+ 	 * we ignore the error status and check identities.
+ 	 */
+ 
+ 	(void) link(othername, name);
+ 
+ 	if (stat(othername, &st1) == 0
+ 	 && stat(name, &st2) == 0
+ 	 && st1.st_nlink == 2
+ 	 && st2.st_nlink == 2
+ 	 && st1.st_dev == st2.st_dev
+ 	 && st1.st_ino == st2.st_ino)
+ 	{
+ 	    (void) unlink(othername);
+ 	    return 0;
+ 	}
+ 
+ 	/* We can't trust errno; so, assume the most benign error. */
+ 
+ 	errno_save = EEXIST;
+     }
+ 
+     unlink(othername);
+ 
+     errno = errno_save;
+     return -1;
+ }
+ 
+ /*
+  * Remove a lockfile.
+  */
+ 
+ int
+ remove_lockfile(name)
+ char *name;
+ {
+     if (unlink(name) == -1)
+ 	return -1;
+ 
+     return 0;
+ }
+ 
+ /*
+  * Return the last component of the given pathname.
+  */
+ 
+ char *
+ basename(name)
+ char *name;
+ {
+     char *b;
+ 
+     if ((b = strrchr(name, '/')) != NULL)
+ 	++b;
+     else
+ 	b = name;
+ 
+     return (b);
+ }
+ 
+ /*
+  * Return an allocated string containing the name of a (probably)
+  * unique temporary file in the same directory as the given file.
+  */
+ 
+ char *
+ unique(path)
+ char *path;
+ {
+     static int uhostlen = 6;
+     char *upath, *ubase;
+     char *basename();
+     unsigned sequence, pid4;
+     int i;
+ 
+     upath = emalloc(strlen(path) + 20);
+     (void) strcpy(upath, path);
+     ubase = basename(upath);
+ 
+     sequence = 0;
+     if (strlen(myhostname) > (unsigned)uhostlen)
+     {
+ 	/*
+ 	 * Use extra characters of hostname to randomize sequence.
+ 	 * (The magic "33" is brought to you courtesy of Chris Torek.)
+ 	 */
+ 
+ 	for (i = uhostlen; myhostname[i]; ++i)
+ 	    sequence = (sequence * 33) + myhostname[i];
+     }
+ 
+     pid4 = (unsigned)getpid() & (unsigned)0xFFFF;
+ 
+     for (i = 0; i < 32; ++i)
+     {
+ 	struct stat st;
+ 
+ 	++sequence;
+ 	(void) sprintf(ubase, "_%02X%04X.%.*s",
+ 		       sequence & 0xFF, pid4, uhostlen, myhostname);
+ 
+ 	if (stat(upath, &st) == -1)
+ 	{
+ 	    if (errno == ENOENT)
+ 		return upath;
+ 
+ 	    break;
+ 	}
+     }
+ 
+     free(upath);
+     return NULL;
+ }
+ #endif /* V7LOCK */
+ 
+ #ifndef GOT_LOCK
+ lock() { return 0; }
+ unlock() { return 0; }
+ #endif /* GOT_LOCK */