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