chiark / gitweb /
with-lock-ex: Provide -t (timeout) option wip.with-lock-ex-t
authorMatthew Vernon <mv3@sanger.ac.uk>
Wed, 3 May 2017 13:30:02 +0000 (14:30 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 3 May 2017 13:54:44 +0000 (14:54 +0100)
Signed-off-by: Matthew Vernon <mv3@sanger.ac.uk>
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
---
v3: split off from conversion to getopt

cprogs/with-lock-ex.c

index e00309db51de72b2043445381eb0dbcb754b7b30..c4bc6cd10ddbd65e4baec8a2c3421ad826d23239 100644 (file)
@@ -2,7 +2,7 @@
  * File locker
  *
  * Usage:
- *  with-lock-ex -<mode> <lockfile> <command> <args>...
+ *  with-lock-ex -<mode> [-t <secs>] <lockfile> <command> <args>...
  *  with-lock-ex -l      <lockfile>
  *
  * modes are
  *         or "write <pid>"; lockfile opened for reading;
  *         no command may be specified)
  *
+ * if -t is specified, then with-lock-ex will wait for up to <secs>
+ * seconds to acquire the lock, and then fail or silently do nothing
+ * (depending on whether -f or -q is specified). You cannot specify
+ * a timeout for modes l or w
+ *
  * with-lock-ex will open and lock the lockfile for writing and
  * then feed the remainder of its arguments to exec(2); when
  * that process terminates the fd will be closed and the file
@@ -56,6 +61,9 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <limits.h>
+#include <signal.h>
+#include <sys/time.h>
 
 static const char *cmd;
 
@@ -76,13 +84,34 @@ static void badusage(void) {
     exit(255);
 }
 
+volatile int gotlock = 0;
 static int mode;
 
+/* This signal handler uses unsafe functions, so MUST NOT be callable
+ * during an unsafe function, as that is Undefined Behaviour
+ */
+static void alrm_handler(int signum) {
+  if (!gotlock) {
+    if (mode=='q') {
+      exit(0);
+    } else {
+      fprintf(stderr,
+             "with-lock-ex %s: timer expired while trying to acquire lock\n",
+             cmd);
+      exit(255);
+    }
+  }
+}
+
 int main(int argc, char **argv) {
-  int fd, um, c;
+  int fd, um, c, r;
   struct stat stab, fstab;
-  long cloexec;
+  long cloexec, secs=0;
   struct flock fl;
+  char *endptr;
+  sigset_t sigs, oldsigs;
+  struct sigaction siga;
+  struct itimerval itv;
 
   mode='x';
   while ((c = getopt(argc,argv,"+wfqlt:")) != -1)
@@ -94,13 +123,40 @@ int main(int argc, char **argv) {
       if (mode != 'x') badusage();
       mode = c;
       break;
+    case 't':
+      errno = 0;
+      secs = strtol(optarg, &endptr, 0);
+      if (*endptr || endptr==optarg || errno==ERANGE)
+       fail("parsing timeout value");
+      if (secs < 0) {
+       fprintf(stderr,"timeout value must be >=0\n");
+       exit(255);
+      }
+      break;
     default:
       badusage();
     }
 
+  if (secs && (mode=='l' || mode=='w')) {
+    fputs("-t only allowed with -q or -f.\n", stderr);
+    exit(255);
+  }
+
   argv += optind-1; argc -= optind-1;
   if (argc < 2) badusage();
 
+  if (secs) {
+    if (sigemptyset(&sigs)) fail("Initialising signal set");
+    if (sigaddset(&sigs,SIGALRM)) fail("Adding SIGALRM to signal set");
+    if (sigprocmask(SIG_BLOCK,&sigs,&oldsigs)) fail("Blocking SIGALRM");
+    memset(&siga,0,sizeof(siga));
+    siga.sa_handler=alrm_handler;
+    if (sigaction(SIGALRM,&siga,NULL)) fail("Installing SIGALRM handler");
+    memset(&itv,0,sizeof(itv));
+    itv.it_value.tv_sec=secs;
+    if (setitimer(ITIMER_REAL,&itv,NULL)) fail("Setting timer");
+  }
+
   cmd= argv[2];
   um= umask(0777); if (um==-1) fail("find umask");
   if (umask(um)==-1) fail("reset umask");
@@ -117,11 +173,17 @@ int main(int argc, char **argv) {
       fl.l_whence= SEEK_SET;
       fl.l_start= 0;
       fl.l_len= mode=='l' ? 0 : 1;
-      if (fcntl(fd,
+      if (secs) sigprocmask(SIG_UNBLOCK,&sigs,NULL);
+      r = fcntl(fd,
                mode=='l' ? F_GETLK :
-               mode=='w' ? F_SETLKW :
+               mode=='w' || secs > 0 ? F_SETLKW :
                F_SETLK,
-               &fl) != -1) break;
+               &fl);
+      if (secs) sigprocmask(SIG_BLOCK,&sigs,NULL);
+      if (!r) {
+       gotlock=1;
+       break;
+      }
       if (mode=='q' &&
          (errno == EAGAIN || errno == EWOULDBLOCK || errno == EBUSY))
        exit(0);
@@ -139,6 +201,13 @@ int main(int argc, char **argv) {
       if (ferror(stdout)) fail("print to stdout\n");
       exit(0);
     }
+    if (secs) {
+      itv.it_value.tv_sec=0;
+      if (setitimer(ITIMER_REAL,&itv,NULL)) fail("Clearing timer");
+      sigprocmask(SIG_SETMASK,&oldsigs,NULL);
+      siga.sa_handler=SIG_DFL;
+      sigaction(SIGALRM,&siga,NULL);
+    }
 
     if (fstat(fd, &fstab)) fail("could not fstat lock fd");
     if (stat(argv[1], &stab)) {