Package: chiark-utils-bin
Version: 4.2.0
Severity: wishlist
Tags: upstream patch

Hi,

I wanted a timeout option to with-lock-ex. The attached patch provides
this - you can see -t <secs> after -q or -f and with-lock-ex will then
wait that long while trying to get the lock before quitting.

I have built and tested the attached patch against the current master
branch in git.

-- System Information:
Debian Release: 7.11
  APT prefers oldstable-updates
  APT policy: (500, 'oldstable-updates'), (500, 'oldstable')
Architecture: i386 (i686)

Kernel: Linux 3.2.0-4-686-pae (SMP w/4 CPU cores)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages chiark-utils-bin depends on:
ii  libc6  2.13-38+deb7u11

Versions of packages chiark-utils-bin recommends:
ii  libc6       2.13-38+deb7u11
ii  libgmp10    2:5.0.5+dfsg-2
ii  libnettle4  2.4-3+deb7u1

Versions of packages chiark-utils-bin suggests:
ii  libx11-6  2:1.5.0-1+deb7u4

-- no debconf information
diff --git a/cprogs/with-lock-ex.c b/cprogs/with-lock-ex.c
index 2b7b45b..560711f 100644
--- a/cprogs/with-lock-ex.c
+++ b/cprogs/with-lock-ex.c
@@ -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
@@ -13,6 +13,11 @@
  *         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
@@ -32,6 +37,10 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <limits.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/time.h>
 
 static const char *cmd;
 
@@ -42,12 +51,22 @@ static void fail(const char *why) {
   exit(255);
 }
 
+volatile int alarmed = 0;
+
+static void alrm_handler(int signum) {
+  alarmed = 1;
+}
+
 int main(int argc, char **argv) {
   int fd, mode, um;
   struct stat stab, fstab;
-  long cloexec;
+  long cloexec, secs=-1;
   struct flock fl;
   const char *p;
+  char *endptr;
+  time_t tstart=0, tend=0;
+  struct sigaction siga, oldsiga;
+  struct itimerval itv;
 
   if (argc >= 3 && !strcmp((p= strrchr(argv[0],'/')) ? ++p : argv[0], "with-lock")) {
     mode= 'f';
@@ -56,7 +75,7 @@ int main(int argc, char **argv) {
 	      && mode != 'l') ||
 	     (mode != 'l' && argc < 4) ||
 	     (mode == 'l' && argc != 3)) {
-    fputs("usage: with-lock-ex -w|-q|-f <lockfile> <command> <args>...\n"
+    fputs("usage: with-lock-ex -w|-q|-f [-t <secs>] <lockfile> <command> <args>...\n"
 	  "       with-lock-ex -l       <lockfile>\n"
 	  "       with-lock             <lockfile> <command> <args>...\n",
 	  stderr);
@@ -64,6 +83,39 @@ int main(int argc, char **argv) {
   } else {
     argv++; argc--;
   }
+  if (0 == strncmp(argv[1],"-t",2)) {
+    errno = 0;
+    secs = strtol(argv[2], &endptr, 0);
+    if ((errno == ERANGE && (secs == LONG_MAX || secs == LONG_MIN))
+	|| (errno !=0 && secs == 0)) {
+      fail("parsing timeout value");
+    }
+    if (secs <= 0) {
+      fputs("timeout value must be greater than zero.\n",
+	    stderr);
+      exit(255);
+    }
+    tstart = time(NULL); if (tstart==(time_t)-1) fail("getting current time");
+    if (mode=='l' || mode=='w') {
+      fputs("-t only allowed with -q or -f.\n", stderr);
+      exit(255);
+    }
+    argv+= 2; argc-= 2;
+    if (argc < 2) {
+      fputs("Insufficient arguments following -t\n"
+	    "Usage: with-lock-ex -f|q -t <secs> <lockfile> <command> <args>...\n",
+	    stderr);
+      exit(255);
+    }
+    /*Install SIGALRM handler, and set a timer for secs seconds from now*/
+    memset(&siga,0,sizeof(siga));
+    siga.sa_handler=alrm_handler;
+    if (sigaction(SIGALRM,&siga,&oldsiga)) 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");
@@ -82,9 +134,21 @@ int main(int argc, char **argv) {
       fl.l_len= mode=='l' ? 0 : 1;
       if (fcntl(fd,
 		mode=='l' ? F_GETLK :
-		mode=='w' ? F_SETLKW :
+		mode=='w' || secs > 0 ? F_SETLKW :
 		F_SETLK,
 		&fl) != -1) break;
+      if (alarmed==1) {
+	tend = time(NULL); if (tend==(time_t)-1) fail("getting current time");
+	if (tend-tstart >= secs) {
+	  if (mode=='q') exit(0);
+	  else {
+	    fprintf(stderr,
+		    "with-lock-ex %s: timer expired while trying to acquire lock\n",
+		    cmd);
+	    exit(255);
+	  }
+	} else alarmed=0;
+      }
       if (mode=='q' &&
 	  (errno == EAGAIN || errno == EWOULDBLOCK || errno == EBUSY))
 	exit(0);
@@ -102,6 +166,11 @@ int main(int argc, char **argv) {
       if (ferror(stdout)) fail("print to stdout\n");
       exit(0);
     }
+    if (secs > 0) {
+      itv.it_value.tv_sec=0;
+      if (setitimer(ITIMER_REAL,&itv,NULL)) fail("Clearing timer");
+      if (sigaction(SIGALRM,&oldsiga,NULL)) fail("Restoring SIGALRM handler");
+    }
 
     if (fstat(fd, &fstab)) fail("could not fstat lock fd");
     if (stat(argv[1], &stab)) {

Reply via email to