/* ***************************************************************************** * * $RCSfile: lockutil.c,v $ * $Date: 1999/04/05 19:11:39 $ * $Source: /home/richard/Xml/RCS/lockutil.c,v $ * $Revision: 1.7 $ * $Author: richard $ * ***************************************************************************** * * Copyright 1998, Brown University and Richard Goerwitz * ***************************************************************************** * * File-locking utilities. They provide access to fopen(), with * mandatory locking,if the OS has it enabled, or advisory locking, * if it doesn't: * * fopen_and_readlock (char *fname) - open fname, shared lock it * fopen_and_writelock (char *fname) - open fname, exclusively lock it * fclose_and_unlock (char *fname) - close fname, unlock it * * If any attempt is made at locking a file, DUMMY_FILE_NAME (see * lockutil.h), the file will be opened. But no lock will be set. * Normally, DUMMY_FILE_NAME is set to "/dev/null". * ***************************************************************************** */ #include "general.h" #include "lockutil.h" #include "errabort.h" #include #include #ifdef HAVE_FLOCK # include #endif static int open_and_readlock (char *, int); static int open_and_writelock (char *, int); static int close_and_unlock (int); FILE * fopen_and_readlock (char *filename) { int fd; if ((fd = open_and_readlock (filename, O_RDONLY)) == -1) return NULL; return fdopen (fd, "r"); } FILE * fopen_and_writelock (char *filename) { int fd; if ((fd = open_and_writelock (filename, O_WRONLY)) == -1) return NULL; return fdopen (fd, "w"); } int fclose_and_unlock (FILE *f) { int fd; #ifndef HAVE_FLOCK struct flock fbuf; #endif #ifdef HAVE_FLOCK fd = fileno (f); flock (fd, LOCK_UN); #else fd = fileno (f); memset (&fbuf, 0, sizeof (struct flock)); fbuf.l_type = F_UNLCK; fbuf.l_whence = SEEK_SET; fcntl (fd, F_SETLK, &fbuf); #endif /* HAVE_FLOCK */ return fclose (f); } static int open_and_readlock (char *filename, int filemode) { char wd[MAXPATHLEN]; #ifndef HAVE_FLOCK struct flock fbuf; #endif int fd, count, sleeptime; if (xmlparse_env.debug_level >= 7) { xwrap (errdebug (7, "attempting to open %s (mode %o)\n", filename, filemode)); if (getcwd (wd, MAXPATHLEN) != NULL) xwrap (errdebug (7, "current working directory is %s\n", wd)); } if ((fd = open (filename, filemode)) == -1) return -1; /* don't bother locking dummy file (usually = /dev/null) */ if (strcoll (filename, DUMMY_FILE_NAME) == 0) return fd; else { for (count = 0; count < 6; count++) { errno = 0; #ifdef HAVE_FLOCK if (flock (fd, LOCK_SH | LOCK_NB) == 0) return fd; #else memset (&fbuf, 0, sizeof (struct flock)); fbuf.l_type = F_RDLCK; fbuf.l_whence = SEEK_SET; if (fcntl (fd, F_SETLK, &fbuf) == 0) return fd; #endif /* HAVE_FLOCK */ switch (errno) { case EWOULDBLOCK: case EACCES: #ifdef STANDALONE_LOCKUTIL_TEST close (fd); return -1; #endif /* somebody else locked it; sleep 1-5 seconds */ sleeptime = 1 + (int)(5.0 * rand() / (RAND_MAX + 1.0)); sleep (sleeptime); break; case EBADF: default: /* serious error; give up */ close (fd); return -1; } } close (fd); /* This is a fatal error */ errabort (14, "lock failed to release on %s\n", filename); return -1; } } static int open_and_writelock (char *filename, int filemode) { #ifndef HAVE_FLOCK struct flock fbuf; #endif int fd, count, sleeptime; /* Must be able to write to filename to lock it for writing */ if ((filemode & 0x03) == O_RDONLY) filemode = (filemode & ~0x03) | O_RDWR; if ((fd = open (filename, filemode)) == -1) return -1; /* don't bother locking dummy file (usually = /dev/null) */ if (strcoll (filename, DUMMY_FILE_NAME) == 0) return fd; else { for (count = 0; count < 6; count++) { errno = 0; #ifdef HAVE_FLOCK if (flock (fd, LOCK_EX | LOCK_NB) == 0) return fd; #else memset (&fbuf, 0, sizeof (struct flock)); fbuf.l_type = F_WRLCK; fbuf.l_whence = SEEK_SET; if (fcntl (fd, F_SETLK, &fbuf) == 0) return fd; #endif /* HAVE_FLOCK */ switch (errno) { case EWOULDBLOCK: case EACCES: #ifdef STANDALONE_LOCKUTIL_TEST close (fd); return -1; #endif /* somebody else locked it; sleep 1-5 seconds */ sleeptime = 1 + (int)(5.0 * rand() / (RAND_MAX + 1.0)); sleep (sleeptime); continue; case EBADF: default: /* serious error; give up */ close (fd); return -1; } } close (fd); /* This is a fatal error */ errabort (14, "lock failed to release on %s\n", filename); return -1; } } int close_and_unlock (int fd) { #ifdef HAVE_FLOCK flock (fd, LOCK_UN); #else struct flock fbuf; memset (&fbuf, 0, sizeof (struct flock)); fbuf.l_type = F_UNLCK; fbuf.l_whence = SEEK_SET; fcntl (fd, F_SETLK, &fbuf); #endif /* HAVE_FLOCK */ return close (fd); } #ifdef STANDALONE_LOCKUTIL_TEST #include "readcfg.h" #include xmlparse_environment xmlparse_env; int main (int argc, char **argv) { int i; FILE *f; readcfg (argc, argv); if (fork()) { if ((f = fopen_and_writelock ("Test/test.tmp")) == NULL) fprintf (stderr, "parent: write lock failed (this is bad)\n"); else fprintf (stderr, "parent: write lock succeeded (this is good)\n"); fprintf (f, "hello!\n"); sleep (3); fprintf (stderr, "return status of fclose_and_unlock() == %d\n", fclose_and_unlock (f)); } else { sleep (2); if ((f = fopen_and_writelock ("Test/test.tmp")) == NULL) fprintf (stderr, "child: write lock failed (this is good)\n"); else { fprintf (stderr, "child: write lock succeeded (this is bad)\n"); i = fprintf (f, "goodbye!\n"); fprintf (stderr, "(child) return status of fprintf == %d\n", i); } exit (0); } wait (&i); return 0; } #endif /* STANDALONE_LOCKUTIL_TEST */