xref: /netbsd-src/usr.bin/mail/dotlock.c (revision 1ca5c1b28139779176bd5c13ad7c5f25c0bcd5f8)
1 /*	$NetBSD: dotlock.c,v 1.4 1998/07/06 06:51:55 mrg 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 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: dotlock.c,v 1.4 1998/07/06 06:51:55 mrg Exp $");
35 #endif
36 
37 #include "rcv.h"
38 #include "extern.h"
39 
40 #ifndef O_SYNC
41 #define O_SYNC	0
42 #endif
43 
44 static int create_exclusive __P((const char *));
45 /*
46  * Create a unique file. O_EXCL does not really work over NFS so we follow
47  * the following trick: [Inspired by  S.R. van den Berg]
48  *
49  * - make a mostly unique filename and try to create it.
50  * - link the unique filename to our target
51  * - get the link count of the target
52  * - unlink the mostly unique filename
53  * - if the link count was 2, then we are ok; else we've failed.
54  */
55 static int
56 create_exclusive(fname)
57 	const char *fname;
58 {
59 	char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN + 1];
60 	const char *ptr;
61 	struct timeval tv;
62 	pid_t pid;
63 	size_t ntries, cookie;
64 	int fd, serrno;
65 	struct stat st;
66 
67 	(void)gettimeofday(&tv, NULL);
68 	(void)gethostname(hostname, sizeof hostname);
69 	hostname[sizeof(hostname) - 1] = '\0';
70 	pid = getpid();
71 
72 	cookie = pid ^ tv.tv_usec;
73 
74 	/*
75 	 * We generate a semi-unique filename, from hostname.(pid ^ usec)
76 	 */
77 	if ((ptr = strrchr(fname, '/')) == NULL)
78 		ptr = fname;
79 	else
80 		ptr++;
81 
82 	(void) snprintf(path, sizeof(path), "%.*s.%s.%lx",
83 	    (int)(ptr - fname), fname, hostname, (u_long)cookie);
84 
85 	/*
86 	 * We try to create the unique filename.
87 	 */
88 	for (ntries = 0; ntries < 5; ntries++) {
89 		fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0);
90 		if (fd != -1) {
91 			(void) close(fd);
92 			break;
93 		}
94 		else if (errno == EEXIST)
95 			continue;
96 		else
97 			return -1;
98 	}
99 
100 	/*
101 	 * We link the path to the name
102 	 */
103 	if (link(path, fname) == -1)
104 		goto bad;
105 
106 	/*
107 	 * Note that we stat our own exclusively created name, not the
108 	 * destination, since the destination can be affected by others.
109 	 */
110 	if (stat(path, &st) == -1)
111 		goto bad;
112 
113 	(void) unlink(path);
114 
115 	/*
116 	 * If the number of links was two (one for the unique file and one
117 	 * for the lock), we've won the race
118 	 */
119 	if (st.st_nlink != 2) {
120 		errno = EEXIST;
121 		return -1;
122 	}
123 	return 0;
124 
125 bad:
126 	serrno = errno;
127 	(void) unlink(path);
128 	errno = serrno;
129 	return -1;
130 }
131 
132 int
133 dot_lock(fname, pollinterval, fp, msg)
134 	const char *fname;	/* Pathname to lock */
135 	int pollinterval;	/* Interval to check for lock, -1 return */
136 	FILE *fp;		/* File to print message */
137 	const char *msg;	/* Message to print */
138 {
139 	char path[MAXPATHLEN];
140 	sigset_t nset, oset;
141 
142 	sigemptyset(&nset);
143 	sigaddset(&nset, SIGHUP);
144 	sigaddset(&nset, SIGINT);
145 	sigaddset(&nset, SIGQUIT);
146 	sigaddset(&nset, SIGTERM);
147 	sigaddset(&nset, SIGTTIN);
148 	sigaddset(&nset, SIGTTOU);
149 	sigaddset(&nset, SIGTSTP);
150 	sigaddset(&nset, SIGCHLD);
151 
152 	(void) snprintf(path, sizeof(path), "%s.lock", fname);
153 
154 	for (;;) {
155 		(void) sigprocmask(SIG_BLOCK, &nset, &oset);
156 		if (create_exclusive(path) != -1) {
157 			(void) sigprocmask(SIG_SETMASK, &oset, NULL);
158 			return 0;
159 		}
160 		else
161 			(void) sigprocmask(SIG_SETMASK, &oset, NULL);
162 
163 		if (errno != EEXIST)
164 			return -1;
165 
166 		if (fp && msg)
167 		    (void) fputs(msg, fp);
168 
169 		if (pollinterval) {
170 			if (pollinterval == -1) {
171 				errno = EEXIST;
172 				return -1;
173 			}
174 			sleep(pollinterval);
175 		}
176 	}
177 }
178 
179 void
180 dot_unlock(fname)
181 	const char *fname;
182 {
183 	char path[MAXPATHLEN];
184 
185 	(void) snprintf(path, sizeof(path), "%s.lock", fname);
186 	(void) unlink(path);
187 }
188