file lock convience routines (part01/01)

Mike Howard how at milhow1.UU.NET
Fri Jan 25 09:06:28 AEST 1991


I wrote this because I'm sick of dealing with different locking
syntax and locking semantics depending on the machine the code is
running on.  BTW, this should be considered an Alpha release.

These are convenience routines which implement file locking in standard
ways regardless of the method selected within the particular operating
system.  Currently `supported' are fcntl(), lockf(), and flock() style
locking.  The code has been developed and `tested' on a Sun
SPARCstation 1 under SunOS 4.1.  It has also been `tested' under SCO
Xenix 2.3.2 and `passes', except that lockf() style locking sets errno
to EAGAIN rather than EACCES, as it does on the Sun.  I haven't
`fixed' it because I think that SCO's errno value is wrong and I feel
like being perverse today.

The routine `test-lock.c' is a half-assed test driver.  `do-test.sh'
causes a variety of test to be performed - the results are saved in
test.log.

If anyone is interested in building up these routines into something
solid for the purpose of porting, send me diffs.  If the interest is
wide enough I will attempt to coordinate revisions and will publish
through comp.sources.misc.

Mike Howard
how%milhow1 at uunet.uu.net

================================cut here================================
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  MANIFEST Makefile README do-test.sh lock-file.c
#   lock-file.h lock-file.man patchlevel.h test-lock.c
# Wrapped by mike at milhow4 on Thu Jan 24 17:07:38 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(393 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   1	
X README                     1	
X do-test.sh                 1	
X lock-file.c                1	
X lock-file.h                1	
X lock-file.man              1	
X patchlevel.h               1	
X test-lock.c                1	
END_OF_FILE
if test 393 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1513 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS	=	-g
X# CFLAGS	=	-O
X
XSRCS	=	test-lock.c lock-file.c
XOBJS	=	$(SRCS:.c=.o)
XHDRS	=	lock-file.h patchlevel.h
XMAN	=	lock-file.man
X
XINCLUDE_DIR	=	/usr/local/include
XMAN_DIR		=	/usr/man/man1
XMAN_SUFFIX	=	1
X
Xall : fcntl_raw fcntl_buf \
X	flock_raw flock_buf \
X	lockf_raw lockf_buf \
X	nolock_raw nolock_buf
X
Xfcntl_raw : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DFCNTL_STYLE $(SRCS) -o fcntl_raw
X	rm -f $(OBJS)
X
Xflock_raw : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DFLOCK_STYLE $(SRCS) -o flock_raw
X	rm -f $(OBJS)
X
Xlockf_raw : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DLOCKF_STYLE $(SRCS) -o lockf_raw
X	rm -f $(OBJS)
X
Xnolock_raw : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) test-lock.c -o nolock_raw
X	rm -f $(OBJS)
X
Xfcntl_buf : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DFCNTL_STYLE -DUSE_BUFFERED $(SRCS) -o fcntl_buf
X	rm -f $(OBJS)
X
Xflock_buf : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DFLOCK_STYLE -DUSE_BUFFERED $(SRCS) -o flock_buf
X	rm -f $(OBJS)
X
Xlockf_buf : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DLOCKF_STYLE -DUSE_BUFFERED $(SRCS) -o lockf_buf
X	rm -f $(OBJS)
X
Xnolock_buf : $(SRCS) $(HDRS)
X	$(CC) $(CFLAGS) -DUSE_BUFFERED test-lock.c -o nolock_buf
X	rm -f $(OBJS)
X
Xinstall :
X	cp file-lock.h $(INCLUDE_DIR)
X	cp file-lock.man $(MAN_DIR)/file-lock.$(MAN_SUFFIX)
X	echo "install file-lock.o in your local library"
X
Xclean :
X	rm -f 	fcntl_raw fcntl_buf \
X		flock_raw flock_buf \
X		lockf_raw lockf_buf \
X		nolock_raw nolock_buf \
X		$(OBJS)
X
Xveryclean :
X	rm -f test-tmp* test.log test.log-
X	make clean
X
Xkit :
X	makekit -m README Makefile $(SRCS) $(HDRS) do-test.sh $(MAN)
END_OF_FILE
if test 1513 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1188 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XI wrote this because I'm sick of dealing with different locking
Xsyntax and locking semantics depending on the machine the code is
Xrunning on.  BTW, this should be considered an Alpha release.
X
XThese are convenience routines which implement file locking in standard
Xways regardless of the method selected within the particular operating
Xsystem.  Currently `supported' are fcntl(), lockf(), and flock() style
Xlocking.  The code has been developed and `tested' on a Sun
XSPARCstation 1 under SunOS 4.1.  It has also been `tested' under SCO
XXenix 2.3.2 and `passes', except that lockf() style locking sets errno
Xto EAGAIN rather than EACCES, as it does on the Sun.  I haven't
X`fixed' it because I think that SCO's errno value is wrong and I feel
Xlike being perverse today.
X
XThe routine `test-lock.c' is a half-assed test driver.  `do-test.sh'
Xcauses a variety of test to be performed - the results are saved in
Xtest.log.
X
XIf anyone is interested in building up these routines into something
Xsolid for the purpose of porting, send me diffs.  If the interest is
Xwide enough I will attempt to coordinate revisions and will publish
Xthrough comp.sources.misc.
X
XMike Howard
Xhow%milhow1 at uunet.uu.net
END_OF_FILE
if test 1188 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'do-test.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'do-test.sh'\"
else
echo shar: Extracting \"'do-test.sh'\" \(1176 characters\)
sed "s/^X//" >'do-test.sh' <<'END_OF_FILE'
X#!/bin/sh
X# sh do-test.sh [min-corruptions(50) [initial-reps(50) [inc(def to 25)]]]
X
Xif [ "$1" = "-h" ] ; then
X  echo "sh do-test.sh [min-corruptions(50) [initial-reps(50) [inc(def to 25)]]]"
X  exit
Xfi
X# find an appropriate rep count
X
XMIN=${1:-50}
XREP=${2:-50}
XINC=${3:-25}
Xtrap "rm /tmp/t-$$ ; exit 0" 0
Xtrap "rm /tmp/t-$$ ; exit 1" 1 2 3 15
X
Xif [ -s test.log ] ; then
X  echo "backing up previous test.log to test.log-"
X  mv test.log test.log-
Xfi
X
Xecho "detail output saved in test.log"
Xecho "computing min reps in order to get $MIN mixed outputs"
Xwhile true ; do
X  echo "trying nolock_raw with $REP reps"
X  nolock_raw $REP | tee test.log |
X  awk '
X  $0 ~ /corrupt sequences found$/ { corrupt = $2 }
X  END { print corrupt }
X  ' - >/tmp/t-$$
X  COUNT=`cat /tmp/t-$$`
X  if [ $COUNT -lt $MIN ] ; then
X    REP=`expr $REP + $INC`
X  else
X    break;
X  fi
Xdone
X
Xecho "$REP repetions satisfy criterion"
Xmv test-tmp test-tmp.nolock_raw
X
Xfor x in nolock_buf \
X  fcntl_raw fcntl_buf \
X  flock_raw flock_buf \
X  lockf_raw lockf_buf
Xdo
X if [ -x $x ] ; then
X   echo "testing $x"
X   echo "" >>test.log
X   echo "testing $x" >>test.log
X   $x $REP >>test.log
X   mv test-tmp test-tmp.$x
X fi
Xdone
END_OF_FILE
if test 1176 -ne `wc -c <'do-test.sh'`; then
    echo shar: \"'do-test.sh'\" unpacked with wrong size!
