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