]> git.saurik.com Git - apple/shell_cmds.git/blame - shlock/shlock.c
shell_cmds-17.1.tar.gz
[apple/shell_cmds.git] / shlock / shlock.c
CommitLineData
44bd5ea7
A
1/* $NetBSD: shlock.c,v 1.3 1998/01/09 08:06:11 perry Exp $ */
2
3/*
4** Program to produce reliable locks for shell scripts.
5** Algorithm suggested by Peter Honeyman, January 1984,
6** in connection with HoneyDanBer UUCP.
7**
8** I tried extending this to handle shared locks in November 1987,
9** and ran into to some fundamental problems:
10**
11** Neither 4.3 BSD nor System V have an open(2) with locking,
12** so that you can open a file and have it locked as soon as
13** it's real; you have to make two system calls, and there's
14** a race...
15**
16** When removing dead process id's from a list in a file,
17** you need to truncate the file (you don't want to create a
18** new one; see above); unfortunately for the portability of
19** this program, only 4.3 BSD has ftruncate(2).
20**
21** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987
22**
23** Extensions for UUCP style locks (i.e. pid is an int in the file,
24** rather than an ASCII string). Also fix long standing bug with
25** full file systems and temporary files.
26**
27** Erik E. Fair <fair@apple.com>, November 12, 1989
28**
29** ANSIfy the code somewhat to make gcc -Wall happy with the code.
30** Submit to NetBSD
31**
32** Erik E. Fair <fair@clock.org>, May 20, 1997
33*/
34
35#include <sys/types.h>
36#include <sys/file.h>
37#include <fcntl.h> /* Needed on hpux */
38#include <stdio.h>
39#include <signal.h>
40#include <errno.h>
41#include <string.h>
42#include <unistd.h>
43#include <stdlib.h>
44
45#define LOCK_SET 0
46#define LOCK_FAIL 1
47
48#define LOCK_GOOD 0
49#define LOCK_BAD 1
50
51#define FAIL (-1)
52
53#define TRUE 1
54#define FALSE 0
55
56int Debug = FALSE;
57char *Pname;
58char *USAGE = "%s: USAGE: shlock -f file -p pid [-d][-u]\n";
59char *E_unlk = "%s: unlink(%s): %s\n";
60char *E_open = "%s: open(%s): %s\n";
61
62#define dprintf if (Debug) printf
63
64/*
65** Prototypes to make the ANSI compilers happy
66** Didn't lint used to do type and argument checking?
67** (and wasn't that sufficient?)
68*/
69
70#ifdef __STDC__
71/* the following is in case you need to make the prototypes go away. */
72#define _P(x) x
73
74char *xtmpfile _P((char *, pid_t, int));
75int p_exists _P((pid_t));
76int cklock _P((char *, int));
77int mklock _P((char *, pid_t, int));
78void bad_usage _P((void));
79int main _P((int, char **));
80#endif /* __STDC__ */
81
82/*
83** Create a temporary file, all ready to lock with.
84** The file arg is so we get the filename right, if he
85** gave us a full path, instead of using the current directory
86** which might not be in the same filesystem.
87*/
88char *
89xtmpfile(file, pid, uucpstyle)
90char *file;
91pid_t pid;
92int uucpstyle;
93{
94 int fd;
95 int len;
96 char *cp, buf[BUFSIZ];
97 static char tempname[BUFSIZ];
98
99 sprintf(buf, "shlock%d", getpid());
100 if ((cp = strrchr(strcpy(tempname, file), '/')) != (char *)NULL) {
101 *++cp = '\0';
102 (void) strcat(tempname, buf);
103 } else
104 (void) strcpy(tempname, buf);
105 dprintf("%s: temporary filename: %s\n", Pname, tempname);
106
107 sprintf(buf, "%d\n", pid);
108 len = strlen(buf);
109openloop:
110 if ((fd = open(tempname, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) {
111 switch(errno) {
112 case EEXIST:
113 dprintf("%s: file %s exists already.\n",
114 Pname, tempname);
115 if (unlink(tempname) < 0) {
116 fprintf(stderr, E_unlk,
117 Pname, tempname, strerror(errno));
118 return((char *)NULL);
119 }
120 /*
121 ** Further profanity
122 */
123 goto openloop;
124 default:
125 fprintf(stderr, E_open,
126 Pname, tempname, strerror(errno));
127 return((char *)NULL);
128 }
129 }
130
131 /*
132 ** Write the PID into the temporary file before attempting to link
133 ** to the actual lock file. That way we have a valid lock the instant
134 ** the link succeeds.
135 */
136 if (uucpstyle ?
137 (write(fd, &pid, sizeof(pid)) != sizeof(pid)) :
138 (write(fd, buf, len) < 0))
139 {
140 fprintf(stderr, "%s: write(%s,%d): %s\n",
141 Pname, tempname, pid, strerror(errno));
142 (void) close(fd);
143 if (unlink(tempname) < 0) {
144 fprintf(stderr, E_unlk,
145 Pname, tempname, strerror(errno));
146 }
147 return((char *)NULL);
148 }
149 (void) close(fd);
150 return(tempname);
151}
152
153/*
154** Does the PID exist?
155** Send null signal to find out.
156*/
157int
158p_exists(pid)
159pid_t pid;
160{
161 dprintf("%s: process %d is ", Pname, pid);
162 if (pid <= 0) {
163 dprintf("invalid\n");
164 return(FALSE);
165 }
166 if (kill(pid, 0) < 0) {
167 switch(errno) {
168 case ESRCH:
169 dprintf("dead\n");
170 return(FALSE); /* pid does not exist */
171 case EPERM:
172 dprintf("alive\n");
173 return(TRUE); /* pid exists */
174 default:
175 dprintf("state unknown: %s\n", strerror(errno));
176 return(TRUE); /* be conservative */
177 }
178 }
179 dprintf("alive\n");
180 return(TRUE); /* pid exists */
181}
182
183/*
184** Check the validity of an existing lock file.
185**
186** Read the PID out of the lock
187** Send a null signal to determine whether that PID still exists
188** Existence (or not) determines the validity of the lock.
189**
190** Two bigs wins to this algorithm:
191**
192** o Locks do not survive crashes of either the system or the
193** application by any appreciable period of time.
194**
195** o No clean up to do if the system or application crashes.
196**
197*/
198int
199cklock(file, uucpstyle)
200char *file;
201int uucpstyle;
202{
203 int fd = open(file, O_RDONLY);
204 ssize_t len;
205 pid_t pid;
206 char buf[BUFSIZ];
207
208 dprintf("%s: checking extant lock <%s>\n", Pname, file);
209 if (fd < 0) {
210 if (errno != ENOENT)
211 fprintf(stderr, E_open, Pname, file, strerror(errno));
212 return(TRUE); /* might or might not; conservatism */
213 }
214
215 if (uucpstyle ?
216 ((len = read(fd, &pid, sizeof(pid))) != sizeof(pid)) :
217 ((len = read(fd, buf, sizeof(buf))) <= 0))
218 {
219 close(fd);
220 dprintf("%s: lock file format error\n", Pname);
221 return(FALSE);
222 }
223 close(fd);
224 buf[len + 1] = '\0';
225 return(p_exists(uucpstyle ? pid : atoi(buf)));
226}
227
228int
229mklock(file, pid, uucpstyle)
230char *file;
231pid_t pid;
232int uucpstyle;
233{
234 char *tmp;
235 int retcode = FALSE;
236
237 dprintf("%s: trying lock <%s> for process %d\n", Pname, file, pid);
238 if ((tmp = xtmpfile(file, pid, uucpstyle)) == (char *)NULL)
239 return(FALSE);
240
241linkloop:
242 if (link(tmp, file) < 0) {
243 switch(errno) {
244 case EEXIST:
245 dprintf("%s: lock <%s> already exists\n", Pname, file);
246 if (cklock(file, uucpstyle)) {
247 dprintf("%s: extant lock is valid\n", Pname);
248 break;
249 } else {
250 dprintf("%s: lock is invalid, removing\n",
251 Pname);
252 if (unlink(file) < 0) {
253 fprintf(stderr, E_unlk,
254 Pname, file, strerror(errno));
255 break;
256 }
257 }
258 /*
259 ** I hereby profane the god of structured programming,
260 ** Edsgar Dijkstra
261 */
262 goto linkloop;
263 default:
264 fprintf(stderr, "%s: link(%s, %s): %s\n",
265 Pname, tmp, file, strerror(errno));
266 break;
267 }
268 } else {
269 dprintf("%s: got lock <%s>\n", Pname, file);
270 retcode = TRUE;
271 }
272 if (unlink(tmp) < 0) {
273 fprintf(stderr, E_unlk, Pname, tmp, strerror(errno));
274 }
275 return(retcode);
276}
277
278void
279bad_usage()
280{
281 fprintf(stderr, USAGE, Pname);
282 exit(LOCK_FAIL);
283}
284
285int
286main(ac, av)
287int ac;
288char *av[];
289{
290 int x;
291 char *file = (char *)NULL;
292 pid_t pid = 0;
293 int uucpstyle = FALSE; /* indicating UUCP style locks */
294 int only_check = TRUE; /* don't make a lock */
295
296 Pname = ((Pname = strrchr(av[0], '/')) ? Pname + 1 : av[0]);
297
298 for(x = 1; x < ac; x++) {
299 if (av[x][0] == '-') {
300 switch(av[x][1]) {
301 case 'u':
302 uucpstyle = TRUE;
303 break;
304 case 'd':
305 Debug = TRUE;
306 break;
307 case 'p':
308 if (strlen(av[x]) > 2) {
309 pid = atoi(&av[x][2]);
310 } else {
311 if (++x >= ac) {
312 bad_usage();
313 }
314 pid = atoi(av[x]);
315 }
316 only_check = FALSE; /* wants one */
317 break;
318 case 'f':
319 if (strlen(av[x]) > 2) {
320 file = &av[x][2];
321 } else {
322 if (++x >= ac) {
323 bad_usage();
324 }
325 file = av[x];
326 }
327 break;
328 default:
329 fprintf(stderr, USAGE, Pname);
330 exit(LOCK_FAIL);
331 }
332 }
333 }
334
335 if (file == (char *)NULL || (!only_check && pid <= 0)) {
336 bad_usage();
337 }
338
339 if (only_check) {
340 exit(cklock(file, uucpstyle) ? LOCK_GOOD : LOCK_BAD);
341 }
342
343 exit(mklock(file, pid, uucpstyle) ? LOCK_SET : LOCK_FAIL);
344}