view zotnet/mts/lock.c @ 17:76d91e545ea8 default tip

addrsbr and dtimep fix
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 24 Nov 2014 14:49:25 +0900
parents 07f8972434be
children
line wrap: on
line source

/* lock.c - universal locking routines */
#ifndef	lint
static char ident[] = "@(#)$Id: lock.c,v 1.2 2006/12/05 18:18:14 kono Exp $";
#endif
/* compile-time priority:
 *	MAILLOCK or LIBLOCKFILE	use if defined
 *	LOCKF	use if defined
 *	FCNTL	use if SYS5  defined and LOCKF not defined
 *	FLOCK	use if BSD42 defined and LOCKF and SYS5 not defined
 */

#ifdef	MMDFONLY
#define	LOCKONLY
#endif

#include "../h/mh.h"
#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 SVR4
#ifndef LOCKF
#define LOCKF
#endif
#include <unistd.h>
#endif /* SVR4 */
#ifdef	LOCKF
#include <sys/errno.h>
#include <sys/file.h>
#ifndef	F_ULOCK
#ifdef	UNISTD
#include <unistd.h>
#else	/* UNISTD */
#include <sys/fcntl.h>
#endif	/* UNISTD */
#endif
#endif	/* LOCKF */
#if defined(_AIX) || defined(AUX)
#include <sys/file.h>
#endif
#if defined(__386BSD__) || defined(BSD44)
#include <fcntl.h>
#endif
#ifdef	_AIX
#include <sys/time.h>
#include <time.h>
#else
#ifdef	BSD42
#include <sys/time.h>
#else	/* BSD42 */
#include <time.h>
#endif	/* BSD42 */
#endif

#ifdef	SYS5
#define	u_short	ushort
#define u_long  ulong
#endif


#if defined(SYS5) && !defined(_AIX)
#define	index	strchr
#define	rindex	strrchr
#endif
#ifdef	BSD42
#define	FLOCK		/* LOCKF will override this, if defined */
#endif

#include <errno.h>
#include <unistd.h>


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

#ifdef	MAILLOCK
/* Both "mts.h" and <maillock.h> defines MAILDIR */
#undef MAILDIR
#include <maillock.h>
static int is_default_spool();
#undef LIBLOCKFILE
#else /* MAILLOCK */
#ifdef LIBLOCKFILE
#define LOCK_LIBLOCKFILE 1
#define LOCK_B_LKOPEN 2
char *lockname_from_fd();
#endif
#endif /* MAILLOCK */

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

time_t	time ();
char   *mktemp ();

/*  */

extern void    mts_init (const char *);

