/************************************************************************
 *	Whatever is needed for (un)locking files in various ways	*
 *									*
 *	Copyright (c) 1990-1996, S.R. van den Berg, The Netherlands	*
 *	#include "../README"						*
 ************************************************************************/
#ifdef RCS
static /*const*/char rcsid[]=
 "$Id: locking.c,v 1.4 1996/12/26 17:20:05 srb Exp $";
#endif
#include "config.h"

#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>

#include "cucipop.h"
#include "locking.h"

#ifndef USEfcntl_lock
#ifndef USElockf
#ifndef USEflock
#ifndef USEdot_lock
#error No locking mechanism enabled
#endif
#define fdlock(fd)	0
#define fdunlock()	0
#endif
#endif
#endif
#ifndef fdlock
static int fdlock(int fd);
static int fdunlock(void);
#endif

static char*lastlocked;
static const char lockext[]=".lock";

void unlockmbox(void)
{ fdunlock();
#ifdef USEdot_lock
  blocksignals();
  if(lastlocked)
     unlink(lastlocked),free(lastlocked),lastlocked=0;
#endif
}

int lockmbox(int fd,const char*mbox)
{ char*ll;
  if(fdlock(fd))
     return 0;
#ifdef USEdot_lock
  if(ll=malloc(strlen(mbox)+STRLEN(lockext)+1))
   { strcpy(ll,mbox);strcat(ll,lockext);
     for(;;)
      { blocksignals();
	switch(xcreat(ll,0644))
	 { case 0:lastlocked=ll;setsignals();
	      return 1;
	   default:setsignals();free(ll);
	      return 0;
	   case -1:setsignals();sleep(LOCKSLEEP);
	 }
      }
   }
  return 0;
#else
  return 1;
#endif
}
	/* if you've ever wondered what conditional compilation was good for */
#ifndef fdlock						/* watch closely :-) */
#ifdef USEflock
#ifndef SYS_FILE_H_MISSING
#include <sys/file.h>
#endif
#define REITflock	1
#else
#define REITflock	0
#endif /* USEflock */
static int oldfdlock= -1;			    /* the fd we locked last */
#ifndef NOfcntl_lock
static struct flock flck;		/* why can't it be a local variable? */
#define REITfcntl	1
#else
#define REITfcntl	0
#endif /* NOfcntl_lock */
#ifdef USElockf
static off_t oldlockoffset;
#define REITlockf	1
#else
#define REITlockf	0
#endif /* USElockf */

static int fdlock(int fd)
{ int ret;
#if REITfcntl+REITflock+REITlockf>1
  for(;;syslog(LOG_DEBUG,"reiterating kernel-lock"),sleep(LOCKSLEEP))
#endif
   {
#ifdef USElockf
     oldlockoffset=0;
#endif
#ifndef NOfcntl_lock
     flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_len=0;
#ifdef USElockf
     flck.l_start=oldlockoffset;
#else
     flck.l_start=tell(fd);
#endif
#endif
#ifndef NOfcntl_lock
     ret=fcntl(fd,F_SETLKW,&flck);
#ifdef USElockf
     if((ret|=lockf(fd,F_TLOCK,(off_t)0))&&(errno==EAGAIN||errno==EACCES||
      errno==EWOULDBLOCK))
ufcntl:
      { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck);
	continue;
      }
#ifdef USEflock
     if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES||
      errno==EWOULDBLOCK))
      { lockf(fd,F_ULOCK,(off_t)0);
	goto ufcntl;
      }
#endif /* USEflock */
#else /* USElockf */
#ifdef USEflock
     if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES||
      errno==EWOULDBLOCK))
      { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck);
	continue;
      }
#endif /* USEflock */
#endif /* USElockf */
#else /* NOfcntl_lock */
#ifdef USElockf
     ret=lockf(fd,F_LOCK,(off_t)0);
#ifdef USEflock
     if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES||
      errno==EWOULDBLOCK))
      { lockf(fd,F_ULOCK,(off_t)0);
	continue;
      }
#endif /* USEflock */
#else /* USElockf */
#ifdef USEflock
     ret=flock(fd,LOCK_EX);
#endif /* USEflock */
#endif /* USElockf */
#endif /* NOfcntl_lock */
     oldfdlock=fd;
     return ret;
   }
}

static int fdunlock(void)
{ int i;
  if(oldfdlock<0)
     return -1;
  i=0;
#ifdef USEflock
  i|=flock(oldfdlock,LOCK_UN);
#endif
#ifdef USElockf
  ;{ off_t curp=tell(oldfdlock);
     lseek(oldfdlock,oldlockoffset,SEEK_SET);
     i|=lockf(oldfdlock,F_ULOCK,(off_t)0);lseek(oldfdlock,curp,SEEK_SET);
   }
#endif
#ifndef NOfcntl_lock
  flck.l_type=F_UNLCK;i|=fcntl(oldfdlock,F_SETLK,&flck);
#endif
  oldfdlock= -1;
  return i;
}
#endif /* fdlock */
