/*
 * W-NEWS       A simple NEWS processing package.
 *		lock.c - Interface to the various types of locking.
 *
 * Version:     @(#)lock.c     4.50    03-Feb-1995
 *
 * Author:	Edvard Tuinder, <ed@ow.org>
 *		Miquel van Smoorenburg, <miquels@drinkel.ow.org>
 *              Copyright 1993 MicroWalt Corporation
 *		Copyright 1994,1995 Cistron Software C.V.
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 */
#include "libwnews.h"


#if HAVE_LOCK


#ifdef DEBUG
extern char debug;
#endif


#if !HAVE_FILE_LOCK


_PRO( static int check_fd, (int fd)					);


/* Local functions */
static int
check_fd(fd)
int fd;
{
#if HAVE_GETDTABLESIZE
  if (fd == -1 || fd > getdtablesize()) {
	logmsg(LOGERR, "invalid file descriptor: %d\n", fd);
	return(ERROR);
  }
#else
  if (fd == -1) {
	logmsg(LOGERR, "invalid file descriptor: %d\n", fd);
	return(ERROR);
  }
  return(OK);
#endif
}
#endif


#if HAVE_FLOCK

#include <sys/file.h>


int
wlock(path, fd)
char *path;
int fd;
{
  int try = 0;

  /* FIXME: should this be -EINVAL? -FvK */
  if ((check_fd(fd)) == ERROR) return(EINVAL);

  while (try < MAXTRY) {
	if ((flock(fd, LOCK_EX)) == ERROR) try++;
	  else break;

	if (try == MAXTRY) {
		logmsg(LOGERR|LOGSYS,
			"can create exclusive flock() lock on `%s'\n", path);
		return(ERROR);
	}
	(void) sleep(SLEEP_TIME);
  }
#ifdef DEBUG
  if (debug) fprintf(stderr, "created flock() lock on fd %d (%s)\n", fd, path);
#endif

  return(OK);
}


void
wunlock(path, fd)
char *path;
int fd;
{
  if ((flock(fd, LOCK_UN)) == ERROR) 
	logmsg(LOGERR, "trying to remove non existing flock lock\n");

#ifdef DEBUG
  if (debug) fprintf(stderr, "removed flock() lock on fd %d\n", fd);
#endif
}


#elif HAVE_FCNTL

#include <fcntl.h>


static struct flock fl;		/* Locking structure */


int
wlock(path, fd)
char *path;
int fd;
{
  int try = 0;

  fl.l_type = F_WRLCK;
  fl.l_whence = SEEK_SET;
  fl.l_start = 0;
  fl.l_len = 0;

#ifdef DEBUG
  if (debug) fprintf(stderr, "want fcntl() lock of size %d on fd %d (%s)\n",
							fl.l_len, fd, path);
#endif

  if ((check_fd(fd)) == ERROR) return(EINVAL);	/* FIXME: -EINVAL ? */

  while (try < MAXTRY) {
	if ((fcntl(fd, F_SETLK, &fl)) == ERROR) try++;
	  else break;

	if (try == MAXTRY) {
		perror ("fcntl");
		logmsg(LOGERR|LOGSYS,
			"can't create exclusive fcntl() lock on fd %d (%s)\n",
								fd, path);
		return(ERROR);
	}
	(void) sleep(SLEEP_TIME);
  }

#ifdef DEBUG
  if (debug) fprintf(stderr, "created fcntl() lock of size %d on fd %d (%s)\n",
							 fl.l_len, fd, path);
#endif

  return(OK);
}


void
wunlock(path, fd)
char *path;
int fd;
{
  extern int errno;

  fl.l_type = F_UNLCK;

  if ((check_fd(fd)) == ERROR) return /* EINVAL FIXME: MvS */;

  errno = 0;
  if ((fcntl(fd, F_SETLK, &fl)) == ERROR) {
	perror ("fcntl(unlock)");
	logmsg(LOGERR, "trying to remove non existing fcntl lock\n");
  } else {
#ifdef DEBUG
    if (debug) fprintf(stderr, "removed fcntl() lock of size %d on fd %d\n",
							fl.l_len , fd);
#endif
  }
}


#elif HAVE_LOCKF

#include <unistd.h>


/*
 * If OS doesn't conform tot SYS-V ideas of include files.
 * Like linux ;-(
 */
#ifndef F_ULOCK
#   define F_ULOCK	0
#   define F_LOCK	1
#   define F_TLOCK	2
#   define F_TEST	3
#endif


int
wlock(path, fd)
char *path;
int fd;
{
  int try = 0;

  if ((check_fd(fd)) == ERROR) return(EINVAL);

  while (try < MAXTRY) {
	if ((lockf(fd, F_LOCK, 0)) == ERROR) try++;
	  else break;

	if (try == MAXTRY) {
		logmsg(LOGERR|LOGSYS,
			"can't create exclusive lockf() lock on `%s'\n", path);
		return(ERROR);
	}
	(void) sleep(SLEEP_TIME);
  }

#ifdef DEBUG
  if (debug) fprintf(stderr, "created lockf() lock on fd %d (%s)\n", fd, path);
#endif

  return(OK);
}


void
wunlock(path, fd)
char *path;
int fd;
{
  if ((lockf(fd, F_ULOCK, 0)) == ERROR)
	logmsg(LOGERR, "trying to remove non existing lockf lock\n");

#ifdef DEBUG
  if (debug) fprintf (stderr, "removed lockf() lock on fd %d\n", fd);
#endif
}


#elif HAVE_FILE_LOCK


int
wlock(path, fd)
char *path;
int fd;
{
  char buff[256];	/* FIXME: PATH_MAX -FvK */
  int try = 0;

#ifdef COHERENT
  fd = 0;
#endif

  if (path == (char *)NULL) {
	logmsg(LOGERR, "empty file received in file_lock()\n");
	return(ERROR);
  }
  sprintf (buff, "%s.lck", path);

  while (try < MAXTRY) {
	if (open(buff, O_CREAT|O_WRONLY|O_EXCL, 0) == ERROR) try++;
	  else break;

	if (try == MAXTRY) {
		logmsg(LOGERR|LOGSYS,
			"can't creat file lock for `%s'\n", path);
		return(ERROR);
	}
	(void) sleep(SLEEP_TIME);
  }
  return(OK);
}


void
wunlock(path, fd)
char *path;
int fd;
{
  char buff[256];	/* FIXME: PATH_MAX -FvK */

#ifdef COHERENT
  fd = 0;
#endif

  if (path == (char *)NULL) {
	logmsg(LOGERR, "empty file received in unlock()\n");
	return;
  }
  sprintf (buff, "%s.lck", path);

  if ((unlink(buff)) == ERROR)
	logmsg(LOGERR,
		"[non-fatal] tried to remove non existing lock for `%s'\n",
									path);
}


#endif /* TYPES OF LOCKING */


#endif /* HAVE_LOCK */
