how@milhow1.UU.NET (Mike Howard) (01/25/91)
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@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@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@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@uunet.uu.net -- Mike Howard uunet!milhow1!how or how%milhow1@uunet.uu.net