#include <sys/file.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

/*
 * Program:	flock emulation via fcntl() locking
 *
 * Author:	Mark Crispin
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: MRC@CAC.Washington.EDU
 *
 * Date:	10 April 2001
 * Last Edited:	6 November 2001
 * 
 * The IMAP toolkit provided in this Distribution is
 * Copyright 2001 University of Washington.
 * The full text of our legal notices is contained in the file called
 * CPYRIGHT, included with this Distribution.
 */
 
#undef flock			/* name is used as a struct for fcntl */
#undef fork			/* make damn sure that we don't use vfork!! */

#ifndef NSIG			/* don't know if this can happen */
#define NSIG 32			/* a common maximum */
#endif

/* Emulator for flock() call
 * Accepts: file descriptor
 *	    operation bitmask
 * Returns: 0 if successful, -1 if failure under BSD conditions
 */


int flocksim (int fd,int op)
{
  struct flock fl;
				/* lock applies to entire file */
  fl.l_whence = fl.l_start = fl.l_len = 0;
  //fl.l_pid = getpid ();		/* shouldn't be necessary */
  switch (op & ~LOCK_NB) {	/* translate to fcntl() operation */
  case LOCK_EX:			/* exclusive */
    fl.l_type = F_WRLCK;
    break;
  case LOCK_SH:			/* shared */
    fl.l_type = F_RDLCK;
    break;
  case LOCK_UN:			/* unlock */
    fl.l_type = F_UNLCK;
    break;
  default:			/* default */
    errno = EINVAL;
    return -1;
  }

				/* do the lock */
  while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
    if (errno != EINTR) {
      /* Can't use switch here because these error codes may resolve to the
       * same value on some systems.
       */
      if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
	fprintf (stderr,"Unexpected file locking failure: %s",strerror (errno));
				/* give the user a warning of what happened */
	if (op & LOCK_NB) return -1;
	sleep (5);		/* slow things down for loops */
      }
				/* return failure for non-blocking lock */
      else if (op & LOCK_NB) return -1;
    }
  return 0;			/* success */
}

#define flock flocksim

/*      Michael Jansen, Oct.,1990.
 *
 *	Here is a little interlude for testing of the BSD file
 *	locking system call flock().
 *
 *	File locks using flock() lock the whole file in shared
 *	exclusive mode.  Shared locks should be useful when
 *	there are many readers.  Exclusive locks handle the
 *	writers.
 *	
 *	Malcolm Cook, Oct., 1990.
 *
 *	Modifications:
 *		- unlock prior to calling for a lock to get around dodgy
 *		  implementation of flock() which causes a freeze if 'ex'
 *		  and 'sh' called in succession
 *		- remove LOCK_NB flag to suspend processes waiting for a lock
 *		- append lock request and pid to file to create a history of
 *		  requests. Note that gaining a lock does not prevent other
 *		  processes from reading/writing to the file, just from gaining
 *		  the lock.
 *
 *	Apr., 1994.
 *
 *		- above problem with freezing doesn't apply to ULTRIX, hence
 *		  initial unlocking commented out. This wasn't an entirely
 *		  satisfactory workaround anyway, since a process could lose
 *		  its lock whilst trying to downgrade from 'ex' to 'sh'.
 */

#include <sys/file.h>
#include <stdio.h>

#define	TRUE	1
#define	FALSE	0

int main ()
{
	char		command [80];
	char		token1 [80], token2 [80];
	char		fileName [80];
	int		showInstructions	= TRUE;
	int		fd = -1;
	int		pid;

	pid = getpid();

	for (;;)
	{
		if (showInstructions)
		{
			printf ("Use 'name fileName' to specify the file to operate on,\n\
	'ex' for an exclusive lock,\n\
	'sh' for a shared lock.\n\
	'un' to unlock\n\
	'q'  to quit\n"
			);

			showInstructions = FALSE;
		}

		printf ("Command? ");

		if (fgets (command, sizeof (command), stdin) == 0) break;
		sscanf (command, "%s%s", token1, token2);

		if (strcmp (token1, "name") == 0)
		{
			if (fd != -1)
				close (fd);
			strcpy (fileName, token2);
			if ((fd = open (fileName, O_RDWR | O_CREAT | O_APPEND, 0777)) == -1)
			{
				printf ("%s: cannot open or create\n", fileName);
				perror("sys");
				fileName [0] = 0;
			}
		}
		else if (strcmp (token1, "ex") == 0)
		{
			if (fd == -1)
			{
				printf ("you have no file open\n");
				showInstructions = TRUE;
			}
			else
			{
				/*flock(fd, LOCK_UN);*/
				if (flock (fd, LOCK_EX /*| LOCK_NB*/) != 0)
					perror ("flock ex");
				else
				{
/* 					fprintf(str,"%d ex\n",pid); */
/* 					fflush(str); */
				}
			}
		}
		else if (strcmp (token1, "sh") == 0)
		{
			if (fd == -1)
			{
				printf ("you have no file open\n");
				showInstructions = TRUE;
			}
			else
			{
				/*flock(fd, LOCK_UN);*/
				if (flock (fd, LOCK_SH /*| LOCK_NB*/) != 0)
					perror ("flock sh");
				else
				{
/* 					fprintf(str,"%d sh\n",pid); */
/* 					fflush(str); */
				}
			}
		}
		else if (strcmp (token1, "un") == 0)
		{
			if (fd == -1)
			{
				printf ("you have no file open\n");
				showInstructions = TRUE;
			}
			else
			{
				/* write 'un' before unlocking to reflect true 
				   sequence of events in unlock-lock sequence
				*/
/* 				fprintf(str,"%d un\n",pid); */
/* 				fflush(str); */
				if (flock (fd, LOCK_UN) != 0)
					perror ("flock un");
			}
		}
		else if (strcmp (token1, "q") == 0)
		{
			break;
		}
		else
		{
			printf ("%s: cannot understand command\n", token1);
			printf ("Please reconsider.\n\n");
			showInstructions = TRUE;
		}
	}

	return 0;
}

/*
 * Local Variables:
 * compile-command: "cc -Wall lock1-sim.c"
 * End:
 */