fi
# end of 'do-test.sh'
fi
if test -f 'lock-file.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lock-file.c'\"
else
echo shar: Extracting \"'lock-file.c'\" \(11646 characters\)
sed "s/^X//" >'lock-file.c' <<'END_OF_FILE'
X/* @(#)lock-file.c	1.2 91/01/24 */
X/* copyright 1991, Mike Howard & Miller/Howard Investments, Inc.
X   all rights reserved */
X
X/*
X  these are convenience routines which implement file locking in standard
X  ways regardless of the method selected within the particular operating
X  system.
X
X  Note: for record locks, the current file position is changed to the
X  beginning of the locked region.  for whole file locks, the current file
X  position is not changed.
X
X  Note: these routines do not work on buffered files.
X
X  In all cases, the three parameters are:
X  fd - int - file descriptor
X  offset - off_t - offset from the beginning of file to begin locking
X                   region.
X  len - off_t - length of locked region.
X
X  offset == 0, len == 0 locks entire file.
X
X  The functions do what they appear to do:
X    int lock_record_shared_wait(fd, offset, len)
X    int lock_record_exclusive_wait(fd, offset, len)
X    int lock_record_shared_nowait(fd, offset, len)
X    int lock_record_exclusive_nowait(fd, offset, len)
X    int unlock_record(fd, offset, len)
X    int lock_file_shared_wait(fd)
X    int lock_file_exclusive_wait(fd)
X    int lock_file_shared_nowait(fd)
X    int lock_file_exclusive_nowait(fd)
X    int unlock_file(fd)
X
X  All functions return 0 if successful, -1 if the requested lock would
X  block, and-2 on failure.  They set  the extern int lock_errno as follows:
X  return  lock_errno   explanation
X     0    LCK_OK       requested lock implemented
X
X    -1    LCK_BLOCK    the requested lock was blocked
X
X    -2    LCK_BADFD    fd is not a valid file descriptor
X    -2    LCK_DEADLOCK a deadlock was detected
X    -2    LCK_INTR     an interrupt occurred which aborted the system call
X    -2    LCK_OTHER    some other error - errno may give more information
X                       if you know how to interpret it
X
X  The extent of support depends on the system call used to implement
X  the actual locks.  The int lock_support contains the appropriate bitwise
X  OR of the following defines:
X    LCK_RECORD      1
X    LCK_SHARED      2
X    LCK_EXCLUSIVE   4
X    LCK_NO_BLOCK    8
X
X  The character pointer locking_style contains a user readable message
X  specifying the underlying locking mechanism.
X
X  Not all functions are available under all UNIX file locking schemes.
X  For example, flock style locking only locks entire files.  lockf style
X  locking does not support shared locks.  If a requested feature is not
X  supported, the least restrictive feature which does gives at least the
X  requested protection is used.
X
X*/
X/*
Xa convenient template
Xint lock_record_shared_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X}
X
Xint lock_record_exclusive_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X}
X
Xint lock_record_shared_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X}
X
Xint lock_record_exclusive_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X}
X
Xint unlock_record(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X}
X
Xint lock_file_shared_wait(fd)
Xint fd;
X{
X}
X
Xint lock_file_exclusive_wait(fd)
Xint fd;
X{
X}
X
Xint lock_file_shared_nowait(fd)
Xint fd;
X{
X}
X
Xint lock_file_exclusive_nowait(fd)
Xint fd;
X{
X}
X
Xint unlock_file(fd)
Xint fd;
X{
X}
X*/
X
X
X#include <sys/types.h>
X#include <errno.h>
Xextern int errno;
Xint lock_errno;
X#include "lock-file.h"
X#include "patchlevel.h"
X
Xstatic patchlevel = PATCHLEVEL;
X
X/* #define FCNTL_STYLE /* */
X/* #define FLOCK_STYLE /* */
X/* #define LOCKF_STYLE /* */
X
X/* default style is FCNTL_STYLE */
X#ifndef FCNTL_STYLE
X#ifndef FLOCK_STYLE
X#ifndef LOCKF_STYLE
X# define FCNTL_STYLE
X#endif
X#endif
X#endif
X
X#ifdef FCNTL_STYLE
X#include <fcntl.h>
X
Xint lock_support = LCK_RECORD | LCK_SHARED | LCK_EXCLUSIVE | LCK_NO_BLOCK;
Xstatic struct flock flock;
Xstatic int do_fcntl_lock();
X
Xchar *locking_style = "fcntl style file locking";
X
Xint lock_record_shared_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_fcntl_lock(fd, offset, len, F_RDLCK, F_SETLKW);
X}
X
Xint lock_record_exclusive_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_fcntl_lock(fd, offset, len, F_WRLCK, F_SETLKW);
X}
X
Xint lock_record_shared_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_fcntl_lock(fd, offset, len, F_RDLCK, F_SETLK);
X}
X
Xint lock_record_exclusive_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_fcntl_lock(fd, offset, len, F_WRLCK, F_SETLK);
X}
X
Xint unlock_record(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_fcntl_lock(fd, offset, len, F_UNLCK, F_SETLK);
X}
X
Xint lock_file_shared_wait(fd)
Xint fd;
X{
X  return do_fcntl_lock(fd, 0L, 0L, F_RDLCK, F_SETLKW);
X}
X
Xint lock_file_exclusive_wait(fd)
Xint fd;
X{
X  return do_fcntl_lock(fd, 0L, 0L, F_WRLCK, F_SETLKW);
X}
X
Xint lock_file_shared_nowait(fd)
Xint fd;
X{
X  return do_fcntl_lock(fd, 0L, 0L, F_RDLCK, F_SETLK);
X}
X
Xint lock_file_exclusive_nowait(fd)
Xint fd;
X{
X  return do_fcntl_lock(fd, 0L, 0L, F_WRLCK, F_SETLK);
X}
X
Xint unlock_file(fd)
Xint fd;
X{
X  return do_fcntl_lock(fd, 0L, 0L, F_UNLCK, F_SETLK);
X}
X
Xstatic int do_fcntl_lock(fd, offset, len, lock_cmd, set_cmd)
Xint fd;
Xoff_t offset;
Xoff_t len;
Xint lock_cmd;
Xint set_cmd;
X{
X  flock.l_type = lock_cmd;
X  flock.l_whence = 0;
X  flock.l_start = offset;
X  flock.l_len = len;
X  if (fcntl(fd, set_cmd, &flock)) {
X    switch (errno) {
X    case EACCES:
X    case EAGAIN:
X      lock_errno = LCK_BLOCK;
X      return -1;
X    case EBADF:
X      lock_errno = LCK_BADFD;
X      return -2;
X    case EDEADLK:
X      lock_errno = LCK_DEADLOCK;
X      return -2;
X    case EINTR:
X      lock_errno = LCK_INTR;
X      return -2;
X    default:
X      lock_errno = LCK_OTHER;
X      return -2;
X    }
X  }
X
X  lock_errno = LCK_OK;
X  return 0;
X}
X#endif
X
X#ifdef FLOCK_STYLE
X/* WARNING: this does not correctly handle the case where more than
X   one lock is applied to a file (or segments of a file) by a single
X   process and then released `a little at a time'.  The fix is straight
X   forward: build a list of lock count records indexed by fd's and
X   decrement when an unlock is requested.  But this doesn't work either
X   in the case that a file is dup'ed or reopenned.  So I have not bothered
X   to 'do it right', inasmuch as 'right' isn't.
X*/
X
X#include <sys/file.h>
X
Xchar *locking_style = "flock style file locking";
Xint lock_support = LCK_SHARED | LCK_EXCLUSIVE | LCK_NO_BLOCK;
Xstatic int do_flock();
X
Xint lock_record_shared_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_flock(fd, LOCK_SH);
X}
X
Xint lock_record_exclusive_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_flock(fd, LOCK_EX);
X}
X
Xint lock_record_shared_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_flock(fd, LOCK_SH | LOCK_NB);
X}
X
Xint lock_record_exclusive_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  return do_flock(fd, LOCK_EX | LOCK_NB);
X}
X
Xint unlock_record(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  return do_flock(fd, LOCK_UN);
X}
X
Xint lock_file_shared_wait(fd)
Xint fd;
X{
X  return do_flock(fd, LOCK_SH);
X}
X
Xint lock_file_exclusive_wait(fd)
Xint fd;
X{
X  return do_flock(fd, LOCK_EX);
X}
X
Xint lock_file_shared_nowait(fd)
Xint fd;
X{
X  return do_flock(fd, LOCK_SH | LOCK_NB);
X}
X
Xint lock_file_exclusive_nowait(fd)
Xint fd;
X{
X  return do_flock(fd, LOCK_EX | LOCK_NB);
X}
X
Xint unlock_file(fd)
Xint fd;
X{
X  return do_flock(fd, LOCK_UN);
X}
X
Xstatic int do_flock(fd, operation)
Xint fd;
Xint operation;
X{
X  if (flock(fd, operation)) {
X    switch (errno) {
X    case EWOULDBLOCK:
X      lock_errno = LCK_BLOCK;
X      return -1;
X    case EBADF:
X      lock_errno = LCK_BADFD;
X      return -2;
X    case EDEADLK:
X      lock_errno = LCK_DEADLOCK;
X      return -2;
X    case EINTR:
X      lock_errno = LCK_INTR;
X      return -2;
X    default:
X      lock_errno = LCK_OTHER;
X      return -2;
X    }
X  }
X
X  lock_errno = LCK_OK;
X  return 0;
X}
X#endif
X
X#ifdef LOCKF_STYLE
X#include <unistd.h>
X#ifndef lseek
Xlong lseek();
X#endif
X
Xchar *locking_style = "lockf style file locking";
Xint lock_support = LCK_RECORD | LCK_EXCLUSIVE | LCK_NO_BLOCK;
Xstatic int do_lockf();
X
Xint lock_record_shared_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  return do_lockf(fd, F_LOCK, offset, len, 0);
X}
X
Xint lock_record_exclusive_wait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  return do_lockf(fd, F_LOCK, offset, len, 0);
X}
X
Xint lock_record_shared_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  return do_lockf(fd, F_TLOCK, offset, len, 0);
X}
X
Xint lock_record_exclusive_nowait(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  return do_lockf(fd, F_TLOCK, offset, len, 0);
X}
X
Xint unlock_record(fd, offset, len)
Xint fd;
Xoff_t offset;
Xoff_t len;
X{
X  return do_lockf(fd, F_ULOCK, offset, len, 0);
X}
X
Xint lock_file_shared_wait(fd)
Xint fd;
X{
X  return do_lockf(fd, F_LOCK, 0L, 0L, 1);
X}
X
Xint lock_file_exclusive_wait(fd)
Xint fd;
X{
X  return do_lockf(fd, F_LOCK, 0L, 0L, 1);
X}
X
Xint lock_file_shared_nowait(fd)
Xint fd;
X{
X  return do_lockf(fd, F_TLOCK, 0L, 0L, 1);
X}
X
Xint lock_file_exclusive_nowait(fd)
Xint fd;
X{
X  return do_lockf(fd, F_TLOCK, 0L, 0L, 1);
X}
X
Xint unlock_file(fd)
Xint fd;
X{
X  return do_lockf(fd, F_ULOCK, 0L, 0L, 1);
X}
X
Xstatic int do_lockf(fd, command, offset, len, original_offset_flag)
Xint fd;
Xint command;
Xoff_t offset;
Xoff_t len;
Xint original_offset_flag;
X{
X  int ret;
X  off_t original_offset;
X  long lseek();
X
X  if (original_offset_flag) {
X    if ((original_offset = (off_t)lseek(fd, 0L, 1)) < 0)
X      original_offset_flag = 0;
X  }
X  if (len < 0) {
X    offset += len;
X    len = -len;
X  }
X  if (lseek(fd, offset, 0) < 0) {
X    lock_errno = errno == EBADF ? LCK_BADFD : LCK_OTHER;
X    return -2;
X  }
X  if (lockf(fd, command, len)) {
X    switch (errno) {
X    case EACCES:
X      lock_errno = LCK_BLOCK;
X      ret = -1;
X      break;
X    case EBADF:
X      lock_errno = LCK_BADFD;
X      ret = -2;
X      break;
X    case EDEADLK:
X      lock_errno = LCK_DEADLOCK;
X      ret = -2;
X      break;
X    case EINTR:
X      lock_errno = LCK_INTR;
X      ret = -2;
X      break;
X    default:
X      lock_errno = LCK_OTHER;
X      ret = -2;
X      break;
X    }
X  }
X  else {
X    lock_errno = LCK_OK;
X    ret = 0;
X  }
X  if (original_offset_flag)
X    lseek(fd, original_offset, 0);
X
X  return ret;
X}
X#endif
END_OF_FILE
if test 11646 -ne `wc -c <'lock-file.c'`; then
    echo shar: \"'lock-file.c'\" unpacked with wrong size!
