1 /* lock_file.c -- routines to lock/unlock files
2  *
3  * This code is Copyright (c) 2002, by the authors of nmh.  See the
4  * COPYRIGHT file in the root directory of the nmh distribution for
5  * complete copyright information.
6  */
7 
8 /* Modified by Ruud de Rooij to support Miquel van Smoorenburg's liblockfile
9  *
10  * Since liblockfile locking shares most of its code with dot locking, it
11  * is enabled by defining both DOT_LOCKING and HAVE_LIBLOCKFILE.
12  *
13  * Ruud de Rooij <ruud@debian.org>  Sun, 28 Mar 1999 15:34:03 +0200
14  */
15 
16 #include <h/mh.h>
17 #include <h/signals.h>
18 #include <h/utils.h>
19 #include <h/mts.h>
20 #include "lock_file.h"
21 #include "m_mktemp.h"
22 
23 #ifdef HAVE_SYS_TIME_H
24 # include <sys/time.h>
25 #endif
26 #include <time.h>
27 #include <fcntl.h>
28 #ifdef HAVE_FLOCK
29 # include <sys/file.h>
30 #endif
31 
32 #if defined(HAVE_LIBLOCKFILE)
33 # include <lockfile.h>
34 #endif
35 
36 #ifdef LOCKDIR
37 char *lockdir = LOCKDIR;
38 #endif
39 
40 /* struct for getting name of lock file to create */
41 struct lockinfo {
42     char curlock[BUFSIZ];
43 #if !defined(HAVE_LIBLOCKFILE)
44     char tmplock[BUFSIZ];
45 #endif
46 };
47 
48 /*
49  * Number of tries to retry locking
50  */
51 #define LOCK_RETRIES 60
52 
53 /*
54  * Amount of time to wait before
55  * updating ctime of lock file.
56  */
57 #define	NSECS 20
58 
59 #if !defined(HAVE_LIBLOCKFILE)
60 /*
61  * How old does a lock file need to be
62  * before we remove it.
63  */
64 #define RSECS 180
65 #endif /* HAVE_LIBLOCKFILE */
66 
67 /* struct for recording and updating locks */
68 struct lock {
69     int	l_fd;
70     char *l_lock;
71     struct lock *l_next;
72 };
73 
74 enum locktype { FCNTL_LOCKING, FLOCK_LOCKING, LOCKF_LOCKING, DOT_LOCKING };
75 
76 /* Our saved lock types. */
77 static enum locktype datalocktype, spoollocktype;
78 
79 /* top of list containing all open locks */
80 static struct lock *l_top = NULL;
81 
82 static int lkopen(const char *, int, mode_t, enum locktype, int *);
83 static int str2accbits(const char *);
84 
85 static int lkopen_fcntl (const char *, int, mode_t, int *);
86 #ifdef HAVE_LOCKF
87 static int lkopen_lockf (const char *, int, mode_t, int *);
88 #endif /* HAVE_LOCKF */
89 #ifdef HAVE_FLOCK
90 static int lkopen_flock (const char *, int, mode_t, int *);
91 #endif /* HAVE_FLOCK */
92 
93 static enum locktype init_locktype(const char *);
94 
95 static int lkopen_dot (const char *, int, mode_t, int *);
96 static void lkclose_dot (int, const char *);
97 static void lockname (const char *, struct lockinfo *, int);
98 static void timerON (char *, int);
99 static void timerOFF (int);
100 static void alrmser (int);
101 
102 #if !defined(HAVE_LIBLOCKFILE)
103 static int lockit (struct lockinfo *);
104 #endif
105 
106 /*
107  * Base functions: determine the data type used to lock files and
108  * call the underlying function.
109  */
110 
111 int
lkopendata(const char * file,int access,mode_t mode,int * failed_to_lock)112 lkopendata(const char *file, int access, mode_t mode, int *failed_to_lock)
113 {
114     static bool deja_vu;
115 
116     if (!deja_vu) {
117         char *dl;
118 
119         deja_vu = true;
120 	if ((dl = context_find("datalocking"))) {
121 	    datalocktype = init_locktype(dl);
122 	} else {
123 	    /* We default to fcntl locking for data files */
124 	    datalocktype = FCNTL_LOCKING;
125 	}
126     }
127 
128     return lkopen(file, access, mode, datalocktype, failed_to_lock);
129 }
130 
131 
132 /*
133  * Locking using the spool locking algorithm
134  */
135 
lkopenspool(const char * file,int access,mode_t mode,int * failed_to_lock)136 int lkopenspool(const char *file, int access, mode_t mode, int *failed_to_lock)
137 {
138     static bool deja_vu;
139 
140     if (!deja_vu) {
141 	deja_vu = true;
142         spoollocktype = init_locktype(spoollocking);
143     }
144 
145     return lkopen(file, access, mode, spoollocktype, failed_to_lock);
146 }
147 
148 
149 /*
150  * Versions of lkopen that return a FILE *
151  */
152 
153 FILE *
lkfopendata(const char * file,const char * mode,int * failed_to_lock)154 lkfopendata(const char *file, const char *mode, int *failed_to_lock)
155 {
156     FILE *fp;
157     int oflags = str2accbits(mode);
158     int fd;
159 
160     if (oflags == -1) {
161     	errno = EINVAL;
162 	return NULL;
163     }
164 
165     if ((fd = lkopendata(file, oflags, 0666, failed_to_lock)) == -1)
166 	return NULL;
167 
168     if ((fp = fdopen (fd, mode)) == NULL) {
169 	close (fd);
170 	return NULL;
171     }
172 
173     return fp;
174 }
175 
176 FILE *
lkfopenspool(const char * file,const char * mode)177 lkfopenspool(const char *file, const char *mode)
178 {
179     FILE *fp;
180     int oflags = str2accbits(mode);
181     int failed_to_lock = 0;
182     int fd;
183 
184     if (oflags == -1) {
185     	errno = EINVAL;
186 	return NULL;
187     }
188 
189     if ((fd = lkopenspool(file, oflags, 0666, &failed_to_lock)) == -1)
190 	return NULL;
191 
192     if ((fp = fdopen (fd, mode)) == NULL) {
193 	close (fd);
194 	return NULL;
195     }
196 
197     return fp;
198 }
199 
200 
201 /*
202  * Corresponding close functions.
203  *
204  * A note here: All of the kernel locking functions terminate the lock
205  * when the descriptor is closed, so why write the code to explicitly
206  * unlock the file?  We only need to do this in the dot-locking case.
207  */
208 
209 int
lkclosedata(int fd,const char * name)210 lkclosedata(int fd, const char *name)
211 {
212     int rc = close(fd);
213 
214     if (datalocktype == DOT_LOCKING)
215     	lkclose_dot(fd, name);
216 
217     return rc;
218 }
219 
220 int
lkfclosedata(FILE * f,const char * name)221 lkfclosedata(FILE *f, const char *name)
222 {
223     int fd, rc;
224 
225     if (f == NULL)
226     	return 0;
227 
228     fd = fileno(f);
229     rc = fclose(f);
230 
231     if (datalocktype == DOT_LOCKING)
232     	lkclose_dot(fd, name);
233 
234     return rc;
235 }
236 
237 int
lkclosespool(int fd,const char * name)238 lkclosespool(int fd, const char *name)
239 {
240     int rc = close(fd);
241 
242     if (spoollocktype == DOT_LOCKING)
243     	lkclose_dot(fd, name);
244 
245     return rc;
246 }
247 
248 int
lkfclosespool(FILE * f,const char * name)249 lkfclosespool(FILE *f, const char *name)
250 {
251     int fd, rc;
252 
253     if (f == NULL)
254     	return 0;
255 
256     fd = fileno(f);
257     rc = fclose(f);
258 
259     if (spoollocktype == DOT_LOCKING)
260     	lkclose_dot(fd, name);
261 
262     return rc;
263 }
264 
265 
266 /*
267  * Convert fopen() mode argument to open() bits
268  */
269 
270 static int
str2accbits(const char * mode)271 str2accbits(const char *mode)
272 {
273     if (strcmp (mode, "r") == 0)
274 	return O_RDONLY;
275     if (strcmp (mode, "r+") == 0)
276 	return O_RDWR;
277     if (strcmp (mode, "w") == 0)
278 	return O_WRONLY | O_CREAT | O_TRUNC;
279     if (strcmp (mode, "w+") == 0)
280 	return O_RDWR | O_CREAT | O_TRUNC;
281     if (strcmp (mode, "a") == 0)
282 	return O_WRONLY | O_CREAT | O_APPEND;
283     if (strcmp (mode, "a+") == 0)
284 	return O_RDWR | O_CREAT | O_APPEND;
285 
286     errno = EINVAL;
287     return -1;
288 }
289 
290 /*
291  * Internal routine to switch between different locking types.
292  */
293 
294 static int
lkopen(const char * file,int access,mode_t mode,enum locktype ltype,int * failed_to_lock)295 lkopen (const char *file, int access, mode_t mode, enum locktype ltype,
296         int *failed_to_lock)
297 {
298     switch (ltype) {
299 
300     case FCNTL_LOCKING:
301     	return lkopen_fcntl(file, access, mode, failed_to_lock);
302 
303     case DOT_LOCKING:
304     	return lkopen_dot(file, access, mode, failed_to_lock);
305 
306 #ifdef HAVE_FLOCK
307     case FLOCK_LOCKING:
308     	return lkopen_flock(file, access, mode, failed_to_lock);
309 #endif /* HAVE_FLOCK */
310 
311 #ifdef HAVE_LOCKF
312     case LOCKF_LOCKING:
313     	return lkopen_lockf(file, access, mode, failed_to_lock);
314 #endif /* HAVE_FLOCK */
315 
316     default:
317     	adios(NULL, "Internal locking error: unsupported lock type used!");
318     }
319 
320     return -1;
321 }
322 
323 
324 /*
325  * Routine to clean up the dot locking file
326  */
327 
328 static void
lkclose_dot(int fd,const char * file)329 lkclose_dot (int fd, const char *file)
330 {
331     struct lockinfo lkinfo;
332 
333     lockname (file, &lkinfo, 0);	/* get name of lock file */
334 #if !defined(HAVE_LIBLOCKFILE)
335     (void) m_unlink (lkinfo.curlock);	/* remove lock file      */
336 #else
337     lockfile_remove(lkinfo.curlock);
338 #endif /* HAVE_LIBLOCKFILE */
339     timerOFF (fd);			/* turn off lock timer   */
340 }
341 
342 
343 /*
344  * Open and lock a file, using fcntl locking
345  */
346 
347 static int
lkopen_fcntl(const char * file,int access,mode_t mode,int * failed_to_lock)348 lkopen_fcntl(const char *file, int access, mode_t mode, int *failed_to_lock)
349 {
350     int fd, i, saved_errno;
351     struct flock flk;
352 
353     /*
354      * The assumption here is that if you open the file for writing, you
355      * need an exclusive lock.
356      */
357 
358     for (i = 0; i < LOCK_RETRIES; i++) {
359     	if ((fd = open(file, access, mode)) == -1)
360 	    return -1;
361 
362 	flk.l_start = 0;
363 	flk.l_len = 0;
364 	flk.l_type = (access & O_ACCMODE) == O_RDONLY ? F_RDLCK : F_WRLCK;
365 	flk.l_whence = SEEK_SET;
366 
367 	if (fcntl(fd, F_SETLK, &flk) != -1)
368 	    return fd;
369 
370 	saved_errno = errno;
371 	close(fd);
372 	sleep(1);
373     }
374 
375     *failed_to_lock = 1;
376     errno = saved_errno;
377     return -1;
378 }
379 
380 
381 #ifdef HAVE_FLOCK
382 /*
383  * Open and lock a file, using flock locking
384  */
385 
386 static int
lkopen_flock(const char * file,int access,mode_t mode,int * failed_to_lock)387 lkopen_flock(const char *file, int access, mode_t mode, int *failed_to_lock)
388 {
389     int fd, i, saved_errno, locktype;
390 
391     /*
392      * The assumption here is that if you open the file for writing, you
393      * need an exclusive lock.
394      */
395 
396     locktype = (((access & O_ACCMODE) == O_RDONLY) ? LOCK_SH : LOCK_EX) |
397     							LOCK_NB;
398 
399     for (i = 0; i < LOCK_RETRIES; i++) {
400     	if ((fd = open(file, access, mode)) == -1)
401 	    return -1;
402 
403 	if (flock(fd, locktype) != -1)
404 	    return fd;
405 
406 	saved_errno = errno;
407 	close(fd);
408 	sleep(1);
409     }
410 
411     *failed_to_lock = 1;
412     errno = saved_errno;
413     return -1;
414 }
415 #endif /* HAVE_FLOCK */
416 
417 /*
418  * Open and lock a file, using lockf locking
419  */
420 
421 static int
lkopen_lockf(const char * file,int access,mode_t mode,int * failed_to_lock)422 lkopen_lockf(const char *file, int access, mode_t mode, int *failed_to_lock)
423 {
424     int fd, i, saved_errno, saved_access;
425 
426     /*
427      * Two notes:
428      *
429      * Because lockf locks start from the current offset, mask off O_APPEND
430      * and seek to the end of the file later if it was requested.
431      *
432      * lockf locks require write access to the file, so always add it
433      * even if it wasn't requested.
434      */
435 
436     saved_access = access;
437 
438     access &= ~O_APPEND;
439 
440     if ((access & O_ACCMODE) == O_RDONLY) {
441     	access &= ~O_RDONLY;
442 	access |= O_RDWR;
443     }
444 
445     for (i = 0; i < LOCK_RETRIES; i++) {
446     	if ((fd = open(file, access, mode)) == -1)
447 	    return -1;
448 
449 	if (lockf(fd, F_TLOCK, 0) != -1) {
450 	    /*
451 	     * Seek to end if requested
452 	     */
453 	    if (saved_access & O_APPEND) {
454 	    	lseek(fd, 0, SEEK_END);
455 	    }
456 	    return fd;
457 	}
458 
459 	saved_errno = errno;
460 	close(fd);
461 	sleep(1);
462     }
463 
464     *failed_to_lock = 1;
465     errno = saved_errno;
466     return -1;
467 }
468 
469 
470 /*
471  * open and lock a file, using dot locking
472  */
473 
474 static int
lkopen_dot(const char * file,int access,mode_t mode,int * failed_to_lock)475 lkopen_dot (const char *file, int access, mode_t mode, int *failed_to_lock)
476 {
477     int fd;
478     struct lockinfo lkinfo;
479 
480     /* open the file */
481     if ((fd = open (file, access, mode)) == -1)
482 	return -1;
483 
484     /*
485      * Get the name of the eventual lock file, as well
486      * as a name for a temporary lock file.
487      */
488     lockname (file, &lkinfo, 1);
489 
490 #if !defined(HAVE_LIBLOCKFILE)
491     {
492 	int i;
493 	for (i = 0; i < LOCK_RETRIES; ++i) {
494             struct stat st;
495 
496 	    /* attempt to create lock file */
497 	    if (lockit (&lkinfo) == 0) {
498 		/* if successful, turn on timer and return */
499 		timerON (lkinfo.curlock, fd);
500 		return fd;
501 	    }
502 
503             /*
504              * Abort locking, if we fail to lock after 5 attempts
505              * and are never able to stat the lock file.  Or, if
506              * we can stat the lockfile but exceed LOCK_RETRIES
507              * seconds waiting for it (by falling out of the loop).
508              */
509             if (stat (lkinfo.curlock, &st) == -1) {
510                 if (i++ > 5) break;
511                 sleep (1);
512             } else {
513                 time_t curtime;
514                 time (&curtime);
515 
516                 /* check for stale lockfile, else sleep */
517                 if (curtime > st.st_ctime + RSECS)
518                     (void) m_unlink (lkinfo.curlock);
519                 else
520                     sleep (1);
521             }
522             lockname (file, &lkinfo, 1);
523 	}
524 
525         *failed_to_lock = 1;
526         return -1;
527     }
528 #else
529     if (lockfile_create(lkinfo.curlock, 5, 0) == L_SUCCESS) {
530         timerON(lkinfo.curlock, fd);
531         return fd;
532     }
533 
534     close(fd);
535     *failed_to_lock = 1;
536     return -1;
537 #endif /* HAVE_LIBLOCKFILE */
538 }
539 
540 #if !defined(HAVE_LIBLOCKFILE)
541 /*
542  * Routine that actually tries to create
543  * the lock file.
544  */
545 
546 static int
lockit(struct lockinfo * li)547 lockit (struct lockinfo *li)
548 {
549     int fd;
550     char *curlock, *tmpfile;
551 
552 #if 0
553     char buffer[128];
554 #endif
555 
556     curlock = li->curlock;
557 
558     if ((tmpfile = m_mktemp(li->tmplock, &fd, NULL)) == NULL) {
559         inform("unable to create temporary file in %s", li->tmplock);
560 	return -1;
561     }
562 
563 #if 0
564     /* write our process id into lock file */
565     snprintf (buffer, sizeof(buffer), "nmh lock: pid %d\n", (int) getpid());
566     write(fd, buffer, strlen(buffer) + 1);
567 #endif
568 
569     close (fd);
570 
571     /*
572      * Now try to create the real lock file
573      * by linking to the temporary file.
574      */
575     fd = link(tmpfile, curlock);
576     (void) m_unlink(tmpfile);
577 
578     return (fd == -1 ? -1 : 0);
579 }
580 #endif /* HAVE_LIBLOCKFILE */
581 
582 /*
583  * Get name of lock file, and temporary lock file
584  */
585 
586 static void
lockname(const char * file,struct lockinfo * li,int isnewlock)587 lockname (const char *file, struct lockinfo *li, int isnewlock)
588 {
589     int bplen, tmplen;
590     char *bp;
591     const char *cp;
592 
593 #if 0
594     struct stat st;
595 #endif
596 
597     if ((cp = strrchr (file, '/')) == NULL || *++cp == 0)
598 	cp = file;
599 
600     bp = li->curlock;
601     bplen = 0;
602 #ifdef LOCKDIR
603     snprintf (bp, sizeof(li->curlock), "%s/", lockdir);
604     tmplen = strlen (bp);
605     bp    += tmplen;
606     bplen += tmplen;
607 #else
608     if (cp != file) {
609 	snprintf (bp, sizeof(li->curlock), "%.*s", (int)(cp - file), file);
610 	tmplen = strlen (bp);
611 	bp    += tmplen;
612 	bplen += tmplen;
613     }
614 #endif
615 
616 #if 0
617     /*
618      * mmdf style dot locking.  Currently not supported.
619      * If we start supporting mmdf style dot locking,
620      * we will need to change the return value of lockname
621      */
622     if (stat (file, &st) == -1)
623 	return -1;
624 
625     snprintf (bp, sizeof(li->curlock) - bplen, "LCK%05d.%05d",
626 	st.st_dev, st.st_ino);
627 #endif
628 
629     snprintf (bp, sizeof(li->curlock) - bplen, "%s.lock", cp);
630 
631 #if defined(HAVE_LIBLOCKFILE)
632     NMH_UNUSED(isnewlock);
633 #else
634     /*
635      * If this is for a new lock, create a name for
636      * the temporary lock file for lockit()
637      */
638     if (isnewlock) {
639 	if ((cp = strrchr (li->curlock, '/')) == NULL || *++cp == 0)
640 	    strncpy (li->tmplock, ",LCK.XXXXXX", sizeof(li->tmplock));
641 	else
642 	    snprintf (li->tmplock, sizeof(li->tmplock), "%.*s,LCK.XXXXXX",
643 		     (int)(cp - li->curlock), li->curlock);
644     }
645 #endif
646 }
647 
648 
649 /*
650  * Add new lockfile to the list of open lockfiles
651  * and start the lock file timer.
652  */
653 
654 static void
timerON(char * curlock,int fd)655 timerON (char *curlock, int fd)
656 {
657     struct lock *lp;
658 
659     NEW(lp);
660     lp->l_lock = mh_xstrdup(curlock);
661     lp->l_fd = fd;
662     lp->l_next = l_top;
663 
664     if (!l_top) {
665 	/* perhaps SIGT{STP,TIN,TOU} ? */
666 	SIGNAL (SIGALRM, alrmser);
667 	alarm (NSECS);
668     }
669     l_top = lp;
670 }
671 
672 
673 /*
674  * Search through the list of lockfiles for the
675  * current lockfile, and remove it from the list.
676  */
677 
678 static void
timerOFF(int fd)679 timerOFF (int fd)
680 {
681     struct lock *pp, *lp;
682 
683     alarm(0);
684 
685     if (l_top) {
686 	for (pp = lp = l_top; lp; pp = lp, lp = lp->l_next) {
687 	    if (lp->l_fd == fd)
688 		break;
689 	}
690 	if (lp) {
691 	    if (lp == l_top)
692 		l_top = lp->l_next;
693 	    else
694 		pp->l_next = lp->l_next;
695 
696 	    free (lp->l_lock);
697 	    free (lp);
698 	}
699     }
700 
701     /* if there are locks left, restart timer */
702     if (l_top)
703 	alarm (NSECS);
704 }
705 
706 
707 /*
708  * If timer goes off, we update the ctime of all open
709  * lockfiles, so another command doesn't remove them.
710  */
711 
712 static void
alrmser(int sig)713 alrmser (int sig)
714 {
715     char *lockfile;
716     struct lock *lp;
717     NMH_UNUSED (sig);
718 
719     /* update the ctime of all the lock files */
720     for (lp = l_top; lp; lp = lp->l_next) {
721 	lockfile = lp->l_lock;
722 #if !defined(HAVE_LIBLOCKFILE)
723 	{
724 	    int j;
725 	    if (*lockfile && (j = creat (lockfile, 0600)) != -1)
726 		close (j);
727 	}
728 #else
729     lockfile_touch(lockfile);
730 #endif
731     }
732 
733     /* restart the alarm */
734     alarm (NSECS);
735 }
736 
737 
738 /*
739  * Return a locking algorithm based on the string name
740  */
741 
742 static enum locktype
init_locktype(const char * lockname)743 init_locktype(const char *lockname)
744 {
745     if (strcasecmp(lockname, "fcntl") == 0) {
746     	return FCNTL_LOCKING;
747     }
748     if (strcasecmp(lockname, "lockf") == 0) {
749 #ifdef HAVE_LOCKF
750 	return LOCKF_LOCKING;
751 #else /* ! HAVE_LOCKF */
752 	adios(NULL, "lockf not supported on this system");
753 #endif /* HAVE_LOCKF */
754     }
755     if (strcasecmp(lockname, "flock") == 0) {
756 #ifdef HAVE_FLOCK
757 	return FLOCK_LOCKING;
758 #else /* ! HAVE_FLOCK */
759 	adios(NULL, "flock not supported on this system");
760 #endif /* HAVE_FLOCK */
761     }
762     if (strcasecmp(lockname, "dot") == 0) {
763     	return DOT_LOCKING;
764     }
765     adios(NULL, "Unknown lock type: \"%s\"", lockname);
766     /* NOTREACHED */
767     return 0;
768 }
769