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