fi
# end of 'lock-file.c'
fi
if test -f 'lock-file.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lock-file.h'\"
else
echo shar: Extracting \"'lock-file.h'\" \(1687 characters\)
sed "s/^X//" >'lock-file.h' <<'END_OF_FILE'
X/* @(#)lock-file.h	1.1 91/01/24 */
X/* copyright (c) Mike Howard & Miller/Howard Investments, Inc. 1990,
X   all rights reseved */
X
X/* lock_errno values */
X#define LCK_OK       0
X#define LCK_BLOCK    1  /* the requested lock was blocked */
X#define LCK_BADFD    2  /* fd is not a valid file descriptor */
X#define LCK_DEADLOCK 3  /* a deadlock was detected */
X#define LCK_INTR     4  /* an interupt aborted system call */
X#define LCK_OTHER    9  /* some other error - errno may give more information
X                           if you know how to interpret it */
X
X/* lock_support values */
X#define LCK_RECORD      1
X#define LCK_SHARED      2
X#define LCK_EXCLUSIVE   4
X#define LCK_NO_BLOCK    8
X
X#ifndef lock_errno
Xextern int lock_errno;
Xextern int lock_support;
X#endif
X
X#ifndef off_t
X#define off_t long
X#define RM_OFF_T_DEF
X#endif
X#ifdef PROTOTYPES_OK
Xint lock_record_shared_wait(int fd, off_t offset, off_t len);
Xint lock_record_exclusive_wait(int fd, off_t offset, off_t len);
Xint lock_record_shared_nowait(int fd, off_t offset, off_t len);
Xint lock_record_exclusive_nowait(int fd, off_t offset, off_t len);
Xint unlock_record(int fd, off_t offset, off_t len);
Xint lock_file_shared_wait(int fd);
Xint lock_file_exclusive_wait(int fd);
Xint lock_file_shared_nowait(int fd);
Xint lock_file_exclusive_nowait(int fd);
Xint unlock_file(int fd);
X#else
Xint lock_record_shared_wait();
Xint lock_record_exclusive_wait();
Xint lock_record_shared_nowait();
Xint lock_record_exclusive_nowait();
Xint unlock_record();
Xint lock_file_shared_wait();
Xint lock_file_exclusive_wait();
Xint lock_file_shared_nowait();
Xint lock_file_exclusive_nowait();
Xint unlock_file();
X#endif
X
X#ifdef RM_OFF_T_DEF
X# undef off_t
X#endif
END_OF_FILE
if test 1687 -ne `wc -c <'lock-file.h'`; then
    echo shar: \"'lock-file.h'\" unpacked with wrong size!
