source: trunk/third/nmh/sbr/lock_file.c @ 12455

Revision 12455, 9.9 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12454, which included commits to RCS files with non-trunk default branches.
Line 
1
2/*
3 * lock.c -- routines to lock/unlock files
4 *
5 * $Id: lock_file.c,v 1.1.1.1 1999-02-07 18:14:08 danw Exp $
6 */
7
8#include <h/mh.h>
9#include <h/signals.h>
10
11#ifdef HAVE_ERRNO_H
12# include <errno.h>
13#endif
14
15#ifdef MMDFONLY
16# include <mmdfonly.h>
17# include <lockonly.h>
18#endif /* MMDFONLY */
19
20#ifdef HAVE_FCNTL_H
21# include <fcntl.h>
22#else
23# include <sys/file.h>
24#endif
25
26#if defined(LOCKF_LOCKING) || defined(FLOCK_LOCKING)
27# include <sys/file.h>
28#endif
29
30#include <signal.h>
31
32extern int errno;
33
34#ifdef LOCKDIR
35char *lockdir = LOCKDIR;
36#endif
37
38/* Are we using any kernel locking? */
39#if defined (FLOCK_LOCKING) || defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
40# define KERNEL_LOCKING
41#endif
42
43#ifdef DOT_LOCKING
44
45/* struct for getting name of lock file to create */
46struct lockinfo {
47    char curlock[BUFSIZ];
48    char tmplock[BUFSIZ];
49};
50
51/*
52 * Amount of time to wait before
53 * updating ctime of lock file.
54 */
55#define NSECS 20
56
57/*
58 * How old does a lock file need to be
59 * before we remove it.
60 */
61#define RSECS 180
62
63/* struct for recording and updating locks */
64struct lock {
65    int l_fd;
66    char *l_lock;
67    struct lock *l_next;
68};
69
70/* top of list containing all open locks */
71static struct lock *l_top = NULL;
72#endif /* DOT_LOCKING */
73
74/*
75 * static prototypes
76 */
77#ifdef KERNEL_LOCKING
78static int lkopen_kernel (char *, int, mode_t);
79#endif
80
81#ifdef DOT_LOCKING
82static int lkopen_dot (char *, int, mode_t);
83static int lockit (struct lockinfo *);
84static void lockname (char *, struct lockinfo *, int);
85static void timerON (char *, int);
86static void timerOFF (int);
87static RETSIGTYPE alrmser (int);
88#endif
89
90
91/*
92 * Base routine to open and lock a file,
93 * and return a file descriptor.
94 */
95
96int
97lkopen (char *file, int access, mode_t mode)
98{
99#ifdef KERNEL_LOCKING
100    return lkopen_kernel(file, access, mode);
101#endif
102
103#ifdef DOT_LOCKING
104    return lkopen_dot(file, access, mode);
105#endif
106}
107
108
109/*
110 * Base routine to close and unlock a file,
111 * given a file descriptor.
112 */
113
114int
115lkclose (int fd, char *file)
116{
117#ifdef FCNTL_LOCKING
118    struct flock buf;
119#endif
120
121#ifdef DOT_LOCKING
122    struct lockinfo lkinfo;
123#endif
124
125    if (fd == -1)
126        return 0;
127
128#ifdef FCNTL_LOCKING
129    buf.l_type   = F_UNLCK;
130    buf.l_whence = SEEK_SET;
131    buf.l_start  = 0;
132    buf.l_len    = 0;
133    fcntl(fd, F_SETLK, &buf);
134#endif
135
136#ifdef FLOCK_LOCKING
137    flock (fd, LOCK_UN);
138#endif
139
140#ifdef LOCKF_LOCKING
141    /* make sure we unlock the whole thing */
142    lseek (fd, (off_t) 0, SEEK_SET);
143    lockf (fd, F_ULOCK, 0L);
144#endif 
145
146#ifdef DOT_LOCKING
147    lockname (file, &lkinfo, 0);        /* get name of lock file */
148    unlink (lkinfo.curlock);            /* remove lock file      */
149    timerOFF (fd);                      /* turn off lock timer   */
150#endif
151
152    return (close (fd));
153}
154
155
156/*
157 * Base routine to open and lock a file,
158 * and return a FILE pointer
159 */
160
161FILE *
162lkfopen (char *file, char *mode)
163{
164    int fd, access;
165    FILE *fp;
166
167    if (strcmp (mode, "r") == 0)
168        access = O_RDONLY;
169    else
170        access = O_RDWR;
171
172    if ((fd = lkopen (file, access, 0)) == -1)
173        return NULL;
174
175    if ((fp = fdopen (fd, mode)) == NULL) {
176        close (fd);
177        return NULL;
178    }
179
180    return fp;
181}
182
183
184/*
185 * Base routine to close and unlock a file,
186 * given a FILE pointer
187 */
188
189int
190lkfclose (FILE *fp, char *file)
191{
192#ifdef FCNTL_LOCKING
193    struct flock buf;
194#endif
195
196#ifdef DOT_LOCKING
197    struct lockinfo lkinfo;
198#endif
199
200    if (fp == NULL)
201        return 0;
202
203#ifdef FCNTL_LOCKING
204    buf.l_type   = F_UNLCK;
205    buf.l_whence = SEEK_SET;
206    buf.l_start  = 0;
207    buf.l_len    = 0;
208    fcntl(fileno(fp), F_SETLK, &buf);
209#endif
210
211#ifdef FLOCK_LOCKING
212    flock (fileno(fp), LOCK_UN);
213#endif
214
215#ifdef LOCKF_LOCKING
216    /* make sure we unlock the whole thing */
217    fseek (fp, 0L, SEEK_SET);
218    lockf (fileno(fp), F_ULOCK, 0L);
219#endif
220
221#ifdef DOT_LOCKING
222    lockname (file, &lkinfo, 0);        /* get name of lock file */
223    unlink (lkinfo.curlock);            /* remove lock file      */
224    timerOFF (fileno(fp));              /* turn off lock timer   */
225#endif
226
227    return (fclose (fp));
228}
229
230
231#ifdef KERNEL_LOCKING
232
233/*
234 * open and lock a file, using kernel locking
235 */
236
237static int
238lkopen_kernel (char *file, int access, mode_t mode)
239{
240    int fd, i, j;
241
242# ifdef FCNTL_LOCKING
243    struct flock buf;
244# endif /* FCNTL_LOCKING */
245
246    for (i = 0; i < 5; i++) {
247
248# if defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
249        /* remember the original mode */
250        j = access;
251
252        /* make sure we open at the beginning */
253        access &= ~O_APPEND;
254
255        /*
256         * We MUST have write permission or
257         * lockf/fcntl() won't work
258         */
259        if ((access & 03) == O_RDONLY) {
260            access &= ~O_RDONLY;
261            access |= O_RDWR;
262        }
263# endif /* LOCKF_LOCKING || FCNTL_LOCKING */
264
265        if ((fd = open (file, access | O_NDELAY, mode)) == -1)
266            return -1;
267
268# ifdef FCNTL_LOCKING
269        buf.l_type   = F_WRLCK;
270        buf.l_whence = SEEK_SET;
271        buf.l_start  = 0;
272        buf.l_len    = 0;
273        if (fcntl (fd, F_SETLK, &buf) != -1)
274            return fd;
275# endif
276
277# ifdef FLOCK_LOCKING
278        if (flock (fd, LOCK_EX | LOCK_NB) != -1)
279            return fd;
280# endif
281
282# ifdef LOCKF_LOCKING
283        if (lockf (fd, F_TLOCK, 0L) != -1) {
284            /* see if we should be at the end */
285            if (j & O_APPEND)
286                lseek (fd, (off_t) 0, SEEK_END);
287            return fd;
288        }
289# endif
290
291        j = errno;
292        close (fd);
293        sleep (5);
294    }
295
296    close (fd);
297    errno = j;
298    return -1;
299}
300
301#endif /* KERNEL_LOCKING */
302
303
304#ifdef DOT_LOCKING
305
306/*
307 * open and lock a file, using dot locking
308 */
309
310static int
311lkopen_dot (char *file, int access, mode_t mode)
312{
313    int i, fd;
314    time_t curtime;
315    struct lockinfo lkinfo;
316    struct stat st;
317
318    /* open the file */
319    if ((fd = open (file, access, mode)) == -1)
320        return -1;
321
322    /*
323     * Get the name of the eventual lock file, as well
324     * as a name for a temporary lock file.
325     */
326    lockname (file, &lkinfo, 1);
327
328    for (i = 0;;) {
329        /* attempt to create lock file */
330        if (lockit (&lkinfo) == 0) {
331            /* if successful, turn on timer and return */
332            timerON (lkinfo.curlock, fd);
333            return fd;
334        } else {
335            /*
336             * Abort locking, if we fail to lock after 5 attempts
337             * and are never able to stat the lock file.
338             */
339            if (stat (lkinfo.curlock, &st) == -1) {
340                if (i++ > 5)
341                    return -1;
342                sleep (5);
343            } else {
344                i = 0;
345                time (&curtime);
346
347                /* check for stale lockfile, else sleep */
348                if (curtime > st.st_ctime + RSECS)
349                    unlink (lkinfo.curlock);
350                else
351                    sleep (5);
352            }
353        }
354    }
355}
356
357/*
358 * Routine that actually tries to create
359 * the lock file.
360 */
361
362static int
363lockit (struct lockinfo *li)
364{
365    int fd;
366    char *curlock, *tmplock;
367
368#if 0
369    char buffer[128];
370#endif
371
372    curlock = li->curlock;
373    tmplock = li->tmplock;
374
375    /* create the temporary lock file */
376    if ((fd = creat(tmplock, 0600)) == -1)
377        return -1;
378
379#if 0
380    /* write our process id into lock file */
381    snprintf (buffer, sizeof(buffer), "nmh lock: pid %d\n", (int) getpid());
382    write(fd, buffer, strlen(buffer) + 1);
383#endif
384
385    close (fd);
386
387    /*
388     * Now try to create the real lock file
389     * by linking to the temporary file.
390     */
391    fd = link(tmplock, curlock);
392    unlink(tmplock);
393
394    return (fd == -1 ? -1 : 0);
395}
396
397/*
398 * Get name of lock file, and temporary lock file
399 */
400
401static void
402lockname (char *file, struct lockinfo *li, int isnewlock)
403{
404    int bplen, tmplen;
405    char *bp, *cp;
406
407#if 0
408    struct stat st;
409#endif
410
411    if ((cp = strrchr (file, '/')) == NULL || *++cp == 0)
412        cp = file;
413
414    bp = li->curlock;
415    bplen = 0;
416#ifdef LOCKDIR
417    snprintf (bp, sizeof(li->curlock), "%s/", lockdir);
418    tmplen = strlen (bp);
419    bp    += tmplen;
420    bplen += tmplen;
421#else
422    if (cp != file) {
423        snprintf (bp, sizeof(li->curlock), "%.*s", cp - file, file);
424        tmplen = strlen (bp);
425        bp    += tmplen;
426        bplen += tmplen;
427    }
428#endif
429
430#if 0
431    /*
432     * mmdf style dot locking.  Currently not supported.
433     * If we start supporting mmdf style dot locking,
434     * we will need to change the return value of lockname
435     */
436    if (stat (file, &st) == -1)
437        return -1;
438
439    snprintf (bp, sizeof(li->curlock) - bplen, "LCK%05d.%05d",
440        st.st_dev, st.st_ino);
441#endif
442
443    snprintf (bp, sizeof(li->curlock) - bplen, "%s.lock", cp);
444
445    /*
446     * If this is for a new lock, create a name for
447     * the temporary lock file for lockit()
448     */
449    if (isnewlock) {
450        if ((cp = strrchr (li->curlock, '/')) == NULL || *++cp == 0)
451            strncpy (li->tmplock, ",LCK.XXXXXX", sizeof(li->tmplock));
452        else
453            snprintf (li->tmplock, sizeof(li->tmplock), "%.*s,LCK.XXXXXX",
454                     cp - li->curlock, li->curlock);
455        mktemp (li->tmplock);
456        unlink (li->tmplock);   /* remove any stray */
457    }
458}
459
460
461/*
462 * Add new lockfile to the list of open lockfiles
463 * and start the lock file timer.
464 */
465
466static void
467timerON (char *curlock, int fd)
468{
469    struct lock *lp;
470    size_t len;
471
472    if (!(lp = (struct lock *) malloc (sizeof(*lp))))
473        return;
474
475    len = strlen(curlock) + 1;
476    lp->l_fd = fd;
477    if (!(lp->l_lock = malloc (len))) {
478        free ((char *) lp);
479        return;
480    }
481    memcpy (lp->l_lock, curlock, len);
482    lp->l_next = l_top;
483
484    if (!l_top) {
485        /* perhaps SIGT{STP,TIN,TOU} ? */
486        SIGNAL (SIGALRM, alrmser);
487        alarm (NSECS);
488    }
489
490    l_top = lp;
491}
492
493
494/*
495 * Search through the list of lockfiles for the
496 * current lockfile, and remove it from the list.
497 */
498
499static void
500timerOFF (int fd)
501{
502    struct lock *pp, *lp;
503
504    alarm(0);
505
506    if (l_top) {
507        for (pp = lp = l_top; lp; pp = lp, lp = lp->l_next) {
508            if (lp->l_fd == fd)
509                break;
510        }
511        if (lp) {
512            if (lp == l_top)
513                l_top = lp->l_next;
514            else
515                pp->l_next = lp->l_next;
516
517            free (lp->l_lock);
518            free (lp);
519        }
520    }
521
522    /* if there are locks left, restart timer */
523    if (l_top)
524        alarm (NSECS);
525}
526
527
528/*
529 * If timer goes off, we update the ctime of all open
530 * lockfiles, so another command doesn't remove them.
531 */
532
533static RETSIGTYPE
534alrmser (int sig)
535{
536    int j;
537    char *lockfile;
538    struct lock *lp;
539
540#ifndef RELIABLE_SIGNALS
541    SIGNAL (SIGALRM, alrmser);
542#endif
543
544    /* update the ctime of all the lock files */
545    for (lp = l_top; lp; lp = lp->l_next) {
546        lockfile = lp->l_lock;
547        if (*lockfile && (j = creat (lockfile, 0600)) != -1)
548            close (j);
549    }
550
551    /* restart the alarm */
552    alarm (NSECS);
553}
554
555#endif /* DOT_LOCKING */
Note: See TracBrowser for help on using the repository browser.