Fixup fromcvs/togit conversion
[minix-pkgsrc.git] / pkgtools / shlock / files / shlock.c
blob7b7c3fc98e80bb0ce0d67a3b076a41419b74fc1d
1 /* $NetBSD: shlock.c,v 1.5 2000/10/11 14:46:18 is Exp $ */
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:
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...
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).
21 ** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987
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.
27 ** Erik E. Fair <fair@apple.com>, November 12, 1989
29 ** ANSIfy the code somewhat to make gcc -Wall happy with the code.
30 ** Submit to NetBSD
32 ** Erik E. Fair <fair@clock.org>, May 20, 1997
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>
45 #define LOCK_SET 0
46 #define LOCK_FAIL 1
48 #define LOCK_GOOD 0
49 #define LOCK_BAD 1
51 #define FAIL (-1)
53 #define TRUE 1
54 #define FALSE 0
56 int Debug = FALSE;
57 char *Pname;
58 const char USAGE[] = "%s: USAGE: shlock -f file -p pid [-d][-u]\n";
59 const char E_unlk[] = "%s: unlink(%s): %s\n";
60 const char E_open[] = "%s: open(%s): %s\n";
62 #define dprintf if (Debug) printf
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?)
70 #ifdef __STDC__
71 /* the following is in case you need to make the prototypes go away. */
72 #define _P(x) x
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__ */
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.
88 char *
89 xtmpfile(file, pid, uucpstyle)
90 char *file;
91 pid_t pid;
92 int uucpstyle;
94 int fd;
95 int len;
96 char *cp, buf[BUFSIZ];
97 static char tempname[BUFSIZ];
99 sprintf(buf, "shlock%ld", (u_long)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);
107 sprintf(buf, "%ld\n", (u_long)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);
121 ** Further profanity
123 goto openloop;
124 default:
125 fprintf(stderr, E_open,
126 Pname, tempname, strerror(errno));
127 return((char *)NULL);
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.
136 if (uucpstyle ?
137 (write(fd, &pid, sizeof(pid)) != sizeof(pid)) :
138 (write(fd, buf, len) < 0))
140 fprintf(stderr, "%s: write(%s,%ld): %s\n",
141 Pname, tempname, (u_long)pid, strerror(errno));
142 (void) close(fd);
143 if (unlink(tempname) < 0) {
144 fprintf(stderr, E_unlk,
145 Pname, tempname, strerror(errno));
147 return((char *)NULL);
149 (void) close(fd);
150 return(tempname);
154 ** Does the PID exist?
155 ** Send null signal to find out.
158 p_exists(pid)
159 pid_t pid;
161 dprintf("%s: process %ld is ", Pname, (u_long)pid);
162 if (pid <= 0) {
163 dprintf("invalid\n");
164 return(FALSE);
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 */
179 dprintf("alive\n");
180 return(TRUE); /* pid exists */
184 ** Check the validity of an existing lock file.
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.
190 ** Two bigs wins to this algorithm:
192 ** o Locks do not survive crashes of either the system or the
193 ** application by any appreciable period of time.
195 ** o No clean up to do if the system or application crashes.
199 cklock(file, uucpstyle)
200 char *file;
201 int uucpstyle;
203 int fd = open(file, O_RDONLY);
204 ssize_t len;
205 pid_t pid;
206 char buf[BUFSIZ];
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 */
215 if (uucpstyle ?
216 ((len = read(fd, &pid, sizeof(pid))) != sizeof(pid)) :
217 ((len = read(fd, buf, sizeof(buf))) <= 0))
219 close(fd);
220 dprintf("%s: lock file format error\n", Pname);
221 return(FALSE);
223 close(fd);
224 buf[len + 1] = '\0';
225 return(p_exists(uucpstyle ? pid : atoi(buf)));
229 mklock(file, pid, uucpstyle)
230 char *file;
231 pid_t pid;
232 int uucpstyle;
234 char *tmp;
235 int retcode = FALSE;
237 dprintf("%s: trying lock <%s> for process %ld\n", Pname, file,
238 (u_long)pid);
239 if ((tmp = xtmpfile(file, pid, uucpstyle)) == (char *)NULL)
240 return(FALSE);
242 linkloop:
243 if (link(tmp, file) < 0) {
244 switch(errno) {
245 case EEXIST:
246 dprintf("%s: lock <%s> already exists\n", Pname, file);
247 if (cklock(file, uucpstyle)) {
248 dprintf("%s: extant lock is valid\n", Pname);
249 break;
250 } else {
251 dprintf("%s: lock is invalid, removing\n",
252 Pname);
253 if (unlink(file) < 0) {
254 fprintf(stderr, E_unlk,
255 Pname, file, strerror(errno));
256 break;
260 ** I hereby profane the god of structured programming,
261 ** Edsgar Dijkstra
263 goto linkloop;
264 default:
265 fprintf(stderr, "%s: link(%s, %s): %s\n",
266 Pname, tmp, file, strerror(errno));
267 break;
269 } else {
270 dprintf("%s: got lock <%s>\n", Pname, file);
271 retcode = TRUE;
273 if (unlink(tmp) < 0) {
274 fprintf(stderr, E_unlk, Pname, tmp, strerror(errno));
276 return(retcode);
279 void
280 bad_usage()
282 fprintf(stderr, USAGE, Pname);
283 exit(LOCK_FAIL);
287 main(ac, av)
288 int ac;
289 char *av[];
291 int x;
292 char *file = (char *)NULL;
293 pid_t pid = 0;
294 int uucpstyle = FALSE; /* indicating UUCP style locks */
295 int only_check = TRUE; /* don't make a lock */
297 Pname = ((Pname = strrchr(av[0], '/')) ? Pname + 1 : av[0]);
299 for(x = 1; x < ac; x++) {
300 if (av[x][0] == '-') {
301 switch(av[x][1]) {
302 case 'u':
303 uucpstyle = TRUE;
304 break;
305 case 'd':
306 Debug = TRUE;
307 break;
308 case 'p':
309 if (strlen(av[x]) > 2) {
310 pid = atoi(&av[x][2]);
311 } else {
312 if (++x >= ac) {
313 bad_usage();
315 pid = atoi(av[x]);
317 only_check = FALSE; /* wants one */
318 break;
319 case 'f':
320 if (strlen(av[x]) > 2) {
321 file = &av[x][2];
322 } else {
323 if (++x >= ac) {
324 bad_usage();
326 file = av[x];
328 break;
329 default:
330 fprintf(stderr, USAGE, Pname);
331 exit(LOCK_FAIL);
336 if (file == (char *)NULL || (!only_check && pid <= 0)) {
337 bad_usage();
340 if (only_check) {
341 exit(cklock(file, uucpstyle) ? LOCK_GOOD : LOCK_BAD);
344 exit(mklock(file, pid, uucpstyle) ? LOCK_SET : LOCK_FAIL);