2 ** lock.c -- routines to lock/unlock files
4 ** This code is Copyright (c) 2002, by the authors of nmh. See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
10 ** Modified by Ruud de Rooij to support Miquel van Smoorenburg's liblockfile
12 ** Since liblockfile locking shares most of its code with dot locking, it
13 ** is enabled by defining both DOT_LOCKING and HAVE_LIBLOCKFILE.
15 ** Ruud de Rooij <ruud@debian.org> Sun, 28 Mar 1999 15:34:03 +0200
19 #include <h/signals.h>
22 #ifdef TIME_WITH_SYS_TIME
23 # include <sys/time.h>
26 # ifdef TM_IN_SYS_TIME
27 # include <sys/time.h>
38 # include <sys/file.h>
41 #if defined(LOCKF_LOCKING) || defined(FLOCK_LOCKING)
42 # include <sys/file.h>
47 #if defined(HAVE_LIBLOCKFILE)
52 char *lockdir = LOCKDIR;
55 /* Are we using any kernel locking? */
56 #if defined (FLOCK_LOCKING) || defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
57 # define KERNEL_LOCKING
62 /* struct for getting name of lock file to create */
65 #if !defined(HAVE_LIBLOCKFILE)
71 ** Amount of time to wait before
72 ** updating ctime of lock file.
76 #if !defined(HAVE_LIBLOCKFILE)
78 ** How old does a lock file need to be
79 ** before we remove it.
82 #endif /* HAVE_LIBLOCKFILE */
84 /* struct for recording and updating locks */
91 /* top of list containing all open locks */
92 static struct lock *l_top = NULL;
93 #endif /* DOT_LOCKING */
99 static int lkopen_kernel(char *, int, mode_t);
103 static int lkopen_dot(char *, int, mode_t);
104 static void lockname(char *, struct lockinfo *, int);
105 static void timerON(char *, int);
106 static void timerOFF(int);
107 static RETSIGTYPE alrmser(int);
109 #if !defined(HAVE_LIBLOCKFILE)
110 static int lockit(struct lockinfo *);
115 ** Base routine to open and lock a file,
116 ** and return a file descriptor.
120 lkopen(char *file, int access, mode_t mode)
122 #ifdef KERNEL_LOCKING
123 return lkopen_kernel(file, access, mode);
127 return lkopen_dot(file, access, mode);
133 ** Base routine to close and unlock a file,
134 ** given a file descriptor.
138 lkclose(int fd, char *file)
145 struct lockinfo lkinfo;
152 buf.l_type = F_UNLCK;
153 buf.l_whence = SEEK_SET;
156 fcntl(fd, F_SETLK, &buf);
164 /* make sure we unlock the whole thing */
165 lseek(fd, (off_t) 0, SEEK_SET);
166 lockf(fd, F_ULOCK, 0L);
170 lockname(file, &lkinfo, 0); /* get name of lock file */
171 #if !defined(HAVE_LIBLOCKFILE)
172 unlink(lkinfo.curlock); /* remove lock file */
174 lockfile_remove(lkinfo.curlock);
175 #endif /* HAVE_LIBLOCKFILE */
176 timerOFF(fd); /* turn off lock timer */
177 #endif /* DOT_LOCKING */
184 ** Base routine to open and lock a file,
185 ** and return a FILE pointer
189 lkfopen(char *file, char *mode)
194 if (strcmp(mode, "r") == 0)
196 else if (strcmp(mode, "r+") == 0)
198 else if (strcmp(mode, "w") == 0)
199 access = O_WRONLY | O_CREAT | O_TRUNC;
200 else if (strcmp(mode, "w+") == 0)
201 access = O_RDWR | O_CREAT | O_TRUNC;
202 else if (strcmp(mode, "a") == 0)
203 access = O_WRONLY | O_CREAT | O_APPEND;
204 else if (strcmp(mode, "a+") == 0)
205 access = O_RDWR | O_CREAT | O_APPEND;
211 if ((fd = lkopen(file, access, 0666)) == -1)
214 if ((fp = fdopen(fd, mode)) == NULL) {
224 ** Base routine to close and unlock a file,
225 ** given a FILE pointer
229 lkfclose(FILE *fp, char *file)
236 struct lockinfo lkinfo;
243 buf.l_type = F_UNLCK;
244 buf.l_whence = SEEK_SET;
247 fcntl(fileno(fp), F_SETLK, &buf);
251 flock(fileno(fp), LOCK_UN);
255 /* make sure we unlock the whole thing */
256 fseek(fp, 0L, SEEK_SET);
257 lockf(fileno(fp), F_ULOCK, 0L);
261 lockname(file, &lkinfo, 0); /* get name of lock file */
262 #if !defined(HAVE_LIBLOCKFILE)
263 unlink(lkinfo.curlock); /* remove lock file */
265 lockfile_remove(lkinfo.curlock);
266 #endif /* HAVE_LIBLOCKFILE */
267 timerOFF(fileno(fp)); /* turn off lock timer */
268 #endif /* DOT_LOCKING */
274 #ifdef KERNEL_LOCKING
277 ** open and lock a file, using kernel locking
281 lkopen_kernel(char *file, int access, mode_t mode)
285 # ifdef FCNTL_LOCKING
287 # endif /* FCNTL_LOCKING */
289 for (i = 0; i < 5; i++) {
291 # if defined(LOCKF_LOCKING) || defined(FCNTL_LOCKING)
292 /* remember the original mode */
295 /* make sure we open at the beginning */
299 ** We MUST have write permission or
300 ** lockf/fcntl() won't work
302 if ((access & 03) == O_RDONLY) {
306 # endif /* LOCKF_LOCKING || FCNTL_LOCKING */
308 if ((fd = open(file, access | O_NDELAY, mode)) == -1)
311 # ifdef FCNTL_LOCKING
312 buf.l_type = F_WRLCK;
313 buf.l_whence = SEEK_SET;
316 if (fcntl(fd, F_SETLK, &buf) != -1)
320 # ifdef FLOCK_LOCKING
321 if (flock(fd, (((access & 03) == O_RDONLY) ? LOCK_SH :
322 LOCK_EX) | LOCK_NB) != -1)
326 # ifdef LOCKF_LOCKING
327 if (lockf(fd, F_TLOCK, 0L) != -1) {
328 /* see if we should be at the end */
330 lseek(fd, (off_t) 0, SEEK_END);
345 #endif /* KERNEL_LOCKING */
351 ** open and lock a file, using dot locking
355 lkopen_dot(char *file, int access, mode_t mode)
358 struct lockinfo lkinfo;
361 if ((fd = open(file, access, mode)) == -1)
365 ** Get the name of the eventual lock file, as well
366 ** as a name for a temporary lock file.
368 lockname(file, &lkinfo, 1);
370 #if !defined(HAVE_LIBLOCKFILE)
374 /* attempt to create lock file */
375 if (lockit(&lkinfo) == 0) {
376 /* if successful, turn on timer and return */
377 timerON(lkinfo.curlock, fd);
381 ** Abort locking, if we fail to lock after 5
382 ** attempts and are never able to stat the
386 if (stat(lkinfo.curlock, &st) == -1) {
396 ** check for stale lockfile,
399 if (curtime > st.st_ctime + RSECS)
400 unlink(lkinfo.curlock);
404 lockname(file, &lkinfo, 1);
409 if (lockfile_create(lkinfo.curlock, 5, 0) == L_SUCCESS) {
410 timerON(lkinfo.curlock, fd);
416 #endif /* HAVE_LIBLOCKFILE */
419 #if !defined(HAVE_LIBLOCKFILE)
421 ** Routine that actually tries to create
426 lockit(struct lockinfo *li)
429 char *curlock, *tmplock;
435 curlock = li->curlock;
436 tmplock = li->tmplock;
438 if ((fd = mkstemp(tmplock)) == -1)
442 /* write our process id into lock file */
443 snprintf(buffer, sizeof(buffer), "nmh lock: pid %d\n", (int) getpid());
444 write(fd, buffer, strlen(buffer) + 1);
450 ** Now try to create the real lock file
451 ** by linking to the temporary file.
453 fd = link(tmplock, curlock);
456 return (fd == -1 ? -1 : 0);
458 #endif /* HAVE_LIBLOCKFILE */
461 ** Get name of lock file, and temporary lock file
465 lockname(char *file, struct lockinfo *li, int isnewlock)
470 if ((cp = strrchr(file, '/')) == NULL || *++cp == 0)
476 snprintf(bp, sizeof(li->curlock), "%s/", lockdir);
482 snprintf(bp, sizeof(li->curlock), "%.*s", (int)(cp - file), file);
489 snprintf(bp, sizeof(li->curlock) - bplen, "%s.lock", cp);
491 #if !defined(HAVE_LIBLOCKFILE)
493 ** If this is for a new lock, create a name for
494 ** the temporary lock file for lockit()
497 if ((cp = strrchr(li->curlock, '/')) == NULL || *++cp == 0)
498 strncpy(li->tmplock, ",LCK.XXXXXX", sizeof(li->tmplock));
500 snprintf(li->tmplock, sizeof(li->tmplock),
502 (int)(cp - li->curlock), li->curlock);
509 ** Add new lockfile to the list of open lockfiles
510 ** and start the lock file timer.
514 timerON(char *curlock, int fd)
519 lp = (struct lock *) mh_xmalloc(sizeof(*lp));
521 len = strlen(curlock) + 1;
523 lp->l_lock = mh_xmalloc(len);
524 memcpy(lp->l_lock, curlock, len);
528 /* perhaps SIGT{STP,TIN,TOU} ? */
529 SIGNAL(SIGALRM, alrmser);
538 ** Search through the list of lockfiles for the
539 ** current lockfile, and remove it from the list.
545 struct lock *pp, *lp;
550 for (pp = lp = l_top; lp; pp = lp, lp = lp->l_next) {
558 pp->l_next = lp->l_next;
565 /* if there are locks left, restart timer */
572 ** If timer goes off, we update the ctime of all open
573 ** lockfiles, so another command doesn't remove them.
582 #ifndef RELIABLE_SIGNALS
583 SIGNAL(SIGALRM, alrmser);
586 /* update the ctime of all the lock files */
587 for (lp = l_top; lp; lp = lp->l_next) {
588 lockfile = lp->l_lock;
589 #if !defined(HAVE_LIBLOCKFILE)
592 if (*lockfile && (j = creat(lockfile, 0600)) != -1)
596 lockfile_touch(lockfile);
600 /* restart the alarm */
604 #endif /* DOT_LOCKING */