]> diplodocus.org Git - nmh/blob - sbr/lock_file.c
Added notes about the configure change.
[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 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
32 extern int errno;
33
34 #ifdef LOCKDIR
35 char *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 */
46 struct 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 */
64 struct lock {
65 int l_fd;
66 char *l_lock;
67 struct lock *l_next;
68 };
69
70 /* top of list containing all open locks */
71 static struct lock *l_top = NULL;
72 #endif /* DOT_LOCKING */
73
74 /*
75 * static prototypes
76 */
77 #ifdef KERNEL_LOCKING
78 static int lkopen_kernel (char *, int, mode_t);
79 #endif
80
81 #ifdef DOT_LOCKING
82 static int lkopen_dot (char *, int, mode_t);
83 static int lockit (struct lockinfo *);
84 static void lockname (char *, struct lockinfo *, int);
85 static void timerON (char *, int);
86 static void timerOFF (int);
87 static 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
96 int
97 lkopen (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
114 int
115 lkclose (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
161 FILE *
162 lkfopen (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
189 int
190 lkfclose (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
237 static int
238 lkopen_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
310 static int
311 lkopen_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
362 static int
363 lockit (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
401 static void
402 lockname (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
466 static void
467 timerON (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
499 static void
500 timerOFF (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
533 static RETSIGTYPE
534 alrmser (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 */