1 /* $NetBSD: dotlock.c,v 1.11 2009/10/21 01:07:46 snj 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 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 #include "sh.h" 27 RCSID("$tcsh: dotlock.c,v 3.4 2015/11/03 21:04:13 christos Exp $") 28 29 #include <stdio.h> 30 #ifndef O_SYNC 31 #define O_SYNC 0 32 #endif 33 34 #include "dotlock.h" 35 36 static int create_exclusive(const char *); 37 /* 38 * Create a unique file. O_EXCL does not really work over NFS so we follow 39 * the following trick: [Inspired by S.R. van den Berg] 40 * 41 * - make a mostly unique filename and try to create it. 42 * - link the unique filename to our target 43 * - get the link count of the target 44 * - unlink the mostly unique filename 45 * - if the link count was 2, then we are ok; else we've failed. 46 */ 47 static int 48 create_exclusive(const char *fname) 49 { 50 char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN + 1]; 51 const char *ptr; 52 struct timeval tv; 53 pid_t pid; 54 size_t ntries, cookie; 55 int fd, serrno; 56 struct stat st; 57 58 (void)gettimeofday(&tv, NULL); 59 (void)gethostname(hostname, sizeof(hostname)); 60 hostname[sizeof(hostname) - 1] = '\0'; 61 pid = getpid(); 62 63 cookie = pid ^ tv.tv_usec; 64 65 /* 66 * We generate a semi-unique filename, from hostname.(pid ^ usec) 67 */ 68 if ((ptr = strrchr(fname, '/')) == NULL) 69 ptr = fname; 70 else 71 ptr++; 72 73 (void)snprintf(path, sizeof(path), "%.*s.%s.%lx", 74 (int)(ptr - fname), fname, hostname, (u_long)cookie); 75 76 /* 77 * We try to create the unique filename. 78 */ 79 for (ntries = 0; ntries < 5; ntries++) { 80 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0); 81 if (fd != -1) { 82 (void)close(fd); 83 break; 84 } 85 else if (errno == EEXIST) 86 continue; 87 else 88 return -1; 89 } 90 91 /* 92 * We link the path to the name 93 */ 94 if (link(path, fname) == -1) 95 goto bad; 96 97 /* 98 * Note that we stat our own exclusively created name, not the 99 * destination, since the destination can be affected by others. 100 */ 101 if (stat(path, &st) == -1) 102 goto bad; 103 104 (void)unlink(path); 105 106 /* 107 * If the number of links was two (one for the unique file and one 108 * for the lock), we've won the race 109 */ 110 if (st.st_nlink != 2) { 111 errno = EEXIST; 112 return -1; 113 } 114 return 0; 115 116 bad: 117 serrno = errno; 118 (void)unlink(path); 119 errno = serrno; 120 return -1; 121 } 122 123 /* 124 * fname -- Pathname to lock 125 * pollinterval -- Interval (miliseconds) to check for lock, -1 return 126 */ 127 int 128 dot_lock(const char *fname, int pollinterval) 129 { 130 char path[MAXPATHLEN]; 131 sigset_t nset, oset; 132 int retval; 133 134 (void)sigemptyset(&nset); 135 (void)sigaddset(&nset, SIGHUP); 136 (void)sigaddset(&nset, SIGINT); 137 (void)sigaddset(&nset, SIGQUIT); 138 (void)sigaddset(&nset, SIGTERM); 139 (void)sigaddset(&nset, SIGTTIN); 140 (void)sigaddset(&nset, SIGTTOU); 141 (void)sigaddset(&nset, SIGTSTP); 142 (void)sigaddset(&nset, SIGCHLD); 143 144 (void)snprintf(path, sizeof(path), "%s.lock", fname); 145 146 retval = -1; 147 for (;;) { 148 handle_pending_signals(); 149 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 150 if (create_exclusive(path) != -1) { 151 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 152 retval = 0; 153 break; 154 } 155 else 156 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 157 158 if (errno != EEXIST) 159 break; 160 161 if (pollinterval) { 162 if (pollinterval == -1) { 163 errno = EEXIST; 164 break; 165 } 166 (void)usleep((unsigned int)pollinterval * 1000); 167 } 168 } 169 handle_pending_signals(); 170 return retval; 171 } 172 173 void 174 dot_unlock(const char *fname) 175 { 176 char path[MAXPATHLEN]; 177 178 (void)snprintf(path, sizeof(path), "%s.lock", fname); 179 (void)unlink(path); 180 } 181