]> git.saurik.com Git - apple/shell_cmds.git/blob - shlock/shlock.c
shell_cmds-74.1.1.tar.gz
[apple/shell_cmds.git] / shlock / shlock.c
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
56 int Debug = FALSE;
57 char *Pname;
58 char *USAGE = "%s: USAGE: shlock -f file -p pid [-d][-u]\n";
59 char *E_unlk = "%s: unlink(%s): %s\n";
60 char *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
74 char *xtmpfile _P((char *, pid_t, int));
75 int p_exists _P((pid_t));
76 int cklock _P((char *, int));
77 int mklock _P((char *, pid_t, int));
78 void bad_usage _P((void));
79 int 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 */
88 char *
89 xtmpfile(file, pid, uucpstyle)
90 char *file;
91 pid_t pid;
92 int 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);
109 openloop:
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 */
157 int
158 p_exists(pid)
159 pid_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 */
198 int
199 cklock(file, uucpstyle)
200 char *file;
201 int 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
228 int
229 mklock(file, pid, uucpstyle)
230 char *file;
231 pid_t pid;
232 int 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
241 linkloop:
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
278 void
279 bad_usage()
280 {
281 fprintf(stderr, USAGE, Pname);
282 exit(LOCK_FAIL);
283 }
284
285 int
286 main(ac, av)
287 int ac;
288 char *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 }