-
/*
* lock.c -- routines to lock/unlock files
*
- * $Id$
+ * This code is Copyright (c) 2002, by the authors of nmh. See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
*/
/* Modified by Ruud de Rooij to support Miquel van Smoorenburg's liblockfile
*
* Ruud de Rooij <ruud@debian.org> Sun, 28 Mar 1999 15:34:03 +0200
*/
-
+
#include <h/mh.h>
#include <h/signals.h>
+#include <h/utils.h>
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <errno.h>
#endif
-#ifdef MMDFONLY
-# include <mmdfonly.h>
-# include <lockonly.h>
-#endif /* MMDFONLY */
-
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#else
#include <lockfile.h>
#endif
-extern int errno;
-
#ifdef LOCKDIR
char *lockdir = LOCKDIR;
#endif
/* struct for getting name of lock file to create */
struct lockinfo {
- char curlock[BUFSIZ];
+ char curlock[BUFSIZ];
#if !defined(HAVE_LIBLOCKFILE)
- char tmplock[BUFSIZ];
+ char tmplock[BUFSIZ];
#endif
};
* Amount of time to wait before
* updating ctime of lock file.
*/
-#define NSECS 20
+#define NSECS 20
#if !defined(HAVE_LIBLOCKFILE)
/*
/* struct for recording and updating locks */
struct lock {
- int l_fd;
- char *l_lock;
- struct lock *l_next;
+ int l_fd;
+ char *l_lock;
+ struct lock *l_next;
};
/* top of list containing all open locks */
#ifdef DOT_LOCKING
static int lkopen_dot (char *, int, mode_t);
-static int lockit (struct lockinfo *);
static void lockname (char *, struct lockinfo *, int);
static void timerON (char *, int);
static void timerOFF (int);
static RETSIGTYPE alrmser (int);
#endif
+#if !defined(HAVE_LIBLOCKFILE)
+static int lockit (struct lockinfo *);
+#endif
/*
* Base routine to open and lock a file,
lkopen (char *file, int access, mode_t mode)
{
#ifdef KERNEL_LOCKING
- return lkopen_kernel(file, access, mode);
+ return lkopen_kernel(file, access, mode);
#endif
#ifdef DOT_LOCKING
- return lkopen_dot(file, access, mode);
+ return lkopen_dot(file, access, mode);
#endif
}
lkclose (int fd, char *file)
{
#ifdef FCNTL_LOCKING
- struct flock buf;
+ struct flock buf;
#endif
#ifdef DOT_LOCKING
- struct lockinfo lkinfo;
+ struct lockinfo lkinfo;
#endif
- if (fd == -1)
- return 0;
+ if (fd == -1)
+ return 0;
#ifdef FCNTL_LOCKING
- buf.l_type = F_UNLCK;
- buf.l_whence = SEEK_SET;
- buf.l_start = 0;
- buf.l_len = 0;
- fcntl(fd, F_SETLK, &buf);
+ buf.l_type = F_UNLCK;
+ buf.l_whence = SEEK_SET;
+ buf.l_start = 0;
+ buf.l_len = 0;
+ fcntl(fd, F_SETLK, &buf);
#endif
#ifdef FLOCK_LOCKING
- flock (fd, LOCK_UN);
+ flock (fd, LOCK_UN);
#endif
#ifdef LOCKF_LOCKING
- /* make sure we unlock the whole thing */
- lseek (fd, (off_t) 0, SEEK_SET);
- lockf (fd, F_ULOCK, 0L);
-#endif
+ /* make sure we unlock the whole thing */
+ lseek (fd, (off_t) 0, SEEK_SET);
+ lockf (fd, F_ULOCK, 0L);
+#endif
#ifdef DOT_LOCKING
- lockname (file, &lkinfo, 0); /* get name of lock file */
+ lockname (file, &lkinfo, 0); /* get name of lock file */
#if !defined(HAVE_LIBLOCKFILE)
- unlink (lkinfo.curlock); /* remove lock file */
+ unlink (lkinfo.curlock); /* remove lock file */
#else
- lockfile_remove(lkinfo.curlock);
+ lockfile_remove(lkinfo.curlock);
#endif /* HAVE_LIBLOCKFILE */
- timerOFF (fd); /* turn off lock timer */
+ timerOFF (fd); /* turn off lock timer */
#endif /* DOT_LOCKING */
- return (close (fd));
+ return (close (fd));
}
FILE *
lkfopen (char *file, char *mode)
{
- int fd, access;
- FILE *fp;
-
- if (strcmp (mode, "r") == 0)
- access = O_RDONLY;
- else
- access = O_RDWR;
+ int fd, access;
+ FILE *fp;
+
+ if (strcmp (mode, "r") == 0)
+ access = O_RDONLY;
+ else if (strcmp (mode, "r+") == 0)
+ access = O_RDWR;
+ else if (strcmp (mode, "w") == 0)
+ access = O_WRONLY | O_CREAT | O_TRUNC;
+ else if (strcmp (mode, "w+") == 0)
+ access = O_RDWR | O_CREAT | O_TRUNC;
+ else if (strcmp (mode, "a") == 0)
+ access = O_WRONLY | O_CREAT | O_APPEND;
+ else if (strcmp (mode, "a+") == 0)
+ access = O_RDWR | O_CREAT | O_APPEND;
+ else {
+ errno = EINVAL;
+ return NULL;
+ }
- if ((fd = lkopen (file, access, 0)) == -1)
- return NULL;
+ if ((fd = lkopen (file, access, 0666)) == -1)
+ return NULL;
- if ((fp = fdopen (fd, mode)) == NULL) {
- close (fd);
- return NULL;
- }
+ if ((fp = fdopen (fd, mode)) == NULL) {
+ close (fd);
+ return NULL;
+ }
- return fp;
+ return fp;
}
lkfclose (FILE *fp, char *file)
{
#ifdef FCNTL_LOCKING
- struct flock buf;
+ struct flock buf;
#endif
#ifdef DOT_LOCKING
- struct lockinfo lkinfo;
+ struct lockinfo lkinfo;
#endif
- if (fp == NULL)
- return 0;
+ if (fp == NULL)
+ return 0;
#ifdef FCNTL_LOCKING
- buf.l_type = F_UNLCK;
- buf.l_whence = SEEK_SET;
- buf.l_start = 0;
- buf.l_len = 0;
- fcntl(fileno(fp), F_SETLK, &buf);
+ buf.l_type = F_UNLCK;
+ buf.l_whence = SEEK_SET;
+ buf.l_start = 0;
+ buf.l_len = 0;
+ fcntl(fileno(fp), F_SETLK, &buf);
#endif
#ifdef FLOCK_LOCKING
- flock (fileno(fp), LOCK_UN);
+ flock (fileno(fp), LOCK_UN);
#endif
#ifdef LOCKF_LOCKING
- /* make sure we unlock the whole thing */
- fseek (fp, 0L, SEEK_SET);
- lockf (fileno(fp), F_ULOCK, 0L);
+ /* make sure we unlock the whole thing */
+ fseek (fp, 0L, SEEK_SET);
+ lockf (fileno(fp), F_ULOCK, 0L);
#endif
#ifdef DOT_LOCKING
- lockname (file, &lkinfo, 0); /* get name of lock file */
+ lockname (file, &lkinfo, 0); /* get name of lock file */
#if !defined(HAVE_LIBLOCKFILE)
- unlink (lkinfo.curlock); /* remove lock file */
+ unlink (lkinfo.curlock); /* remove lock file */
#else
- lockfile_remove(lkinfo.curlock);
+ lockfile_remove(lkinfo.curlock);
#endif /* HAVE_LIBLOCKFILE */
- timerOFF (fileno(fp)); /* turn off lock timer */
+ timerOFF (fileno(fp)); /* turn off lock timer */
#endif /* DOT_LOCKING */
- return (fclose (fp));
+ return (fclose (fp));
}
static int
lkopen_kernel (char *file, int access, mode_t mode)
{
- int fd, i, j;
+ int fd, i, j;
# ifdef FCNTL_LOCKING
- struct flock buf;
+ struct flock buf;
# endif /* FCNTL_LOCKING */
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < 5; i++) {
# if defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
- /* remember the original mode */
- j = access;
-
- /* make sure we open at the beginning */
- access &= ~O_APPEND;
-
- /*
- * We MUST have write permission or
- * lockf/fcntl() won't work
- */
- if ((access & 03) == O_RDONLY) {
- access &= ~O_RDONLY;
- access |= O_RDWR;
- }
+ /* remember the original mode */
+ j = access;
+
+ /* make sure we open at the beginning */
+ access &= ~O_APPEND;
+
+ /*
+ * We MUST have write permission or
+ * lockf/fcntl() won't work
+ */
+ if ((access & 03) == O_RDONLY) {
+ access &= ~O_RDONLY;
+ access |= O_RDWR;
+ }
# endif /* LOCKF_LOCKING || FCNTL_LOCKING */
- if ((fd = open (file, access | O_NDELAY, mode)) == -1)
- return -1;
+ if ((fd = open (file, access | O_NDELAY, mode)) == -1)
+ return -1;
# ifdef FCNTL_LOCKING
- buf.l_type = F_WRLCK;
- buf.l_whence = SEEK_SET;
- buf.l_start = 0;
- buf.l_len = 0;
- if (fcntl (fd, F_SETLK, &buf) != -1)
- return fd;
+ buf.l_type = F_WRLCK;
+ buf.l_whence = SEEK_SET;
+ buf.l_start = 0;
+ buf.l_len = 0;
+ if (fcntl (fd, F_SETLK, &buf) != -1)
+ return fd;
# endif
# ifdef FLOCK_LOCKING
- if (flock (fd, LOCK_EX | LOCK_NB) != -1)
- return fd;
+ if (flock (fd, (((access & 03) == O_RDONLY) ? LOCK_SH :
+ LOCK_EX) | LOCK_NB) != -1)
+ return fd;
# endif
# ifdef LOCKF_LOCKING
- if (lockf (fd, F_TLOCK, 0L) != -1) {
- /* see if we should be at the end */
- if (j & O_APPEND)
- lseek (fd, (off_t) 0, SEEK_END);
- return fd;
- }
+ if (lockf (fd, F_TLOCK, 0L) != -1) {
+ /* see if we should be at the end */
+ if (j & O_APPEND)
+ lseek (fd, (off_t) 0, SEEK_END);
+ return fd;
+ }
# endif
- j = errno;
- close (fd);
- sleep (5);
- }
+ j = errno;
+ close (fd);
+ sleep (5);
+ }
- close (fd);
- errno = j;
- return -1;
+ close (fd);
+ errno = j;
+ return -1;
}
#endif /* KERNEL_LOCKING */
static int
lkopen_dot (char *file, int access, mode_t mode)
{
- int i, fd;
- time_t curtime;
- struct lockinfo lkinfo;
- struct stat st;
+ int fd;
+ struct lockinfo lkinfo;
- /* open the file */
- if ((fd = open (file, access, mode)) == -1)
- return -1;
+ /* open the file */
+ if ((fd = open (file, access, mode)) == -1)
+ return -1;
- /*
- * Get the name of the eventual lock file, as well
- * as a name for a temporary lock file.
- */
- lockname (file, &lkinfo, 1);
+ /*
+ * Get the name of the eventual lock file, as well
+ * as a name for a temporary lock file.
+ */
+ lockname (file, &lkinfo, 1);
#if !defined(HAVE_LIBLOCKFILE)
- for (i = 0;;) {
- /* attempt to create lock file */
- if (lockit (&lkinfo) == 0) {
- /* if successful, turn on timer and return */
- timerON (lkinfo.curlock, fd);
- return fd;
- } else {
- /*
- * Abort locking, if we fail to lock after 5 attempts
- * and are never able to stat the lock file.
- */
- if (stat (lkinfo.curlock, &st) == -1) {
- if (i++ > 5)
- return -1;
- sleep (5);
- } else {
- i = 0;
- time (&curtime);
-
- /* check for stale lockfile, else sleep */
- if (curtime > st.st_ctime + RSECS)
- unlink (lkinfo.curlock);
- else
- sleep (5);
- }
+ {
+ int i;
+ for (i = 0;;) {
+ /* attempt to create lock file */
+ if (lockit (&lkinfo) == 0) {
+ /* if successful, turn on timer and return */
+ timerON (lkinfo.curlock, fd);
+ return fd;
+ } else {
+ /*
+ * Abort locking, if we fail to lock after 5 attempts
+ * and are never able to stat the lock file.
+ */
+ struct stat st;
+ if (stat (lkinfo.curlock, &st) == -1) {
+ if (i++ > 5)
+ return -1;
+ sleep (5);
+ } else {
+ time_t curtime;
+ i = 0;
+ time (&curtime);
+
+ /* check for stale lockfile, else sleep */
+ if (curtime > st.st_ctime + RSECS)
+ unlink (lkinfo.curlock);
+ else
+ sleep (5);
+ }
+ lockname (file, &lkinfo, 1);
+ }
+ }
}
- }
#else
- if (lockfile_create(lkinfo.curlock, 5, 0) == L_SUCCESS) {
- timerON(lkinfo.curlock, fd);
- return fd;
- }
- else {
- close(fd);
- return -1;
- }
+ if (lockfile_create(lkinfo.curlock, 5, 0) == L_SUCCESS) {
+ timerON(lkinfo.curlock, fd);
+ return fd;
+ } else {
+ close(fd);
+ return -1;
+ }
#endif /* HAVE_LIBLOCKFILE */
}
static int
lockit (struct lockinfo *li)
{
- int fd;
- char *curlock, *tmplock;
+ int fd;
+ char *curlock, *tmplock;
#if 0
- char buffer[128];
+ char buffer[128];
#endif
- curlock = li->curlock;
- tmplock = li->tmplock;
+ curlock = li->curlock;
+ tmplock = li->tmplock;
#ifdef HAVE_MKSTEMP
- if ((fd = mkstemp(tmplock)) == -1)
- return -1;
+ if ((fd = mkstemp(tmplock)) == -1)
+ return -1;
#else
- if (mktemp(tmplock) == NULL)
- return -1;
- if (unlink(tmplock) == -1 && errno != ENOENT)
- return -1;
- /* create the temporary lock file */
- if ((fd = creat(tmplock, 0600)) == -1)
- return -1;
+ if (mktemp(tmplock) == NULL)
+ return -1;
+ if (unlink(tmplock) == -1 && errno != ENOENT)
+ return -1;
+ /* create the temporary lock file */
+ if ((fd = creat(tmplock, 0600)) == -1)
+ return -1;
#endif
#if 0
- /* write our process id into lock file */
- snprintf (buffer, sizeof(buffer), "nmh lock: pid %d\n", (int) getpid());
- write(fd, buffer, strlen(buffer) + 1);
+ /* write our process id into lock file */
+ snprintf (buffer, sizeof(buffer), "nmh lock: pid %d\n", (int) getpid());
+ write(fd, buffer, strlen(buffer) + 1);
#endif
- close (fd);
+ close (fd);
- /*
- * Now try to create the real lock file
- * by linking to the temporary file.
- */
- fd = link(tmplock, curlock);
- unlink(tmplock);
+ /*
+ * Now try to create the real lock file
+ * by linking to the temporary file.
+ */
+ fd = link(tmplock, curlock);
+ unlink(tmplock);
- return (fd == -1 ? -1 : 0);
+ return (fd == -1 ? -1 : 0);
}
#endif /* HAVE_LIBLOCKFILE */
static void
lockname (char *file, struct lockinfo *li, int isnewlock)
{
- int bplen, tmplen;
- char *bp, *cp;
+ int bplen, tmplen;
+ char *bp, *cp;
#if 0
- struct stat st;
+ struct stat st;
#endif
- if ((cp = strrchr (file, '/')) == NULL || *++cp == 0)
- cp = file;
+ if ((cp = strrchr (file, '/')) == NULL || *++cp == 0)
+ cp = file;
- bp = li->curlock;
- bplen = 0;
+ bp = li->curlock;
+ bplen = 0;
#ifdef LOCKDIR
- snprintf (bp, sizeof(li->curlock), "%s/", lockdir);
- tmplen = strlen (bp);
- bp += tmplen;
- bplen += tmplen;
-#else
- if (cp != file) {
- snprintf (bp, sizeof(li->curlock), "%.*s", cp - file, file);
+ snprintf (bp, sizeof(li->curlock), "%s/", lockdir);
tmplen = strlen (bp);
- bp += tmplen;
+ bp += tmplen;
bplen += tmplen;
- }
+#else
+ if (cp != file) {
+ snprintf (bp, sizeof(li->curlock), "%.*s", (int)(cp - file), file);
+ tmplen = strlen (bp);
+ bp += tmplen;
+ bplen += tmplen;
+ }
#endif
#if 0
- /*
- * mmdf style dot locking. Currently not supported.
- * If we start supporting mmdf style dot locking,
- * we will need to change the return value of lockname
- */
- if (stat (file, &st) == -1)
- return -1;
+ /*
+ * mmdf style dot locking. Currently not supported.
+ * If we start supporting mmdf style dot locking,
+ * we will need to change the return value of lockname
+ */
+ if (stat (file, &st) == -1)
+ return -1;
- snprintf (bp, sizeof(li->curlock) - bplen, "LCK%05d.%05d",
- st.st_dev, st.st_ino);
+ snprintf (bp, sizeof(li->curlock) - bplen, "LCK%05d.%05d",
+ st.st_dev, st.st_ino);
#endif
- snprintf (bp, sizeof(li->curlock) - bplen, "%s.lock", cp);
+ snprintf (bp, sizeof(li->curlock) - bplen, "%s.lock", cp);
#if !defined(HAVE_LIBLOCKFILE)
- /*
- * If this is for a new lock, create a name for
- * the temporary lock file for lockit()
- */
- if (isnewlock) {
- if ((cp = strrchr (li->curlock, '/')) == NULL || *++cp == 0)
- strncpy (li->tmplock, ",LCK.XXXXXX", sizeof(li->tmplock));
- else
- snprintf (li->tmplock, sizeof(li->tmplock), "%.*s,LCK.XXXXXX",
- cp - li->curlock, li->curlock);
- }
+ /*
+ * If this is for a new lock, create a name for
+ * the temporary lock file for lockit()
+ */
+ if (isnewlock) {
+ if ((cp = strrchr (li->curlock, '/')) == NULL || *++cp == 0)
+ strncpy (li->tmplock, ",LCK.XXXXXX", sizeof(li->tmplock));
+ else
+ snprintf (li->tmplock, sizeof(li->tmplock), "%.*s,LCK.XXXXXX",
+ (int)(cp - li->curlock), li->curlock);
+ }
#endif
}
static void
timerON (char *curlock, int fd)
{
- struct lock *lp;
- size_t len;
-
- if (!(lp = (struct lock *) malloc (sizeof(*lp))))
- return;
-
- len = strlen(curlock) + 1;
- lp->l_fd = fd;
- if (!(lp->l_lock = malloc (len))) {
- free ((char *) lp);
- return;
- }
- memcpy (lp->l_lock, curlock, len);
- lp->l_next = l_top;
-
- if (!l_top) {
- /* perhaps SIGT{STP,TIN,TOU} ? */
- SIGNAL (SIGALRM, alrmser);
- alarm (NSECS);
- }
+ struct lock *lp;
+ size_t len;
+
+ lp = (struct lock *) mh_xmalloc (sizeof(*lp));
- l_top = lp;
+ len = strlen(curlock) + 1;
+ lp->l_fd = fd;
+ lp->l_lock = mh_xmalloc (len);
+ memcpy (lp->l_lock, curlock, len);
+ lp->l_next = l_top;
+
+ if (!l_top) {
+ /* perhaps SIGT{STP,TIN,TOU} ? */
+ SIGNAL (SIGALRM, alrmser);
+ alarm (NSECS);
+ }
+
+ l_top = lp;
}
static void
timerOFF (int fd)
{
- struct lock *pp, *lp;
-
- alarm(0);
-
- if (l_top) {
- for (pp = lp = l_top; lp; pp = lp, lp = lp->l_next) {
- if (lp->l_fd == fd)
- break;
- }
- if (lp) {
- if (lp == l_top)
- l_top = lp->l_next;
- else
- pp->l_next = lp->l_next;
-
- free (lp->l_lock);
- free (lp);
+ struct lock *pp, *lp;
+
+ alarm(0);
+
+ if (l_top) {
+ for (pp = lp = l_top; lp; pp = lp, lp = lp->l_next) {
+ if (lp->l_fd == fd)
+ break;
+ }
+ if (lp) {
+ if (lp == l_top)
+ l_top = lp->l_next;
+ else
+ pp->l_next = lp->l_next;
+
+ free (lp->l_lock);
+ free (lp);
+ }
}
- }
- /* if there are locks left, restart timer */
- if (l_top)
- alarm (NSECS);
+ /* if there are locks left, restart timer */
+ if (l_top)
+ alarm (NSECS);
}
static RETSIGTYPE
alrmser (int sig)
{
- int j;
- char *lockfile;
- struct lock *lp;
+ char *lockfile;
+ struct lock *lp;
-#ifndef RELIABLE_SIGNALS
- SIGNAL (SIGALRM, alrmser);
+#ifndef RELIABLE_SIGNALS
+ SIGNAL (SIGALRM, alrmser);
#endif
- /* update the ctime of all the lock files */
- for (lp = l_top; lp; lp = lp->l_next) {
- lockfile = lp->l_lock;
+ /* update the ctime of all the lock files */
+ for (lp = l_top; lp; lp = lp->l_next) {
+ lockfile = lp->l_lock;
#if !defined(HAVE_LIBLOCKFILE)
- if (*lockfile && (j = creat (lockfile, 0600)) != -1)
- close (j);
+ {
+ int j;
+ if (*lockfile && (j = creat (lockfile, 0600)) != -1)
+ close (j);
+ }
#else
- lockfile_touch(lockfile);
+ lockfile_touch(lockfile);
#endif
- }
+ }
- /* restart the alarm */
- alarm (NSECS);
+ /* restart the alarm */
+ alarm (NSECS);
}
#endif /* DOT_LOCKING */