/* @(#) $Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/mh/zotnet/mts/lock.c,v 1.1.1.1 2018/04/24 16:12:56 root Exp $ (LBL) */
/* lock.c - universal locking routines */

#ifdef	MMDFONLY
#define	LOCKONLY
#endif	MMDFONLY

#include <stdio.h>
#ifndef	LOCKONLY
#include "../h/strings.h"
#include "mts.h"
#else	LOCKONLY
#include "strings.h"
#ifdef	MMDFONLY
#include "mmdfonly.h"
#include "mts.h"
#else	not MMDFONLY
#include "lockonly.h"
#endif	not MMDFONLY
#endif	LOCKONLY
#include <sys/types.h>
#include <sys/stat.h>
#ifdef SUNOS4
#include <sys/unistd.h>
#endif


#define	NOTOK	(-1)
#define	OK	0

#define	NULLCP	((char *) 0)

#ifdef	SYS5
#define	index	strchr
#define	rindex	strrchr
#endif	SYS5

#ifndef SIGDECL
#ifdef SUNOS4
#define SIGDECL void
#else
#define SIGDECL int
#endif
#endif

extern int  errno;

#ifdef	LOCKONLY
#ifndef	MMDFONLY
char   *lockldir = "/usr/spool/locks";
#endif	not MMDFONLY
#endif	LOCKONLY


long	time();

static int b_lkopen(), lockit(), f_lkopen();
static void lockname(), timerON(), timerOFF();

/*  */

lkopen(file, access)
	register char *file;
	register int access;
{
    mts_init ("mts");
    switch (lockstyle) {
	case LOK_UNIX:
#ifdef	BSD42
	    return f_lkopen (file, access);
#endif	BSD42

	default:
	    return b_lkopen (file, access);
	}
}

/*  */

static int
b_lkopen(file, access)
	register char *file;
	register int access;
{
    register int    i,
                    j;
    long    curtime;
    char    curlock[BUFSIZ],
            tmplock[BUFSIZ];
    struct stat st;

    if (stat (file, &st) == NOTOK)
	return NOTOK;
    lockname (curlock, tmplock, file, (int) st.st_dev, (int) st.st_ino);

    for (i = 0;;)
	switch (lockit (tmplock, curlock)) {
	    case OK:
		if ((i = open (file, access)) == NOTOK) {
		    j = errno;
		    (void) unlink (curlock);
		    errno = j;
		}
		timerON (curlock, i);
		return i;

	    case NOTOK:
		if (stat (curlock, &st) == NOTOK) {
		    if (i++ > 5)
			return NOTOK;
		    sleep (5);
		    break;
		}

		i = 0;
		(void) time (&curtime);
		if (curtime < st.st_ctime + 60L)
		    sleep (5);
		else
		    (void) unlink (curlock);
		break;
	}
}


static int
lockit(tmp, file)
	register char *tmp, *file;
{
    register int    fd;

    if ((fd = creat (tmp, 0400)) == NOTOK)
	return NOTOK;
    (void) close (fd);

    fd = link (tmp, file);
    (void) unlink (tmp);

    return (fd != NOTOK ? OK : NOTOK);
}

/*  */

static void
lockname(curlock, tmplock, file, dev, ino)
	register char *curlock, *tmplock, *file;
	register int dev, ino;
{
    register char  *bp,
                   *cp;

    bp = curlock;
    if ((cp = rindex (file, '/')) == NULL || *++cp == NULL)
	cp = file;
    if (lockldir == NULL || *lockldir == NULL) {
	if (cp != file) {
	    (void) sprintf (bp, "%.*s", cp - file, file);
	    bp += strlen (bp);
	}
    }
    else {
	(void) sprintf (bp, "%s/", lockldir);
	bp += strlen (bp);
    }

    switch (lockstyle) {
	case LOK_BELL:
	default:
	    (void) sprintf (bp, "%s.lock", cp);
	    break;

	case LOK_MMDF:
	    (void) sprintf (bp, "LCK%05d.%05d", dev, ino);
	    break;
    }

    if (tmplock) {
	if ((cp = rindex (curlock, '/')) == NULL || *++cp == NULL)
	    (void) strcpy (tmplock, ",LCK.XXXXXX");
	else
	    (void) sprintf (tmplock, "%.*s,LCK.XXXXXX",
		cp - curlock, curlock);
	(void) unlink (mktemp (tmplock));
    }
}

/*  */

#ifdef	BSD42