fi
# end of 'lock-file.h'
fi
if test -f 'lock-file.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lock-file.man'\"
else
echo shar: Extracting \"'lock-file.man'\" \(3599 characters\)
sed "s/^X//" >'lock-file.man' <<'END_OF_FILE'
X.	\"	-*- nroff -*-
X.TH LOCK-FILE 3 "local"
X.SH NAME
Xlock-file \- implementation independent file locking primitives
X.SH SYNOPSIS
X.PP
X#include <lock-file.h>
X.PP
X.B int lock_record_shared_wait(fd, offset, len)
X.PP
X.B int lock_record_exclusive_wait(fd, offset, len)
X.PP
X.B int lock_record_shared_nowait(fd, offset, len)
X.PP
X.B int lock_record_exclusive_nowait(fd, offset, len)
X.PP
X.B int unlock_record(fd, offset, len)
X.PP
X.B int lock_file_shared_wait(fd)
X.PP
X.B int lock_file_exclusive_wait(fd)
X.PP
X.B int lock_file_shared_nowait(fd)
X.PP
X.B int lock_file_exclusive_nowait(fd)
X.PP
X.B int unlock_file(fd)
X.SH DESCRIPTION
X.PP
XThese are convenience routines which implement file locking in standard
Xways regardless of the method selected within the particular operating
Xsystem.
X.PP
XNote: for record locks, the current file position is changed to the
Xbeginning of the locked region.  For whole file locks, the current file
Xposition is not changed.
X.PP
XIn order to use buffered i/o, you have to open the file using open(),
Xfdopen() to get a FILE *, and then fflush prior to releasing locks.
X.PP
XIn all cases, the parameters are:
X.IP
X.B fd
X\- int \- file descriptor
X.IP
X.B offset 
X\- off_t \- offset from the beginning of file to begin locking region.
X.IP
X.B len
X\- off_t \- length of locked region.
X.PP
XThe functions do what their names say that they do.
X.PP
XAll functions return 0 if successful, -1 if the requested lock would
Xblock, and -2 on failure.  They set the
X.B extern int lock_errno
Xas follows:
X.sp
X.TS
Xbox tab(;) ;
Xc l l
Xn l l.
XRet Code;lock_errno Value;Interpretation
X_
X0;LCK_OK;requested lock implemented
X-1;LCK_BLOCK;the requested lock was blocked
X-2;LCK_BADFD;fd is not a valid file descriptor
X-2;LCK_DEADLOCK;deadlock was detected
X-2;LCK_INTR; an interrupt occurred which aborted the system call
X-2;LCK_OTHER;some other error - errno may give more information
X;;if you know how to interpret it
X.TE
X.PP
XSince all locking schemes are not equal, some of the functionality
Xexpected of these routines is not implemented.  For example, lockf()
Xstyle locking does not support shared locks, nor does it support
Xlocking without write permission on the named file.  The routines,
Xlock_..._shared...() routines still attempt to lock, but by promoting
Xto exclusive locks.  See
X.B BUGS
Xbelow for details.  Two values are defined in order to determine the
Xfeatures you actually have: the
X.B int lock_support
Xand the string
X.B locking_style.
X.B locking_style
Xpoints to a string of the form ``fcntl style file locking''.  The
Xvalue of
X.B lock_support
Xis the bitwise OR of:
X.sp
X.TS
Xbox tab(;);
Xl n l.
XDefine;Value;Interpretation
X_
XLCK_RECORD;1;Record Locking supported
XLCK_SHARED;2;Shared locks supported
XLCK_EXCLUSIVE;4;exclusive locks supported
XLCK_NO_BLOCK;8;nowait locking calls supported
X.TE
X.SH FILES
X.PP
X.I lock-file.h
X.SH "SEE ALSO"
Xfcntl(2), flock(2), and lockf(2)
X.SH DIAGNOSTICS
Xsee discussion above.
X.SH BUGS
X.PP
XFull functionality works
X.I only
Xwhen using FCNTL style locking.
X.PP
XFLOCK does not support record level locking.  Record level locks are
Xpromoted to file level.
X.B Warning:
Xthis means that the first record level lock released release
X.I all
Xpending locks.  I realize that this is not good, but it isn't
Xpractical to do much about it without encapsulating
X.I all
Xi/o calls, including dup() and dup2().
X.PP
XLOCKF does not support shared locks, nor does it support any locking
Xfor files opened read only.  Shared locks are promoted to exclusive
Xlocks.  Lock attempts where the user does not have write permission on
Xthe file fail with EBADF (from lockf) and LCK_BADFD from lock_...().
END_OF_FILE
if test 3599 -ne `wc -c <'lock-file.man'`; then
    echo shar: \"'lock-file.man'\" unpacked with wrong size!
