Hi,
Revised patch attached. My commit message:
Use getopt for CLI parsing; simplify SIGALRM handling
Using getopt for option parsing means that the ordering of -t and -f /
-q doesn't matter, and we can put more error-handling of user input into
the same place.
We now assume:
i) SIGALRM hasn't come from some other source
ii) SIGALRM was set to SIG_DFL on entry, and will be reset thus by execvp
...and this means we can simplify the code somewhat (no need to track
elapsed time or store the old sigaction).
I'll point you at a git branch out-of-band.
Regards,
Matthew
diff --git a/cprogs/with-lock-ex.c b/cprogs/with-lock-ex.c
index 2b7b45b..2ae316d 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,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;
@@ -42,28 +50,72 @@ static void fail(const char *why) {
exit(255);
}
-int main(int argc, char **argv) {
- int fd, mode, um;
- struct stat stab, fstab;
- long cloexec;
- struct flock fl;
- const char *p;
-
- if (argc >= 3 && !strcmp((p= strrchr(argv[0],'/')) ? ++p : argv[0],
"with-lock")) {
- mode= 'f';
- } else if (argc < 3 || argv[1][0] != '-' || argv[1][2] ||
- ((mode= argv[1][1]) != 'w' && mode != 'q' && mode != 'f'
- && mode != 'l') ||
- (mode != 'l' && argc < 4) ||
- (mode == 'l' && argc != 3)) {
- fputs("usage: with-lock-ex -w|-q|-f <lockfile> <command> <args>...\n"
+static void badusage(void) __attribute__((noreturn));
+
+static void badusage(void) {
+ 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);
exit(255);
- } else {
- argv++; argc--;
+}
+
+volatile int alarmed = 0;
+
+static void alrm_handler(int signum) {
+ alarmed = 1;
+}
+
+int main(int argc, char **argv) {
+ int fd, mode, um, c;
+ struct stat stab, fstab;
+ long cloexec, secs=-1;
+ struct flock fl;
+ char *endptr;
+ struct sigaction siga;
+ struct itimerval itv;
+
+ mode='x';
+ while ((c = getopt(argc,argv,"+wfqlt:")) != -1)
+ switch(c) {
+ case 'l':
+ case 'w':
+ case 'f':
+ case 'q':
+ if (mode != 'x') badusage();
+ mode = c;
+ break;
+ case 't':
+ errno = 0;
+ secs = strtol(optarg, &endptr, 0);
+ if (*endptr || endptr==argv[2] || errno==ERANGE)
+ fail("parsing timeout value");
+ if (secs < 0) {
+ fprintf(stderr,"timeout value must be >=0\n");
+ exit(255);
+ }
+ break;
+ default:
+ badusage();
+ }
+
+ if (secs > 0 && (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 > 0) {
+ 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");
@@ -82,9 +134,18 @@ 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) {
+ if (mode=='q') exit(0);
+ else {
+ fprintf(stderr,
+ "with-lock-ex %s: timer expired while trying to acquire
lock\n",
+ cmd);
+ exit(255);
+ }
+ }
if (mode=='q' &&
(errno == EAGAIN || errno == EWOULDBLOCK || errno == EBUSY))
exit(0);
@@ -102,6 +163,10 @@ 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 (fstat(fd, &fstab)) fail("could not fstat lock fd");
if (stat(argv[1], &stab)) {