/* -*-c-*-
*
- * $Id: lock.c,v 1.2 1999/05/26 20:53:40 mdw Exp $
+ * $Id: lock.c,v 1.5 1999/06/19 20:35:45 mdw Exp $
*
* Simplified POSIX locking interface
*
/*----- Revision history --------------------------------------------------*
*
* $Log: lock.c,v $
+ * Revision 1.5 1999/06/19 20:35:45 mdw
+ * Whoops. I'd left the type of the jump buffer as `sigjmp_buf'.
+ *
+ * Revision 1.4 1999/06/19 20:33:16 mdw
+ * More sophisticated and excessive signal and alarm handling.
+ *
+ * Revision 1.3 1999/06/06 01:23:00 mdw
+ * Fix signal handling.
+ *
* Revision 1.2 1999/05/26 20:53:40 mdw
* Fixes for stupid bugs.
*
/*----- Header files ------------------------------------------------------*/
#include <errno.h>
+#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#define LOCK_TIMEOUT 10 /* Maximum time in seconds to wait */
+/*----- Static variables --------------------------------------------------*/
+
+static jmp_buf jmp; /* Jump here to interrup @fcntl@ */
+
/*----- Main code ---------------------------------------------------------*/
-/* --- @lock_alarm@ --- *
+/* --- @sigalrm@ --- *
*
* Arguments: @int sig@ = signal number
*
* Use: Makes sure that a @SIGALRM@ signal interrupts system calls.
*/
-static void lock_alarm(int sig) { ; }
+static void sigalrm(int sig) { longjmp(jmp, 1); }
/* --- @lock_file@ --- *
*
int lock_file(int fd, unsigned how)
{
struct flock fk;
- void (*alrm)(int);
+ struct sigaction sa, osa;
+ sigset_t ss, oss;
int e;
+ int al, d, left;
/* --- Fill in the easy bits --- */
return (fcntl(fd, F_SETLK, &fk));
}
- /* --- Set an alarm handler --- */
+ /* --- Decide how to do the locking --- */
if (how == LOCK_EXCL)
fk.l_type = F_WRLCK;
return (-1);
}
- alrm = signal(SIGALRM, lock_alarm);
- alarm(LOCK_TIMEOUT);
- if ((e = fcntl(fd, F_SETLKW, &fk)) != 0) {
- if (errno == EINTR)
- errno = EAGAIN;
+ /* --- Block @SIGALRM@ for a while --- *
+ *
+ * I don't want stray alarms going off while I'm busy here.
+ */
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &ss, &oss))
+ return (-1);
+
+ /* --- Set up the signal handler --- */
+
+ sa.sa_handler = sigalrm;
+ sa.sa_flags = 0;
+#ifdef SA_INTERRUPT
+ sa.sa_flags |= SA_INTERRUPT;
+#endif
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGALRM, &sa, &osa))
+ return (-1);
+
+ /* --- Set up the alarm, remembering when it's meant to go off --- */
+
+ al = alarm(0);
+ if (al && al < LOCK_TIMEOUT)
+ d = al;
+ else
+ d = LOCK_TIMEOUT;
+ alarm(d);
+
+ /* --- Set up the return context for the signal handler --- */
+
+ if (setjmp(jmp)) {
+ sigprocmask(SIG_SETMASK, &oss, 0);
+ errno = EINTR;
+ e = -1;
+ goto done;
+ }
+
+ /* --- Unblock the signal and we're ready --- */
+
+ if (sigprocmask(SIG_SETMASK, &oss, 0)) {
+ alarm(al);
+ e = -1;
+ goto done;
}
- alarm(0);
- signal(SIGALRM, alrm);
+
+ /* --- Do it --- */
+
+ e = fcntl(fd, F_SETLKW, &fk);
+
+ /* --- Tidy up the mess I left --- */
+
+ left = alarm(0);
+ if (al)
+ alarm(al - d + left);
+done:
+ sigaction(SIGALRM, &osa, 0);
return (e);
}