
static char rcsid[] ="@(#)$Id: lock.c,v 4.1.1.3 90/06/21 23:51:34 syd Exp $";

/*******************************************************************************
 *  The Elm Mail System  -  $Revision: 4.1.1.3 $   $State: Exp $
 *
 * 			Copyright (c) 1986, 1987 Dave Taylor
 * 			Copyright (c) 1988, 1989, 1990 USENET Community Trust
 *******************************************************************************
 * Bug reports, patches, comments, suggestions should be sent to:
 *
 *	Syd Weinstein - elm@DSI.COM
 *			dsinc!elm
 *
 *******************************************************************************
 * $Log:	lock.c,v $
 * Revision 4.1.1.3  90/06/21  23:51:34  syd
 * Fix typo
 * 
 * Revision 4.1.1.2  90/06/21  23:02:37  syd
 * Make lock use same name as elm
 * From: Edwin WIles
 * 
 * Revision 4.1.1.1  90/06/05  20:28:53  syd
 * The open system call in actions.c for EMERGENCY_MAILBOX and EMER_MBOX
 * were tested with the inequality >= 0 exactly backwards.
 * If the user's system mail box (/usr/spool/mail/user_id) is
 * removed the attempt of filter to flock it fails.  If it does not exist then
 * it should create it and then lock it.
 * From: john@hopf.math.nwu.edu (John Franks)
 * 
 * Revision 4.1  90/04/28  22:41:57  syd
 * checkin of Elm 2.3 as of Release PL0
 * 
 *
 ******************************************************************************/


/** The lock() and unlock() routines herein duplicate exactly the
    equivalent routines in the Elm Mail System, and should also be
    compatible with sendmail, rmail, etc etc.

  
**/

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "defs.h"
#include "filter.h"

static  int  we_locked_it;
static  char lockfile[SLEN];

#ifdef	LOCK_BY_FLOCK
#include <sys/types.h>
#include <sys/file.h>
static	flock_fd = -1;
static	char flock_name[SLEN];
#endif

extern  int  errno;

char *
mk_lockname(home, user)
char *home, *user;
{
	/** Create the proper name of the lock file for file_to_lock.
	    Return lock_name for informational purposes.
	 **/

#ifdef XENIX
	/* lock is /tmp/[basename of file_to_lock].mlk */
	sprintf(lockfile, "/tmp/%.10s.mlk", user);
#else
	/* lock is [file_to_lock].lock */
	sprintf(lockfile, "%s%s.lock", home, user);
#endif
	return(lockfile);
}


int
lock()
{
	/** This routine will return 1 if we could lock the mailfile,
	    zero otherwise.
	**/

	int attempts = 0, ret;

#ifndef	LOCK_FLOCK_ONLY			/* { !LOCK_FLOCK_ONLY	*/
	mk_lockname(mailhome, username);
#ifdef PIDCHECK
	/** first, try to read the lock file, and if possible, check the pid.
	    If we can validate that the pid is no longer active, then remove
	    the lock file.
	**/
	if((ret=open(lockfile,O_RDONLY)) != -1) {
	  char pid_buffer[SHORT];
	  if (read(ret, pid_buffer, SHORT) > 0) {
	    attempts = atoi(pid_buffer);
	    if (attempts) {
	      if (kill(attempts, 0)) {
	        close(ret);
	        if (unlink(lockfile) != 0)
		  return(1);
	      }
	    }
	  }
	  attempts = 0;
        }
#endif

	while ((ret = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0444)) < 0 
	       && attempts++ < 10) {
	  sleep(3);	/* wait three seconds each pass, okay?? */
	}

	if (ret >= 0) {
	  we_locked_it++;
	  close(ret);			/* no need to keep it open! */
	  ret = 1;
	} else {
	  ret = 0;
	}
	  
#endif					/* } !LOCK_FLOCK_ONLY	*/
#ifdef	LOCK_BY_FLOCK			/* { LOCK_BY_FLOCK	*/
	(void)sprintf(flock_name,"%s%s",mailhome,username);
	flock_fd = open(flock_name, O_RDONLY | O_CREAT, 0600);
	if ( flock_fd >= 0 )
	  for (attempts = 0; attempts < 10; attempts++) {
	    if ( (ret = flock(flock_fd,LOCK_NB|LOCK_EX)) != -1 )
	        break;
	    if ( errno != EWOULDBLOCK && errno != EAGAIN )
	        break;
	    (void)sleep((unsigned)3);
	  }
	if ( flock_fd >= 0 && ret == 0 ) {
	    we_locked_it++;
	    ret = 1;
	} else {
	    we_locked_it = 0;
	    if ( lockfile[0] ) {
	    	(void)unlink(lockfile);
		lockfile[0] = 0;
	    }
	    if ( flock_fd >= 0 ) {
	    	(void)close(flock_fd);
	    	flock_fd = -1;
	    }
	    ret = 0;
	}
#endif
	return(ret);
}

unlock()
{
	/** this routine will remove the lock file, but only if we were
	    the people that locked it in the first place... **/

#ifndef	LOCK_FLOCK_ONLY
	if (we_locked_it && lockfile[0]) {
	  unlink(lockfile);	/* blamo! */
	  lockfile[0] = 0;
	}
#endif
#ifdef	LOCK_BY_FLOCK
	if (we_locked_it && flock_fd >= 0) {
	  (void)close(flock_fd);
	  flock_fd = -1;
	}
#endif
	we_locked_it = 0;
}
