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