Home | History | Annotate | Line # | Download | only in mail
dotlock.c revision 1.1
      1 /*	$NetBSD: dotlock.c,v 1.1 1996/06/08 19:48:19 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1996 Christos Zoulas.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *	This product includes software developed by Christos Zoulas.
     17  * 4. The name of the author may not be used to endorse or promote products
     18  *    derived from this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #ifndef lint
     33 static char rcsid[] = "$NetBSD: dotlock.c,v 1.1 1996/06/08 19:48:19 christos Exp $";
     34 #endif
     35 
     36 #include <sys/types.h>
     37 #include <sys/param.h>
     38 #include <sys/stat.h>
     39 #include <sys/time.h>
     40 
     41 #include <stdio.h>
     42 #include <string.h>
     43 #include <fcntl.h>
     44 #include <stdlib.h>
     45 #include <unistd.h>
     46 #include <errno.h>
     47 #include <signal.h>
     48 
     49 #include "extern.h"
     50 
     51 #ifndef O_SYNC
     52 #define O_SYNC	0
     53 #endif
     54 
     55 static int create_exclusive __P((const char *));
     56 /*
     57  * Create a unique file. O_EXCL does not really work over NFS so we follow
     58  * the following trick: [Inspired by  S.R. van den Berg]
     59  *
     60  * - make a mostly unique filename and try to create it.
     61  * - link the unique filename to our target
     62  * - get the link count of the target
     63  * - unlink the mostly unique filename
     64  * - if the link count was 2, then we are ok; else we've failed.
     65  */
     66 static int
     67 create_exclusive(fname)
     68 	const char *fname;
     69 {
     70 	char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN];
     71 	const char *ptr;
     72 	struct timeval tv;
     73 	pid_t pid;
     74 	size_t ntries, cookie;
     75 	int fd, serrno;
     76 	struct stat st;
     77 
     78 	(void) gettimeofday(&tv, NULL);
     79 	(void) gethostname(hostname, MAXHOSTNAMELEN);
     80 	pid = getpid();
     81 
     82 	cookie = pid ^ tv.tv_usec;
     83 
     84 	/*
     85 	 * We generate a semi-unique filename, from hostname.(pid ^ usec)
     86 	 */
     87 	if ((ptr = strrchr(fname, '/')) == NULL)
     88 		ptr = fname;
     89 	else
     90 		ptr++;
     91 
     92 	(void) snprintf(path, sizeof(path), "%.*s.%s.%x",
     93 	    ptr - fname, fname, hostname, cookie);
     94 
     95 	/*
     96 	 * We try to create the unique filename.
     97 	 */
     98 	for (ntries = 0; ntries < 5; ntries++) {
     99 		fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0);
    100 		if (fd != -1) {
    101 			(void) close(fd);
    102 			break;
    103 		}
    104 		else if (errno == EEXIST)
    105 			continue;
    106 		else
    107 			return -1;
    108 	}
    109 
    110 	/*
    111 	 * We link the path to the name
    112 	 */
    113 	if (link(path, fname) == -1)
    114 		goto bad;
    115 
    116 	/*
    117 	 * Note that we stat our own exclusively created name, not the
    118 	 * destination, since the destination can be affected by others.
    119 	 */
    120 	if (stat(path, &st) == -1)
    121 		goto bad;
    122 
    123 	(void) unlink(path);
    124 
    125 	/*
    126 	 * If the number of links was two (one for the unique file and one
    127 	 * for the lock), we've won the race
    128 	 */
    129 	if (st.st_nlink != 2) {
    130 		errno = EEXIST;
    131 		return -1;
    132 	}
    133 	return 0;
    134 
    135 bad:
    136 	serrno = errno;
    137 	(void) unlink(path);
    138 	errno = serrno;
    139 	return -1;
    140 }
    141 
    142 int
    143 dot_lock(fname, pollinterval, fp, msg)
    144 	const char *fname;	/* Pathname to lock */
    145 	int pollinterval;	/* Interval to check for lock, -1 return */
    146 	FILE *fp;		/* File to print message */
    147 	const char *msg;	/* Message to print */
    148 {
    149 	char path[MAXPATHLEN];
    150 	sigset_t nset, oset;
    151 
    152 	sigemptyset(&nset);
    153 	sigaddset(&nset, SIGHUP);
    154 	sigaddset(&nset, SIGINT);
    155 	sigaddset(&nset, SIGQUIT);
    156 	sigaddset(&nset, SIGTERM);
    157 	sigaddset(&nset, SIGTTIN);
    158 	sigaddset(&nset, SIGTTOU);
    159 	sigaddset(&nset, SIGTSTP);
    160 	sigaddset(&nset, SIGCHLD);
    161 
    162 	(void) snprintf(path, sizeof(path), "%s.lock", fname);
    163 
    164 	for (;;) {
    165 		(void) sigprocmask(SIG_BLOCK, &nset, &oset);
    166 		if (create_exclusive(path) != -1) {
    167 			(void) sigprocmask(SIG_SETMASK, &oset, NULL);
    168 			return 0;
    169 		}
    170 		else
    171 			(void) sigprocmask(SIG_SETMASK, &oset, NULL);
    172 
    173 		if (errno != EEXIST)
    174 			return -1;
    175 
    176 		if (fp && msg)
    177 		    (void) fputs(msg, fp);
    178 
    179 		if (pollinterval) {
    180 			if (pollinterval == -1) {
    181 				errno = EEXIST;
    182 				return -1;
    183 			}
    184 			sleep(pollinterval);
    185 		}
    186 	}
    187 }
    188 
    189 void
    190 dot_unlock(fname)
    191 	const char *fname;
    192 {
    193 	char path[MAXPATHLEN];
    194 
    195 	(void) snprintf(path, sizeof(path), "%s.lock", fname);
    196 	(void) unlink(path);
    197 }
    198