int	lkopen (file, access)
register char   *file;
register int     access;
{
    mts_init ("mts");
    switch (lockstyle) {
	case LOK_UNIX:
#ifdef	MAILLOCK
	    if (is_default_spool (file))
		return f_lkopen (file, access);
	    /* else fall */
#else /* MAILLOCK */
#if defined(FLOCK) || defined(LOCKF) || defined(FCNTL) || defined(LIBLOCKFILE)
	    return f_lkopen (file, access);
#endif
#endif /* MAILLOCK */

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

/*  */

static int  b_lkopen (file, access)
register char   *file;
register int     access;
{
    register int    i,
                    j;
    time_t  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;
		}
#ifdef LIBLOCKFILE
		timerON (curlock, i, LOCK_B_LKOPEN);
#else
		timerON (curlock, i);
#endif
		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;
#if defined(hpux) || defined(ncr)
    write(fd, "MH lock\n",8);
#endif /* hpux */
    (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 == 0)
	cp = file;
    if (lockldir == NULL || *lockldir == 0) {
	if (cp != file) {
	    (void) sprintf (bp, "%.*s", (int)(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 == 0)
	    (void) strcpy (tmplock, ",LCK.XXXXXX");
	else
	    (void) sprintf (tmplock, "%.*s,LCK.XXXXXX",
		(int)(cp - curlock), curlock);
	(void) unlink (mktemp (tmplock));
    }
}

/*  */

#if	defined(BSD42) || defined(SVR4)
#include <sys/file.h>
#if	defined(SUN40) || defined(SVR4)
#include <sys/fcntl.h>
#endif
#else 
#ifdef	FCNTL
#include <fcntl.h>
#endif
#endif

#ifdef	MAILLOCK

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

    if (maillock (getusr (), 5) == L_SUCCESS) {
	if ((fd = open (file, access | O_NDELAY)) == NOTOK)
	    return NOTOK;
	return fd;
    }

    return NOTOK;
}

#else /* not MAILLOCK */
#ifdef LIBLOCKFILE
#include <lockfile.h>
#include <limits.h>

static int  f_lkopen (file, access)
register char   *file;  
register int     access;
{
	int r, fd;
	char mlockfile[PATH_MAX];
	
	snprintf(mlockfile, PATH_MAX, "%s.lock", file);
	r = lockfile_create(mlockfile, 5, 0);
	if (r != 0) 
		return NOTOK;
	fd = open(file, access | O_NDELAY);
	if (fd == -1)
		return NOTOK;
	/* we have to set up a timer so we can touch the lockfile occasionally */
	timerON(mlockfile, fd, LOCK_LIBLOCKFILE);
	/* NB: it's OK to pass mlockfile as timerON immediately copies it
	 * and we then use the copy in all lockfile_foo() operations.
	 */
	return fd;
}
 
#else /* not LIBLOCKFILE */ 
#if	defined(FLOCK) || defined(LOCKF) || defined(FCNTL)

static int  f_lkopen (file, access)
register char   *file;
register int     access;
{
    register int    fd,
                    i,
		    j;
#ifdef FCNTL
    struct flock    buf;
#endif /* FCNTL */

    for (i = 0; i < 5; i++) {
#if defined(LOCKF) || defined(FCNTL)
	j = access;
	access &= ~O_APPEND;	/* make sure we open at the beginning */
	if ((access & 03) == O_RDONLY) {
	/* We MUST have write permission or lockf/fcntl() won't work */
	/* (Stupid eh?) */
	    access &= ~O_RDONLY;
	    access |= O_RDWR;
	}
#endif	/* LOCKF || FCNTL */
	if ((fd = open (file, access | O_NDELAY)) == NOTOK)
	    return NOTOK;
#ifndef	LOCKF
#ifndef	FLOCK
#ifndef	FCNTL
	/* should be an error? */
#else /* FCNTL */
	buf.l_type = F_WRLCK;
	buf.l_whence = 0;
	buf.l_start = 0;
	buf.l_len = 0;
	if (fcntl (fd, F_SETLK, &buf) != NOTOK)
	    return fd;
#endif
#else /* FLOCK */
	if (flock (fd, LOCK_EX | LOCK_NB) != NOTOK)
	    return fd;
#endif
#else /* LOCKF */
	if (lockf (fd, F_TLOCK, 0L) != NOTOK) {
	    /* see if we should be at the end */
	    if (j & O_APPEND)
#ifdef SVR4
		lseek (fd, (off_t)0, SEEK_END);
#else
		lseek (fd, (off_t)0, L_XTND);
#endif
	    return fd;
	}
	/* Fix errno - lockf screws it */
	if (errno == EACCES)
	    errno = EWOULDBLOCK;
#endif
	j = errno;
	(void) close (fd);

	sleep (5);
    }

    (void) close (fd);
    errno = j;
    return NOTOK;
}
#endif	/* FLOCK || LOCKF || FCNTL */
#endif /* not LIBLOCKFILE */
#endif /* not MAILLOCK */

/*  */

/* ARGSUSED */

int     lkclose (fd, file)
register int     fd;
register char   *file;
{
    char    curlock[BUFSIZ];
    struct stat st;
#ifdef FCNTL
    struct flock buf;
#endif

    if (fd == NOTOK)
	return OK;
    switch (lockstyle) {
	case LOK_UNIX: 
#ifndef	MAILLOCK
#ifndef	LIBLOCKFILE
#ifndef	LOCKF
#ifndef	FLOCK
#ifndef	FCNTL
	/* should be an error? */
#else	/* FCNTL */
	    buf.l_type = F_UNLCK;
	    buf.l_whence = 0;
	    buf.l_start = 0;
	    buf.l_len = 0;
	    fcntl(fd, F_SETLK, &buf);
	    break;
#endif
#else	/* FLOCK */
	    flock (fd, LOCK_UN);
	    break;
#endif
#else	/* LOCKF */
	    lseek (fd, (off_t)0, L_SET); /* make sure we unlock the whole thing */
	    lockf (fd, F_ULOCK, 0L);
	    break;
#endif	
#else	/* LIBLOCKFILE */
            {
               char *n = lockname_from_fd(fd);
               if (!n)     /* shouldn't happen: would be program bug */
               	  return NOTOK;
	       lockfile_remove(n);
	       timerOFF(fd);
   	       break;
   	    }
#endif
#else	/* MAILLOCK */
	    if (is_default_spool (file)) {
		mailunlock ();
		break;
	    }
	    /* else fall */
#endif

	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 */

int	lkfclose (fp, file)
register FILE	*fp;
register char	*file;
{
    char    curlock[BUFSIZ];
    struct stat st;
#ifdef FCNTL
    struct flock buf;
#endif

    if (fp == NULL)
	return OK;

    switch (lockstyle) {
	case LOK_UNIX: 
#ifndef	MAILLOCK
#ifndef	LIBLOCKFILE
#ifndef	LOCKF
#ifndef	FLOCK
#ifndef	FCNTL
	/* should be an error? */
#else	/* FCNTL */
	    buf.l_type = F_UNLCK;
	    buf.l_whence = 0;
	    buf.l_start = 0;
	    buf.l_len = 0;
	    fcntl(fileno(fp), F_SETLK, &buf);
	    break;
#endif
#else /* FLOCK */
	    flock (fileno(fp), LOCK_UN);
	    break;
#endif
#else	/* LOCKF */
	    fseek (fp, 0L, 0); /* make sure we unlock the whole thing */
	    lockf (fileno(fp), F_ULOCK, 0L);
	    break;
#endif
#else	/* LIBLOCKFILE */
	    {
	       char *n = lockname_from_fd(fileno(fp));
	       if (!n)      /* shouldn't happen */
	          return NOTOK;
 	       lockfile_remove(n);
	       timerOFF(fileno(fp));
	       break;
            }
#endif
#else	/* MAILLOCK */
	    if (is_default_spool (file)) {
		mailunlock ();
		break;
	    }
	    /* else fall */
#endif

	default: 
	    if (fstat (fileno (fp), &st) != NOTOK) {
		lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
		(void) unlink (curlock);
#ifdef LIBLOCKFILE
	        /* shouldn't we timerOFF(fileno(fp))? We do in lkclose() -- PMM */
#else
		timerOFF (fileno (fp));
#endif
	    }
    }

    return (fclose (fp));
}

/*  */

#include <signal.h>

#define	NSECS	((unsigned) 20)


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

static struct lock *l_top = NULLP;

#ifdef MAILLOCK
static int  is_default_spool (file)
register char	*file;
{
    static char *default_spool = NULLCP;

    if (! default_spool)
	default_spool = concat (MAILDIR, getusr (), NULLCP);
    return strcmp(file, default_spool) == 0;
}

#else /* MAILLOCK */
#ifdef LIBLOCKFILE
/* simple routine to allow us to get the filename given an fd.
 * Returns NULL if the fd isn't valid.
 */
char *lockname_from_fd(fd)
int	fd;
{
	struct lock *pp;
	
	for (pp = l_top; pp; pp = pp -> l_next)
		if (pp->l_fd == fd)
			return pp->l_lock;

	return NULL;
}
#endif /* LIBLOCKFILE */
#endif /* MAILLOCK */

/* ARGSUSED */

static TYPESIG 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)
#ifdef LIBLOCKFILE
	if (lp->l_locktype == LOCK_LIBLOCKFILE)
	   lockfile_touch(lp->l_lock);
	else
#endif
	if (*(cp = lp -> l_lock) && (j = creat (cp, 0400)) != NOTOK)
	    (void) close (j);

    (void) alarm (NSECS);
}

/*  */

#ifdef LIBLOCKFILE
static void timerON (lock, fd, ltype)
int 	ltype;
#else
static void timerON (lock, fd)
#endif
char   *lock;
int	fd;
{
    register struct lock   *lp;

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

    lp -> l_fd = fd;
#ifdef LIBLOCKFILE
    lp -> l_locktype = ltype;
#endif
    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;
    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);
}