xref: /netbsd-src/usr.bin/mail/dotlock.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
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