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