chiark / gitweb /
Merge branch 'acctdump'
[chiark-utils.git] / cprogs / with-lock-ex.c
1 /*
2  * File locker
3  *
4  * Usage: with-lock-ex -<mode> <lockfile> <command> <args>...
5  *
6  * modes are
7  *  w    wait for the lock
8  *  f    fail if the lock cannot be acquired
9  *  q    silently do nothing if the lock cannot be acquired
10  *
11  * with-lock-ex will open and lock the lockfile for writing and
12  * then feed the remainder of its arguments to exec(2); when
13  * that process terminates the fd will be closed and the file
14  * unlocked automatically by the kernel.
15  *
16  * If invoked as with-lock, behaves like with-lock-ex -f (for backward
17  * compatibility with an earlier version).
18  *
19  * This file written by me, Ian Jackson, in 1993, 1994, 1995, 1996,
20  * 1998, 1999.  I hereby place it in the public domain.
21  */
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/stat.h>
30
31 static const char *cmd;
32
33 static void fail(const char *why) __attribute__((noreturn));
34
35 static void fail(const char *why) {
36   fprintf(stderr,"with-lock-ex %s: %s: %s\n",cmd,why,strerror(errno));
37   exit(255);
38 }
39
40 int main(int argc, char **argv) {
41   int fd, mode, um;
42   struct stat stab, fstab;
43   long cloexec;
44   struct flock fl;
45   const char *p;
46
47   if (argc >= 3 && !strcmp((p= strrchr(argv[0],'/')) ? ++p : argv[0], "with-lock")) {
48     mode= 'f';
49   } else if (argc < 4 || argv[1][0] != '-' || argv[1][2] ||
50              ((mode= argv[1][1]) != 'w' && mode != 'q' && mode != 'f')) {
51     fputs("usage: with-lock-ex -w|-q|-f <lockfile> <command> <args>...\n"
52           "       with-lock             <lockfile> <command> <args>...\n",
53           stderr);
54     exit(255);
55   } else {
56     argv++; argc--;
57   }
58   cmd= argv[2];
59   um= umask(0777); if (um==-1) fail("find umask");
60   if (umask(um)==-1) fail("reset umask");
61
62   for (;;) {
63   
64     fd= open(argv[1],O_RDWR|O_CREAT,0666&~(um|((um&0222)<<1)));
65     if (fd<0) fail(argv[1]);
66   
67     for (;;) {
68       fl.l_type= F_WRLCK;
69       fl.l_whence= SEEK_SET;
70       fl.l_start= 0;
71       fl.l_len= 1;
72       if (fcntl(fd, mode=='w' ? F_SETLKW : F_SETLK, &fl) != -1) break;
73       if (mode=='q' &&
74           (errno == EAGAIN || errno == EWOULDBLOCK || errno == EBUSY))
75         exit(0);
76       if (errno != EINTR) fail("could not acquire lock");
77     }
78
79     if (fstat(fd, &fstab)) fail("could not fstat lock fd");
80     if (stat(argv[1], &stab)) {
81       if (errno != ENOENT) fail("could not stat lockfile");
82     } else {
83       if (stab.st_dev == fstab.st_dev &&
84           stab.st_ino == fstab.st_ino) break;
85     }
86     close(fd);
87   }
88
89   cloexec= fcntl(fd, F_GETFD); if (cloexec==-1) fail("fcntl F_GETFD");
90   cloexec &= ~1;
91   if (fcntl(fd, F_SETFD, cloexec)==-1) fail("fcntl F_SETFD");
92
93   execvp(cmd,argv+2);
94   fail("unable to execute command");
95 }