Subject: File locking
To: None <netbsd-users@NetBSD.ORG>
From: D'Arcy J.M. Cain <darcy@druid.com>
List: netbsd-users
Date: 04/23/1997 21:29:47
Here is the file lock program I mentioned in a previous post.  The uulock
function that follows uses this to do UUCP locking.


/*
filelock.c
Written by D'Arcy J.M. Cain
darcy@druid.com
Copyright 1991, 1992, 1996

This code may be used on any computer system for any purpose by anyone.
Just give me credit.

implements file locking

*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<unistd.h>
#include	<fcntl.h>
#include	<sys/stat.h>
#include	<errno.h>
#include	<signal.h>

static int
rmlock(char *file, int me)
{
	int		r, fd, pid;
	char	buf[12];

	/* make sure it wasn't removed already somehow */
	if ((fd = open(file, O_RDONLY)) == -1)
	{
	    if (errno == ENOENT)
			return(0);
	}
	else
	{
		/* get PID of process that locked file */
		r = read(fd, buf, 11);
		close(fd);

		/* can't remove lock of another active process unless it is gone */
		if (r == 11 && (pid = atoi(buf)) != 0 && pid != me && !kill(pid, 0))
		   	return(EPERM);
	}

	/* OK to remove lock if we can */
	return(unlink(file));
}

static int
try2lock(char *chkstr, char *tempfile, char *realfile)
{
	int		fd;
	int		ret = 0;
	int		cmask = umask(0);

	/* create the temporary file if we can */
	if ((fd = creat(tempfile, 0444)) < 0)
	{
		if((errno == EMFILE) || (errno == ENFILE))
			unlink(tempfile);

		umask(cmask);
		return(errno);
	}

	/* write our PID to the file */
	write(fd, chkstr, strlen(chkstr));
	close(fd);
	umask(cmask);

	/* try to link the file to the actual lock file */
	if (link(tempfile, realfile) < 0)
		ret = errno;

	/* in any case remove the temporary file */
	unlink(tempfile);
	return(ret);
}

int
fileunlock(const char *lockdir, const char *file)
{
	char	realfile[BUFSIZ];

	sprintf(realfile, "%s/%s", lockdir, file);
	return(rmlock(realfile, getpid()));
}

int
filelock(const char *lockdir, const char *file)
{
	char	chkstr[12], tempfile[BUFSIZ], realfile[BUFSIZ];
	int		pid = getpid(), k = -1;

	sprintf(chkstr, "%10.10d\n", pid);

	if (lockdir == NULL)
	{
		strcpy(realfile, file);
		sprintf(tempfile, "TL.%10.010d", pid);
	}
	else
	{
		sprintf(realfile, "%s/%s", lockdir, file);
		sprintf(tempfile, "%s/TL.%10.010d", lockdir, pid);
	}

	/* see if simple lock works */
	if ((k = try2lock(chkstr, tempfile, realfile)) == 0)
		return(0);

	/* file exists only acceptable error */
	if (k != EEXIST)
		return(k);

	/* check if process with lock still active) */
	if ((k = rmlock(realfile, pid)) != 0)
		return(k);
	else
		return(try2lock(chkstr, tempfile, realfile));
}

And here is the uulock function.

/*
uulock.c
Written by D'Arcy J.M. Cain
darcy@druid.com
Copyright 1991, 1992, 1996

SVR4 & NetBSD stuff - I'll ifdef in any other versions if the same syntax used 

*/

#include	<stdio.h>
#include	<string.h>
#include	<uucp.h>

extern int		filelock(const char *, const char *);
extern int		fileunlock(const char *, const char *);

#ifdef		SVR4
#include	<sys/stat.h>

#undef	getmajor
#undef	getminor
#define	getmajor(x)	(x >> 18)
#define	getminor(x)	(x & 0xff)
#endif

/* note: assumes only one device locked at a time by this program */
int
uu_lock(const char *tty)
{
	static char		buf[128] = "";

#ifdef	NetBSD
	const char		*p;
#else
	struct stat		st_buf;
#endif

	if (*buf)
		fileunlock(LOCKDIR, buf);

	*buf = 0;

	if (!tty)
		return(0);

	uu_debug(5, "Locking device %s\n", tty);

#ifdef	NetBSD
	if ((p = strrchr(tty, '/')) == NULL)
		p = tty;
	else
		p++;

	sprintf(buf, "LCK..%s", p);
#else
	if (stat(tty, &st_buf))
		return(1);

	sprintf(buf, "LK.000.%3.03lu.%3.03lu",
		getmajor(st_buf.st_rdev), getminor(st_buf.st_rdev));
#endif

	uu_debug(7, "Lock file " LOCKDIR "/%s\n", buf);
	return(filelock(LOCKDIR, buf));
}


-- 
D'Arcy J.M. Cain                           |  Democracy is three wolves
darcy@{druid.com|vex.net}                  |  and a sheep voting on         
+1 416 943 5281     (DoD#0082)    (eNTP)   |  what's for dinner.
                --  http://www.druid.com/darcy  --