#include <sys/file.h>
#ifndef O_NDELAY
#include <fcntl.h>
#endif

static int
f_lkopen(file, access)
	register char *file;
	register int access;
{
    register int    fd,
                    i,
		    j;

#ifdef sun
    /* Must force some kind of write access or else lockf() fails */
    if (access == 0)
	access = O_RDWR;
#endif
    for (i = 0; i < 5; i++) {
	if ((fd = open (file, access | O_NDELAY)) == NOTOK)
	    return NOTOK;
#ifndef sun
	if (flock (fd, LOCK_EX | LOCK_NB) != NOTOK)
#else
	if (lockf (fd, F_TLOCK, 0) != NOTOK)
#endif
	    return fd;
	j = errno;
	(void) close (fd);

	sleep (5);
    }

    (void) close (fd);
    errno = j;
    return NOTOK;
}
#endif	BSD42

/*  */

/* ARGSUSED */

lkclose(fd, file)
	register int fd;
	register char *file;
{
    char    curlock[BUFSIZ];
    struct stat st;

    if (fd == NOTOK)
	return OK;
    switch (lockstyle) {
	case LOK_UNIX:
#ifdef	BSD42
	    break;
#endif	BSD42

	default:
	    if (fstat (fd, &st) != NOTOK) {
		lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
		(void) unlink (curlock);
		timerOFF (fd);
	    }
    }

    return (close (fd));
}


/*  */

FILE *
lkfopen(file, mode)
	register char *file, *mode;
{
    register int    fd;
    register FILE  *fp;

    if ((fd = lkopen (file, strcmp (mode, "r") ? 2 : 0)) == NOTOK)
	return NULL;

    if ((fp = fdopen (fd, mode)) == NULL) {
	(void) close (fd);
	return NULL;
    }

    return fp;
}


/* ARGSUSED */

lkfclose(fp, file)
	register FILE *fp;
	register char *file;
{
    char    curlock[BUFSIZ];
    struct stat st;

    if (fp == NULL)
	return OK;

    switch (lockstyle) {
	case LOK_UNIX:
#ifdef	BSD42
	    break;
#endif	BSD42

	default:
	    if (fstat (fileno (fp), &st) != NOTOK) {
		lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
		(void) unlink (curlock);
	    }
    }

    return (fclose (fp));
}

/*  */

#include <signal.h>

#define	NSECS	((unsigned) 20)


struct lock {
    int		 l_fd;
    char	*l_lock;
    struct lock *l_next;
};
#define	NULLP	((struct lock *) 0)

static struct lock *l_top = NULLP;


/* ARGSUSED */

static SIGDECL
alrmser(sig)
	int sig;
{
    register int    j;
    register char  *cp;
    register struct lock   *lp;

#ifndef	BSD42
    (void) signal (SIGALRM, alrmser);
#endif	BSD42

    for (lp = l_top; lp; lp = lp -> l_next)
	if (*(cp = lp -> l_lock) && (j = creat (cp, 0400)) != NOTOK)
	    (void) close (j);

    (void) alarm (NSECS);
}

/*  */

static void
timerON(lock, fd)
	char *lock;
	int fd;
{
    register struct lock   *lp;

    if ((lp = (struct lock *) malloc ((unsigned) (sizeof *lp))) == NULLP)
	return;			/* XXX */

    lp -> l_fd = fd;
    if ((lp -> l_lock = malloc ((unsigned) (strlen (lock) + 1))) == NULLCP) {
	free ((char *) lp);
	return;			/* XXX */
    }
    (void) strcpy (lp -> l_lock, lock);
    lp -> l_next = NULLP;

    if (l_top)
	lp -> l_next = l_top -> l_next;
    else {
	(void) signal (SIGALRM, alrmser);/* perhaps SIGT{STP,TIN,TOU} */
	(void) alarm (NSECS);
    }
    l_top = lp;
}


static void
timerOFF(fd)
	int fd;
{
    register struct lock   *pp,
                           *lp;

    (void) alarm (0);

    if (l_top) {
	for (pp = lp = l_top; lp; pp = lp, lp = lp -> l_next)
	    if (lp -> l_fd == fd)
		break;
	if (lp) {
	    if (lp == l_top)
		l_top = lp -> l_next;
	    else
		pp -> l_next = lp -> l_next;

	    free (lp -> l_lock);
	    free ((char *) lp);
	}
    }

    if (l_top)
	(void) alarm (NSECS);
}
