From decwrl!labrea!rutgers!ukma!cwjcc!hal!ncoast!allbery Sun Oct 30 15:06:24 PST 1988 Article 691 of comp.sources.misc: Path: granite!decwrl!labrea!rutgers!ukma!cwjcc!hal!ncoast!allbery From: ksb@s.cc.purdue.edu (Kevin Braunsdorf) Newsgroups: comp.sources.misc Subject: v05i014: flock - shell level access to flock(2) Message-ID: <8810232013.AA06867@s.cc.purdue.edu> Date: 28 Oct 88 02:46:21 GMT Sender: allbery@ncoast.UUCP Reply-To: ksb@s.cc.purdue.edu (Kevin Braunsdorf) Lines: 411 Approved: allbery@ncoast.UUCP Posting-number: Volume 5, Issue 14 Submitted-by: "Kevin Braunsdorf" 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 K#include K#include K#include K#include 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