chiark / gitweb /
Commit 2.4.5-5 as unpacked
[innduct.git] / backends / shlock.c
1 /*  $Id: shlock.c 6124 2003-01-14 06:03:29Z rra $
2 **
3 **  Produce reliable locks for shell scripts, by Peter Honeyman as told
4 **  to Rich $alz.
5 */
6
7 #include "config.h"
8 #include "clibrary.h"
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <signal.h>
12 #include <sys/stat.h>
13
14 #include "inn/messages.h"
15
16
17 static bool     BinaryLock;
18
19
20 /*
21 **  See if the process named in an existing lock still exists by
22 **  sending it a null signal.
23 */
24 static bool
25 ValidLock(char *name, bool JustChecking)
26 {
27     int fd;
28     int i;
29     pid_t               pid;
30     char                buff[BUFSIZ];
31
32     /* Open the file. */
33     if ((fd = open(name, O_RDONLY)) < 0) {
34         if (JustChecking)
35             return false;
36         syswarn("cannot open %s", name);
37         return true;
38     }
39
40     /* Read the PID that is written there. */
41     if (BinaryLock) {
42         if (read(fd, (char *)&pid, sizeof pid) != sizeof pid) {
43             close(fd);
44             return false;
45         }
46     }
47     else {
48         if ((i = read(fd, buff, sizeof buff - 1)) <= 0) {
49             close(fd);
50             return false;
51         }
52         buff[i] = '\0';
53         pid = (pid_t) atol(buff);
54     }
55     close(fd);
56     if (pid <= 0)
57         return false;
58
59     /* Send the signal. */
60     if (kill(pid, 0) < 0 && errno == ESRCH)
61         return false;
62
63     /* Either the kill worked, or we're optimistic about the error code. */
64     return true;
65 }
66
67
68 /*
69 **  Unlink a file, print a message on error, and exit.
70 */
71 static void
72 UnlinkAndExit(char *name, int x)
73 {
74     if (unlink(name) < 0)
75         syswarn("cannot unlink %s", name);
76     exit(x);
77 }
78
79
80 /*
81 **  Print a usage message and exit.
82 */
83 static void
84 Usage(void)
85 {
86     fprintf(stderr, "Usage: shlock [-u|-b] -f file -p pid\n");
87     exit(1);
88 }
89
90
91 int
92 main(int ac, char *av[])
93 {
94     int i;
95     char        *p;
96     int fd;
97     char                tmp[BUFSIZ];
98     char                buff[BUFSIZ];
99     char                *name;
100     pid_t               pid;
101     bool                ok;
102     bool                JustChecking;
103
104     /* Establish our identity. */
105     message_program_name = "shlock";
106
107     /* Set defaults. */
108     pid = 0;
109     name = NULL;
110     JustChecking = false;
111     umask(NEWSUMASK);
112
113     /* Parse JCL. */
114     while ((i = getopt(ac, av, "bcup:f:")) != EOF)
115         switch (i) {
116         default:
117             Usage();
118             /* NOTREACHED */
119         case 'b':
120         case 'u':
121             BinaryLock = true;
122             break;
123         case 'c':
124             JustChecking = true;
125             break;
126         case 'p':
127             pid = (pid_t) atol(optarg);
128             break;
129         case 'f':
130             name = optarg;
131             break;
132         }
133     ac -= optind;
134     av += optind;
135     if (ac || pid == 0 || name == NULL)
136         Usage();
137
138     /* Create the temp file in the same directory as the destination. */
139     if ((p = strrchr(name, '/')) != NULL) {
140         *p = '\0';
141         snprintf(tmp, sizeof(tmp), "%s/shlock%ld", name, (long)getpid());
142         *p = '/';
143     }
144     else
145         snprintf(tmp, sizeof(tmp), "shlock%ld", (long)getpid());
146
147     /* Loop until we can open the file. */
148     while ((fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0)
149         switch (errno) {
150         default:
151             /* Unknown error -- give up. */
152             sysdie("cannot open %s", tmp);
153         case EEXIST:
154             /* If we can remove the old temporary, retry the open. */
155             if (unlink(tmp) < 0)
156                 sysdie("cannot unlink %s", tmp);
157             break;
158         }
159
160     /* Write the process ID. */
161     if (BinaryLock)
162         ok = write(fd, &pid, sizeof pid) == sizeof pid;
163     else {
164         snprintf(buff, sizeof(buff), "%ld\n", (long) pid);
165         i = strlen(buff);
166         ok = write(fd, buff, i) == i;
167     }
168     if (!ok) {
169         syswarn("cannot write PID to %s", tmp);
170         close(fd);
171         UnlinkAndExit(tmp, 1);
172     }
173
174     close(fd);
175
176     /* Handle the "-c" flag. */
177     if (JustChecking) {
178         if (ValidLock(name, true))
179             UnlinkAndExit(tmp, 1);
180         UnlinkAndExit(tmp, 0);
181     }
182
183     /* Try to link the temporary to the lockfile. */
184     while (link(tmp, name) < 0)
185         switch (errno) {
186         default:
187             /* Unknown error -- give up. */
188             syswarn("cannot link %s to %s", tmp, name);
189             UnlinkAndExit(tmp, 1);
190             /* NOTREACHED */
191         case EEXIST:
192             /* File exists; if lock is valid, give up. */
193             if (ValidLock(name, false))
194                 UnlinkAndExit(tmp, 1);
195             if (unlink(name) < 0) {
196                 syswarn("cannot unlink %s", name);
197                 UnlinkAndExit(tmp, 1);
198             }
199         }
200
201     UnlinkAndExit(tmp, 0);
202     /* NOTREACHED */
203     return 1;
204 }