Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / zotnet / mts / lock.c
1 /* lock.c - universal locking routines */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: lock.c,v 2.19 1993/08/25 17:33:09 jromine Exp $";
4 #endif
5 /* compile-time priority:
6  *      LOCKF   use if defined
7  *      FCNTL   use if SYS5  defined and LOCKF not defined
8  *      FLOCK   use if BSD42 defined and LOCKF and SYS5 not defined
9  */
10
11 #ifdef  MMDFONLY
12 #define LOCKONLY
13 #endif
14
15 #include "../h/mh.h"
16 #include <stdio.h>
17 #ifndef LOCKONLY
18 #include "../h/strings.h"
19 #include "mts.h"
20 #else   /* LOCKONLY */
21 #include "strings.h"
22 #ifdef  MMDFONLY
23 #include "mmdfonly.h"
24 #include "mts.h"
25 #else   /* not MMDFONLY */
26 #include "lockonly.h"
27 #endif  /* not MMDFONLY */
28 #endif  /* LOCKONLY */
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #ifdef SVR4
32 #define LOCKF
33 #include <unistd.h>
34 #endif
35 #ifdef  LOCKF
36 #include <sys/errno.h>
37 #include <sys/file.h>
38 #ifndef F_ULOCK
39 #ifdef  UNISTD
40 #include <unistd.h>
41 #else   /* UNISTD */
42 #include <sys/fcntl.h>
43 #endif  /* UNISTD */
44 #endif
45 #endif  /* LOCKF */
46 #if defined(_AIX) || defined(AUX)
47 #include <sys/file.h>
48 #endif
49
50 #ifdef  SYS5
51 #define u_short ushort
52 #define u_long  ulong
53 #endif
54
55
56 #if defined(SYS5) && !defined(_AIX)
57 #define index   strchr
58 #define rindex  strrchr
59 #endif
60 #ifdef  BSD42
61 #define FLOCK           /* LOCKF will override this, if defined */
62 #endif
63
64 extern int  errno;
65
66 #ifdef  LOCKONLY
67 #ifndef MMDFONLY
68 char   *lockldir = "/usr/spool/locks";
69 #endif  /* not MMDFONLY */
70 #endif  /* LOCKONLY */
71
72 static int      b_lkopen(), lockit(), f_lkopen();
73 static          lockname(), timerON(), timerOFF();
74
75 long    time ();
76
77 /* \f */
78
79 int     lkopen (file, access)
80 register char   *file;
81 register int     access;
82 {
83     mts_init ("mts");
84     switch (lockstyle) {
85         case LOK_UNIX:
86 #if     defined (FLOCK) || defined(LOCKF) || defined(FCNTL)
87             return f_lkopen (file, access);
88 #endif
89
90         default:
91             return b_lkopen (file, access);
92         }
93 }
94
95 /* \f */
96
97 static int  b_lkopen (file, access)
98 register char   *file;
99 register int     access;
100 {
101     register int    i,
102                     j;
103     long    curtime;
104     char    curlock[BUFSIZ],
105             tmplock[BUFSIZ];
106     struct stat st;
107
108     if (stat (file, &st) == NOTOK)
109         return NOTOK;
110     lockname (curlock, tmplock, file, (int) st.st_dev, (int) st.st_ino);
111
112     for (i = 0;;)
113         switch (lockit (tmplock, curlock)) {
114             case OK: 
115                 if ((i = open (file, access)) == NOTOK) {
116                     j = errno;
117                     (void) unlink (curlock);
118                     errno = j;
119                 }
120                 timerON (curlock, i);
121                 return i;
122
123             case NOTOK: 
124                 if (stat (curlock, &st) == NOTOK) {
125                     if (i++ > 5)
126                         return NOTOK;
127                     sleep (5);
128                     break;
129                 }
130
131                 i = 0;
132                 (void) time (&curtime);
133                 if (curtime < st.st_ctime + 60L)
134                     sleep (5);
135                 else
136                     (void) unlink (curlock);
137                 break;
138         }
139 }
140
141
142 static int  lockit (tmp, file)
143 register char   *tmp,
144                 *file;
145 {
146     register int    fd;
147
148     if ((fd = creat (tmp, 0400)) == NOTOK)
149         return NOTOK;
150 #if defined(hpux) || defined(ncr)
151     write(fd, "MH lock\n",8);
152 #endif /* hpux */
153     (void) close (fd);
154
155     fd = link (tmp, file);
156     (void) unlink (tmp);
157
158     return (fd != NOTOK ? OK : NOTOK);
159 }
160
161 /* \f */
162
163 static  lockname (curlock, tmplock, file, dev, ino)
164 register char   *curlock,
165                 *tmplock,
166                 *file;
167 register int     dev,
168                  ino;
169 {
170     register char  *bp,
171                    *cp;
172
173     bp = curlock;
174     if ((cp = rindex (file, '/')) == NULL || *++cp == 0)
175         cp = file;
176     if (lockldir == NULL || *lockldir == 0) {
177         if (cp != file) {
178             (void) sprintf (bp, "%.*s", cp - file, file);
179             bp += strlen (bp);
180         }
181     }
182     else {
183         (void) sprintf (bp, "%s/", lockldir);
184         bp += strlen (bp);
185     }
186
187     switch (lockstyle) {
188         case LOK_BELL: 
189         default: 
190             (void) sprintf (bp, "%s.lock", cp);
191             break;
192
193         case LOK_MMDF: 
194             (void) sprintf (bp, "LCK%05d.%05d", dev, ino);
195             break;
196     }
197
198     if (tmplock) {
199         if ((cp = rindex (curlock, '/')) == NULL || *++cp == 0)
200             (void) strcpy (tmplock, ",LCK.XXXXXX");
201         else
202             (void) sprintf (tmplock, "%.*s,LCK.XXXXXX",
203                 cp - curlock, curlock);
204         (void) unlink (mktemp (tmplock));
205     }
206 }
207
208 /* \f */
209
210 #if     defined(FLOCK) || defined(LOCKF) || defined(FCNTL)
211
212 #if     defined(BSD42) || defined(SVR4)
213 #include <sys/file.h>
214 #if     defined(SUN40) || defined(SVR4)
215 #include <sys/fcntl.h>
216 #endif
217 #else 
218 #ifdef  FCNTL
219 #include <fcntl.h>
220 #endif
221 #endif
222
223 static int  f_lkopen (file, access)
224 register char   *file;
225 register int     access;
226 {
227     register int    fd,
228                     i,
229                     j;
230 #ifdef FCNTL
231     struct flock    buf;
232 #endif /* FCNTL */
233
234     for (i = 0; i < 5; i++) {
235 #if defined(LOCKF) || defined(FCNTL)
236         j = access;
237         access &= ~O_APPEND;    /* make sure we open at the beginning */
238         if ((access & 03) == O_RDONLY) {
239         /* We MUST have write permission or lockf/fcntl() won't work */
240         /* (Stupid eh?) */
241             access &= ~O_RDONLY;
242             access |= O_RDWR;
243         }
244 #endif  /* LOCKF || FCNTL */
245         if ((fd = open (file, access | O_NDELAY)) == NOTOK)
246             return NOTOK;
247 #ifndef LOCKF
248 #ifndef FLOCK
249 #ifndef FCNTL
250         /* should be an error? */
251 #else /* FCNTL */
252         buf.l_type = F_WRLCK;
253         buf.l_whence = 0;
254         buf.l_start = 0;
255         buf.l_len = 0;
256         if (fcntl (fd, F_SETLK, &buf) != NOTOK)
257             return fd;
258 #endif
259 #else /* FLOCK */
260         if (flock (fd, LOCK_EX | LOCK_NB) != NOTOK)
261             return fd;
262 #endif
263 #else /* LOCKF */
264         if (lockf (fd, F_TLOCK, 0L) != NOTOK) {
265             /* see if we should be at the end */
266             if (j & O_APPEND)
267 #ifdef SVR4
268                 lseek (fd, (off_t)0, SEEK_END);
269 #else
270                 lseek (fd, (off_t)0, L_XTND);
271 #endif
272             return fd;
273         }
274         /* Fix errno - lockf screws it */
275         if (errno == EACCES)
276             errno = EWOULDBLOCK;
277 #endif
278         j = errno;
279         (void) close (fd);
280
281         sleep (5);
282     }
283
284     (void) close (fd);
285     errno = j;
286     return NOTOK;
287 }
288 #endif  /* FLOCK || LOCKF || FCNTL */
289
290 /* \f */
291
292 /* ARGSUSED */
293
294 int     lkclose (fd, file)
295 register int     fd;
296 register char   *file;
297 {
298     char    curlock[BUFSIZ];
299     struct stat st;
300 #ifdef FCNTL
301     struct flock buf;
302 #endif
303
304     if (fd == NOTOK)
305         return OK;
306     switch (lockstyle) {
307         case LOK_UNIX: 
308 #ifndef LOCKF
309 #ifndef FLOCK
310 #ifndef FCNTL
311         /* should be an error? */
312 #else   /* FCNTL */
313             buf.l_type = F_UNLCK;
314             buf.l_whence = 0;
315             buf.l_start = 0;
316             buf.l_len = 0;
317             fcntl(fd, F_SETLK, &buf);
318             break;
319 #endif
320 #else   /* FLOCK */
321             flock (fd, LOCK_UN);
322             break;
323 #endif
324 #else   /* LOCKF */
325             lseek (fd, (off_t)0, L_SET); /* make sure we unlock the whole thing */
326             lockf (fd, F_ULOCK, 0L);
327             break;
328 #endif  
329
330         default: 
331             if (fstat (fd, &st) != NOTOK) {
332                 lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
333                 (void) unlink (curlock);
334                 timerOFF (fd);
335             }
336     }
337
338     return (close (fd));
339 }
340
341
342 /* \f */
343
344 FILE    *lkfopen (file, mode)
345 register char   *file,
346                 *mode;
347 {
348     register int    fd;
349     register FILE  *fp;
350
351     if ((fd = lkopen (file, strcmp (mode, "r") ? 2 : 0)) == NOTOK)
352         return NULL;
353
354     if ((fp = fdopen (fd, mode)) == NULL) {
355         (void) close (fd);
356         return NULL;
357     }
358
359     return fp;
360 }
361
362
363 /* ARGSUSED */
364
365 int     lkfclose (fp, file)
366 register FILE   *fp;
367 register char   *file;
368 {
369     char    curlock[BUFSIZ];
370     struct stat st;
371 #ifdef FCNTL
372     struct flock buf;
373 #endif
374
375     if (fp == NULL)
376         return OK;
377
378     switch (lockstyle) {
379         case LOK_UNIX: 
380 #ifndef LOCKF
381 #ifndef FLOCK
382 #ifndef FCNTL
383         /* should be an error? */
384 #else   /* FCNTL */
385             buf.l_type = F_UNLCK;
386             buf.l_whence = 0;
387             buf.l_start = 0;
388             buf.l_len = 0;
389             fcntl(fileno(fp), F_SETLK, &buf);
390             break;
391 #endif
392 #else /* FLOCK */
393             flock (fileno(fp), LOCK_UN);
394             break;
395 #endif
396 #else   /* LOCKF */
397             fseek (fp, 0L, 0); /* make sure we unlock the whole thing */
398             lockf (fileno(fp), F_ULOCK, 0L);
399             break;
400 #endif
401
402         default: 
403             if (fstat (fileno (fp), &st) != NOTOK) {
404                 lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
405                 (void) unlink (curlock);
406             }
407     }
408
409     return (fclose (fp));
410 }
411
412 /* \f */
413
414 #include <signal.h>
415
416 #define NSECS   ((unsigned) 20)
417
418
419 struct lock {
420     int          l_fd;
421     char        *l_lock;
422     struct lock *l_next;
423 };
424 #define NULLP   ((struct lock *) 0)
425
426 static struct lock *l_top = NULLP;
427
428
429 /* ARGSUSED */
430
431 static TYPESIG alrmser (sig)
432 int     sig;
433 {
434     register int    j;
435     register char  *cp;
436     register struct lock   *lp;
437
438 #ifndef BSD42
439     (void) signal (SIGALRM, alrmser);
440 #endif  /* BSD42 */
441
442     for (lp = l_top; lp; lp = lp -> l_next)
443         if (*(cp = lp -> l_lock) && (j = creat (cp, 0400)) != NOTOK)
444             (void) close (j);
445
446     (void) alarm (NSECS);
447 }
448
449 /* \f */
450
451 static timerON (lock, fd)
452 char   *lock;
453 int     fd;
454 {
455     register struct lock   *lp;
456
457     if ((lp = (struct lock *) malloc ((unsigned) (sizeof *lp))) == NULLP)
458         return;                 /* XXX */
459
460     lp -> l_fd = fd;
461     if ((lp -> l_lock = malloc ((unsigned) (strlen (lock) + 1))) == NULLCP) {
462         free ((char *) lp);
463         return;                 /* XXX */
464     }
465     (void) strcpy (lp -> l_lock, lock);
466     lp -> l_next = NULLP;
467
468     if (l_top)
469         lp -> l_next = l_top -> l_next;
470     else {
471         (void) signal (SIGALRM, alrmser);/* perhaps SIGT{STP,TIN,TOU} */
472         (void) alarm (NSECS);
473     }
474     l_top = lp;
475 }
476
477
478 static timerOFF (fd)
479 int     fd;
480 {
481     register struct lock   *pp,
482                            *lp;
483
484     (void) alarm (0);
485
486     if (l_top) {
487         for (pp = lp = l_top; lp; pp = lp, lp = lp -> l_next)
488             if (lp -> l_fd == fd)
489                 break;
490         if (lp) {
491             if (lp == l_top)
492                 l_top = lp -> l_next;
493             else
494                 pp -> l_next = lp -> l_next;
495
496             free (lp -> l_lock);
497             free ((char *) lp);
498         }
499     }
500
501     if (l_top)
502         (void) alarm (NSECS);
503 }