[comp.sources.misc] v05i014: flock - shell level access to flock

ksb@s.cc.purdue.edu (Kevin Braunsdorf) (10/28/88)

Posting-number: Volume 5, Issue 14
Submitted-by: "Kevin Braunsdorf" <ksb@s.cc.purdue.edu>
Archive-name: flock

[This was supposed to have been posted a few weeks ago but somehow found a
crack to get lost in!  (oops!)  ++bsa]

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README		Makefile
#	flock.1l 	flock.c
# This archive created: Sun Oct 23 15:09:58 1988
# By:	Kevin Braunsdorf (Purdue UNIX Group)
sed 's/^K//' << \SHAR_EOF > README
KFlock applies or removes an advisory lock on the named file or file
Kdescriptor.  Advisory locks allow cooperating processes to perform
Kconsistent operations on files see flock(2).  This program is *very*
Kuseful on a Sequent machine; almost as useful on fast uniprocessors.
K
KHere is a case where flock(1) is very useful: on a Sequent Balance
Kseries machine parallel make can be used to compile many files at the
Ksame time.  This doesn't work for programs that use xstr(1) because
Kcompeting processes using xstrings fails.  In the Makefile for vi one
Kmight use a flock on the Makefile to force (only) the xstr to be run
Ksequentially:
K
K	.c.o:
K		${CC} -E ${CFLAGS} $*.c | \
K		flock Makefile sh -c "${XSTR} -c -; mv x.c $*.p.x"; \
K		sed '/rcsid\[\]/d' < $*.p.x > $*.p.c; \
K		${CC} ${CFLAGS} -c $*.p.c; \
K		mv $*.p.o $*.o; \
K		rm $*.p.x $*.p.c
K	  
KWhich will start a few cpps in parallel and run only one xstr at a time,
Kas soon as it can it will run the sed/cc/mv/rm allowing another process
Kto begin using xstr.
K
K(I flock the Makefile by convention, because I know I always have one.
K If more then one command set needs to work from the same Makefile I
K flock `complie.lock', and `yacc.lock' or some such, not using Makefile
K for either.  The clean target in the Makefile removes these files.)
K
Kkayessbee (Kevin S Braunsdorf) ksb@j.cc.purdue.edu, pur-ee!ksb
SHAR_EOF
sed 's/^K//' << \SHAR_EOF > flock.1l
K.\" $Id: flock.1l,v 1.3 88/10/23 14:53:53 ksb Exp $
K.\" by Kevin Braunsdorf and Matthew Bradburn
K.TH FLOCK 1L LOCAL
K.SH NAME
Kflock - lock a file to synchronize command streams
K.SH SYNOPSIS
K\fBflock\fP [-\fBc\fP\fBh\fP\fBn\fP] [\-\fBEX\fP|\fBSH\fP|\fBUN\fP|\fBNB\fP] \fIfile\fP|\fIfd\fP [\fIcmd\fP]
K.SH DESCRIPTION
KFlock applies or removes an advisory lock on the named file or file descriptor.
KAdvisory locks allow cooperating processes to perform consistent operations
Kon files see \fIflock\fP(2).
K.PP
KThe exit status from the \fBflock\fP command is the exit status of
Kits child.  The default \fIcmd\fP is ``/bin/true'' so that by default
K\fBflock\fP will succeed.  \fBFlock\fP with exit nonzero if either
K\-\fBNB\fP (nonblocking) is specified and flock fails, or
Kthe specified \fIfile\fP (\fIfd\fP) cannot be (isn't) opened.
K.PP
KIt \fBflock\fP has to open the file to get the lock it will not pass
Kthe created file descriptor to the child process.  (Said process might
Kunlock the descriptor.)
K.SH OPTIONS
K.TP
K.BI \-c
KCreate the lock file if it doesn't exist.
K.TP
K.BI \-h
KGive a brief help message.
K.TP
K.BI \-n
KDo not create the lock file if it doesn't exist.  This is the default.
K.TP
K.BI \-EX
KSet or reset the LOCK_EX bit in the arguments to \fIflock\fP(2).
KThis is the default if no locking bits are given.
K.TP
K.BI \-NB
KSet or reset the LOCK_NB bit in the arguments to \fIflock\fP(2).
K.TP
K.BI \-SH
KSet or reset the LOCK_SH bit in the arguments to \fIflock\fP(2).
K.TP
K.BI \-UN
KSet or reset the LOCK_UN bit in the arguments to \fIflock\fP(2).
KThis option is only useful when a file descriptor is given as the
K`file' on which to operate.
K.PP
KThe option \-\fBEX\fP may be spelled \-\fBLOCK_EX\fP, likewise the other
Klocking bits may be spelled more verbosely.
K.PP
KThe locking bits may be inclusive or'ed together with a pipe (`|'), which
Kmust be quoted from the shell.
K.SH EXAMPLES
K.PP
KIf a given daemon flocks its log file before each group of writes
Kone might use:
K.sp 1
K	flock -EX 1 echo \*(lqsomething important\*(rq >>daemon.log
K.sp 1
Kto avoid interlaminate writes to the log.
K.PP
KIn a shell script:
K.sp 1
K	# stdout is a (possibly) shared file so we wait for it
K.br
K	flock -EX 1
K.br
K	# these have to be run under one lock
K.br
K	doit1 ; doit2; doit3
K.br
K	# end critical code, unlock the descriptor
K.br
K	flock -UN 1
K.sp 1
KTo edit a shared file:
K.sp 1
K	flock share.c vi share.c
K.sp 1
K.SH BUGS
KNone known.
K.SH AUTHOR
KKevin Braunsdorf
K.br
KPurdue University, West Laffayette IN
K.br
Kpur-ee!ksb, ksb@j.cc.purdue.edu
K.SH "SEE ALSO"
Ksh(1), true(1), flock(2), open(2)
SHAR_EOF
sed 's/^K//' << \SHAR_EOF > Makefile
K# makefile for flock
K#			by Kevin S Braunsdorf, PUCC
K
KBIN=	${DESTDIR}$$HOME/bin
K
KI=/usr/include
KS=/usr/include/sys
K
KINCLUDE=
KDEBUG=	-O
KCDEFS=	-Dbsd
KCFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE}
K
KHDR=	
KSRC=	flock.c
KOBJ=	flock.o
KSOURCE=	Makefile ${HDR} ${SRC}
K
Kall: flock
K
Kflock:
K	${CC} -o $@ ${CFLAGS} flock.c
K
Kclean: FRC
K	rm -f Makefile.bak flock *.o a.out core errs tags
K
Kdepend: ${SRC} ${HDR} FRC
K	maketd -a ${CDEFS} ${INCLUDE} -b flock.c
K
Kinstall: all FRC
K	install -c -s flock ${BIN}
K
Klint: ${HDR} ${SRC} FRC
K	lint -hnx ${CDEFS} ${INCLUDE} ${SRC}
K
Kprint: source FRC
K	lpr -J'flock source' ${SOURCE}
K
Ksource: ${SOURCE}
K
Kspotless: clean
K	rcsclean ${SOURCE}
K
Ktags: ${SRC} ${HDR}
K	ctags -t ${SRC} ${HDR}
K
K${SOURCE}:
K	co $@
K
KFRC:
K
K# DO NOT DELETE THIS LINE - maketd DEPENDS ON IT
K
Kflock: $I/fcntl.h $I/stdio.h $S/file.h $S/types.h $S/wait.h flock.c
K
K# *** Do not add anything here - It will go away. ***
SHAR_EOF
sed 's/^K//' << \SHAR_EOF > flock.c
K/*
K * flock a file and wait for a process to exit				(ksb)
K *
K * Copyright 1988, All Rights Reserved
K *	Kevin S Braunsdorf
K *	ksb@j.cc.purdue.edu, pur-ee!ksb
K *	Math/Sci Building, Purdue Univ
K *	West Lafayette, IN
K *
K *  `You may redistibute this code as long as you don't claim to have
K *   written it. -- kayessbee'
K *
K * $Compile: ${cc-cc} ${DEBUG--C} ${SYS--Dbsd} %f -o %F
K */
K#include <sys/types.h>
K#include <sys/file.h>
K#include <sys/wait.h>
K#include <fcntl.h>
K#include <stdio.h>
K
K#if defined(bsd)
K#define strchr	index
K#define strrchr	rindex
K#endif
K
Kstatic char *progname =
K	"$Id: flock.c,v 2.0 88/10/23 15:06:21 ksb Exp $";
K
Kstatic char acUsage[] =		/* this is the usage line for the user	*/
K	"%s: usage [-chn] [-EX|SH|UN|NB] file|fd [cmd]\n";
Kstatic char *apcHelp[] = {	/* help text				*/
K	"c	create file, if nonexistant",
K	"h	print this help message",
K	"n	do not create file",
K	"EX	exclusive lock, default",
K	"NB	do not block for lock",
K	"SH	shared lock",
K	"UN	unlock",
K	(char *)0
K};
K
Ktypedef struct LKnode {		/* a cmd line option implies a lock bit	*/
K	char *pcflag;
K	int iflag;
K} LOCKKEY;
K
Kstatic LOCKKEY aLKLocks[] = {	/* the list of the cmd lines we know	*/
K	{"LOCK_EX", LOCK_EX},
K	{"LOCK_SH", LOCK_SH},
K	{"LOCK_UN", LOCK_UN},
K	{"LOCK_NB", LOCK_NB},
K	{"EX", LOCK_EX},
K	{"SH", LOCK_SH},
K	{"UN", LOCK_UN},
K	{"NB", LOCK_NB},
K	{(char *)0, -1}
K};
K
K/*
K * determine which flag the use wants to set/clear			(ksb)
K */
Kint
KFlag(pcCmd)
Kchar *pcCmd;
K{
K	register char *pcTry;
K	register LOCKKEY *pLK;
K	extern char *strchr();
K
K	for (pLK = aLKLocks; (char *)0 != (pcTry = pLK->pcflag); ++pLK) {
K		if (0 == strcmp(pcTry, pcCmd)) {
K			return pLK->iflag;
K		}
K	}
K	if ((char *)0 != (pcTry = strchr(pcCmd, '|'))) {
K		*pcTry++ = '\000';
K		return Flag(pcCmd)|Flag(pcTry);
K	}
K
K	fprintf(stderr, "%s: `%s' is not a flock key\n", progname, pcCmd);
K	exit(1);
K	/*NOTREACHED*/
K}
K
K/*
K * is this character string all digits					(ksb)
K */
Kint
Kisnumber(pc)
Kchar *pc;
K{
K	while (*pc) {
K		if (*pc < '0' || *pc > '9')
K			break;
K		++pc;
K	}
K	return *pc == '\000';
K}
K
K/*
K * Get a lock on the named file and execute the rest of our arguments	(ksb)
K * while the lock is active, when this command exits exit with it's
K * extit status.
K */
Kint
Kmain(argc, argv)
Kint argc;
Kchar **argv;
K{
K	extern int atoi();
K	extern char *strrchr();
K	static char *apcTrue[] = {"true", (char *)0};
K	auto union wait wait_buf;
K	auto int fd, tClose, oFlags, fCreate = 0;
K	auto int iLock = -1;
K	auto char **ppcHelp;
K
K
K	if ((char *)0 != (progname = strrchr(*argv, '/'))) {
K		++progname;
K	} else {
K		progname = *argv;
K	}
K	++argv, --argc;
K	while (argc > 0 && '-' == argv[0][0]) {
K		switch (*++argv[0]) {
K		case '\000':
K			break;
K		case 'n':
K		case 'c':
K			fCreate = argv[0][0] == 'c';
K			argv[0][0] = '-';
K			continue;
K		case 'h':
K			fprintf(stdout, acUsage, progname);
K			for (ppcHelp = apcHelp; (char *)0 != *ppcHelp; ++ppcHelp) {
K				fprintf(stdout, "%s\n", *ppcHelp);
K			}
K			exit(0);
K		default:
K			if (-1 == iLock)
K				iLock = 0;
K			iLock ^= Flag(argv[0]);
K			break;
K		}
K		++argv, --argc;
K	}
K
K	if (-1 == iLock) {
K		iLock = LOCK_EX;
K	}
K
K	if (0 == argc) {
K		fprintf(stderr, acUsage, progname);
K		exit(1);
K	}
K
K	tClose = 1;
K	if (-1 == (fd = open(argv[0], O_RDONLY, 0600))) {
K		oFlags = 0 != fCreate ? O_CREAT|O_WRONLY|O_APPEND : O_WRONLY|O_APPEND;
K		if (-1 == (fd = open(argv[0], oFlags, 0666))) {
K			if (!isnumber(argv[0])) {
K				fprintf(stderr, "%s: open: ", progname);
K				perror(argv[0]);
K				exit(1);
K			}
K			fd = atoi(argv[0]);
K			if (-1 == fcntl(fd, F_GETFD, 0)) {
K				fprintf(stderr, "%s: %d: ", progname, fd);
K				perror("fcntl");
K				exit(1);
K			}
K			tClose = 0;
K		}
K	}
K	if (-1 == flock(fd, iLock)) {
K		fprintf(stderr, "%s: flock: ", progname);
K		perror(argv[0]);
K		exit(1);
K	}
K	++argv, --argc;
K
K	if (0 == argc) {
K		argv = apcTrue;
K	}
K	if (tClose != 0) {		/* save a fork */
K		switch (fork()) {
K		case 0:
K			close(fd);
K			break;
K		case -1:
K			fprintf(stderr, "%s: ", progname);
K			perror("fork");
K			exit(1);
K			/*NOTREACHED*/
K		default:
K			wait(& wait_buf);
K			/* exit will close fd for us */
K			exit((int) wait_buf.w_retcode);
K			/*NOTREACHED*/
K		}
K	}
K	execvp(argv[0], argv);
K	fprintf(stderr, "%s: ", progname);
K	perror(argv[0]);
K	exit(1);
K	/*NOTREACHED*/
K}
SHAR_EOF
#	End of shell archive
exit 0