fi
# end of 'lock-file.man'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(22 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define PATCHLEVEL  0
END_OF_FILE
if test 22 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'test-lock.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'test-lock.c'\"
else
echo shar: Extracting \"'test-lock.c'\" \(7646 characters\)
sed "s/^X//" >'test-lock.c' <<'END_OF_FILE'
X/* @(#)test-lock.c	1.2 91/01/24 */
X/* copyright 1991, Mike Howard & Miller/Howard Investments, Inc.
X   all rights reserved */
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/wait.h>
X#include <sys/stat.h>
X#include "lock-file.h"
X
X#ifdef FCNTL_STYLE
Xextern char *locking_style;
Xextern int lock_errno;
X#endif
X
X#ifdef FLOCK_STYLE
Xextern char *locking_style;
Xextern int lock_errno;
X#endif
X
X#ifdef LOCKF_STYLE
Xextern char *locking_style;
Xextern int lock_errno;
X#endif
X
X#ifndef FCNTL_STYLE
X#ifndef FLOCK_STYLE
X#ifndef LOCKF_STYLE
X# define lock_record_shared_wait(fd, offset, len) 0
X# define lock_record_exclusive_wait(fd, offset, len) 0
X# define lock_record_shared_nowait(fd, offset, len) 0
X# define lock_record_exclusive_nowait(fd, offset, len) 0
X# define unlock_record(fd, offset, len) 0
X# define lock_file_shared_wait(fd) 0
X# define lock_file_exclusive_wait(fd) 0
X# define lock_file_shared_nowait(fd) 0
X# define lock_file_exclusive_nowait(fd) 0
X# define unlock_file(fd) 0
Xchar *locking_style = "no locking";
Xint lock_support;
Xint lock_errno;
X#endif
X#endif
X#endif
X
X/* #define USE_BUFFERED /* */
X#ifdef USE_BUFFERED
Xchar *io_style = "buffered i/o with fflush()ing";
XFILE *file;
X#else
Xchar *io_style = "raw i/o";
X#endif
X
Xint fd;
Xint reps = 50;
Xint child_pid;
Xint parent_pid;
Xint corruptions;
Xchar *parent_fmt = "parent[%d]: ";
Xchar *child_fmt = "child[%d]: ";
Xchar parent_text[80];
Xchar child_text[80];
Xint exit_code;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X  fprintf(stdout, "%s %s - pid: %d\n", argv[0], argc > 1 ? argv[1] : "",
X	  getpid());
X  sprintf(parent_text, parent_fmt, parent_pid = getpid());
X  fprintf(stdout, "%s%s - %s\n", parent_text, locking_style, io_style);
X  fprintf(stdout, "%s lock_support: 0x%0x\n", parent_text, lock_support);
X  fflush(stdout);
X
X  if (argc > 1) {
X    if ((reps = atoi(argv[1])) < 1) {
X      fprintf(stderr, "%s [reps (>= 1)]\n", argv[0]);
X      exit(1);
X    }
X  }
X  if ((fd = open("test-tmp", O_RDWR | O_CREAT | O_TRUNC, 0666)) < 0) {
X    fprintf(stderr, "cannot open test-tmp file\n");
X    exit(1);
X  }
X#ifdef USE_BUFFERED
X  file = fdopen(fd, "r+");
X#endif
X
X  /* exclusive file lock test */
X  if (child_pid = fork())
X    do_parent_append_test();
X  else {
X    do_child_append_test();
X    exit(0);
X  }
X  wait_for_child(child_pid);
X
X  /* record lock test */
X  if (child_pid = fork())
X    do_parent_record_lock_test();
X  else {
X    do_child_record_lock_test();
X    exit(0);
X  }
X  wait_for_child(child_pid);
X
X#ifdef USE_BUFFERED
X  fclose(file);
X#endif
X  close(fd);
X  exit(exit_code);
X}
X
Xwait_for_child(child_pid)
Xint child_pid;
X{
X  int i;
X  int status;
X
X  while ((i = wait(&status)) != child_pid && i != -1)
X    ;
X}
X
X/* exclusive file lock test */
X
Xdo_parent_append_test()
X{
X  int i;
X  int count = 0;
X  int status;
X  int parent_reps;
X  int child_reps;
X
X  if ((parent_reps = reps / 2 > 50 ? 50 : reps / 2) < 1)
X    parent_reps = 1;
X  if ((child_reps = reps / 5 > 20 ? 20 : reps / 5) < 1)
X    child_reps = 1;
X  
X  sprintf(child_text, child_fmt, child_pid);
X
X  fprintf(stdout, "%sreps: %d, parent_reps: %d, child_reps: %d\n",
X	  parent_text, reps, parent_reps, child_reps);
X  fflush(stdout);
X  for (i=0;i<reps;i+=parent_reps) {
X    write_a_line("%s%d\n", parent_text, count++, parent_reps);
X    printf("%s%d corrupt sequences found\n", parent_text,
X	   corruptions += read_the_file(parent_text));
X  }
X
X  exit_code = corruptions;
X}
X
Xdo_child_append_test()
X{
X  int i;
X  int count = 0;
X  int child_reps;
X
X  sprintf(child_text, child_fmt, getpid());
X  close(fd);
X  if ((fd = open("test-tmp", O_RDWR)) < 0) {
X    fprintf(stderr, "child: cannot open test-tmp\n");
X    exit(1);
X  }
X  if ((child_reps = reps / 5 > 20 ? 20 : reps / 5) < 1)
X    child_reps = 1;
X  child_pid = getpid();
X  for (i=0;i<reps;i+=child_reps)
X    write_a_line("%s%d\n", child_text, count++, child_reps);
X}
X
Xwrite_a_line(fmt, text, msg, reps)
Xchar *fmt;
Xchar *text;
Xint msg;
Xint reps;
X{
X  char buf[256];
X  int i;
X
X  if (i = lock_file_exclusive_wait(fd)) {
X    fprintf(stdout, "%slock_file_exclusive_wait() failed: %d, lock_errno: %d\n",
X	    text, i, lock_errno);
X    return;
X  }
X  sprintf(buf, fmt, text, msg);
X  fprintf(stdout, fmt, text, msg);
X  fflush(stdout);
X#ifdef USE_BUFFERED
X  fseek(file, 0L, 2);
X  for (i=0;i<reps;i++) {
X    fprintf(file, fmt, text, msg);
X    fflush(file);
X    sleep(1);
X  }
X#else  /* USE_BUFFERED */
X  lseek(fd, 0L, 2);
X  sprintf(buf, fmt, text, msg);
X  for (i=0;i<reps;i++) {
X    write(fd, buf, strlen(buf));
X    sleep(1);
X  }
X#endif /* USE_BUFFERED */
X  unlock_file(fd);
X}
X
Xread_the_file(s)
Xchar *s;
X{
X  char buf[256];
X  int i;
X  int corruptions = 0;
X
X  if (i = lock_file_shared_wait(fd)) {
X    fprintf(stdout, "lock_file_shared_wait() failed: %d, lock_errno: %d\n",
X	    i, lock_errno);
X    return;
X  }
X  fprintf(stdout, "%s starting to read file\n", s);
X  fflush(stdout);
X#ifdef USE_BUFFERED
X  fseek(file, 0L, 0);
X  while (fgets(buf, 256, file) && !feof(file))
X    corruptions += count_corruptions(buf, strlen(buf));
X#else  /* USE_BUFFERED */
X  lseek(fd, 0L, 0);
X  while ((i = read(fd, buf, 256)) > 0)
X    corruptions += count_corruptions(buf, i);
X#endif /* USE_BUFFERED */
X  fprintf(stdout, "%s finished reading file\n", s);
X  fflush(stdout);
X  unlock_file(fd);
X
X  return corruptions;
X}
X
X#define START  0
X#define PARENT 1
X#define CHILD  2
X#define NUM    3
X
Xcount_corruptions(s, len)
Xchar *s;
Xint len;
X{
X  static int state = START;
X  static int idx = 0;
X  int count = 0;
X  char c;
X
X  while (len-- > 0) {
X    c = *s++;
X    switch (state) {
X    case START:
X      switch (c) {
X      case 'p':
X	state = PARENT;
X	idx = 1;
X	break;
X      case 'c':
X	state = CHILD;
X	idx = 1;
X	break;
X      default:
X	break;
X      }
X      break;
X    case PARENT:
X      if (c != parent_text[idx++]) {
X	count++;
X	state = START;
X      }
X      else {
X	if (idx == strlen(parent_text))
X	  state = NUM;
X      }
X      break;
X    case CHILD:
X      if (c != child_text[idx++]) {
X	count++;
X	state = START;
X      }
X      else {
X	if (idx == strlen(child_text))
X	  state = NUM;
X      }
X      break;
X    case NUM:
X      if (c == '\n')
X	state = START;
X      else if (c < '0' || c > '9') {
X	count++;
X	state = START;
X      }
X      break;
X    }
X  }
X
X  return count;
X}
X
X/* record lock test - child locks the middle third of the file
X   and then sleeps for 5 seconds; the parent sleeps for 2 seconds
X   and then tests for a shared lock on the middle third */
X
Xdo_parent_record_lock_test()
X{
X  int i;
X  struct stat stat_buf;
X  off_t offset;
X
X  sprintf(parent_text, parent_fmt, parent_pid = getpid());
X
X  fprintf(stdout, "%srecord lock test: %s - %s\n",
X	  parent_text, locking_style, io_style);
X  fflush(stdout);
X  fstat(fd, &stat_buf);
X  offset = stat_buf.st_size / 3;
X  sleep(2);
X  printf("%s lock_record_shared_nowait(%d, %ld, %ld): %d\n",
X	 parent_text, fd, offset, offset,
X	 i = lock_record_shared_nowait(fd, offset, offset));
X  printf("%s record lock test %s lock_errno: %d (should be %d)\n",
X	 parent_text, i ? "passed" : "failed", lock_errno,
X	 LCK_BLOCK);
X  if (!i) {
X    unlock_record(fd, offset, offset);
X    exit_code++;
X  }
X}
X
Xdo_child_record_lock_test()
X{
X  struct stat stat_buf;
X  off_t offset;
X
X  sprintf(child_text, child_fmt, getpid());
X  close(fd);
X  if ((fd = open("test-tmp", O_RDWR)) < 0) {
X    fprintf(stderr, "%s cannot open test-tmp\n", child_text);
X    exit(1);
X  }
X  fstat(fd, &stat_buf);
X  offset = stat_buf.st_size/3;
X
X  if (lock_record_exclusive_nowait(fd, offset, offset)) {
X    printf("%s lock_record_exclusive_nowait(%d, %ld, %ld) failed: lock_errno: %d\n",
X	   child_text, fd, offset, offset, lock_errno);
X    return(2);
X  }
X  sleep(5);
X  unlock_record(fd, offset, offset);
X
X  return 0;
X}
END_OF_FILE
if test 7646 -ne `wc -c <'test-lock.c'`; then
    echo shar: \"'test-lock.c'\" unpacked with wrong size!
fi
# end of 'test-lock.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
================================cut here================================

Mike Howard
how%milhow1 at uunet.uu.net
-- 
Mike Howard
uunet!milhow1!how or how%milhow1 at uunet.uu.net



More information about the Alt.sources mailing list