1354195Sbrooks/* NetBSD: dotlock.c,v 1.11 2009/10/21 01:07:46 snj Exp */ 2315512Sdchagin 3315512Sdchagin/* 4315512Sdchagin * Copyright (c) 1996 Christos Zoulas. All rights reserved. 5315512Sdchagin * 6315512Sdchagin * Redistribution and use in source and binary forms, with or without 7315512Sdchagin * modification, are permitted provided that the following conditions 8315512Sdchagin * are met: 9315512Sdchagin * 1. Redistributions of source code must retain the above copyright 10315512Sdchagin * notice, this list of conditions and the following disclaimer. 11315512Sdchagin * 2. Redistributions in binary form must reproduce the above copyright 12315512Sdchagin * notice, this list of conditions and the following disclaimer in the 13315512Sdchagin * documentation and/or other materials provided with the distribution. 14315512Sdchagin * 15315512Sdchagin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16315512Sdchagin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17315512Sdchagin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18315512Sdchagin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19315512Sdchagin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20315512Sdchagin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21315512Sdchagin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22315512Sdchagin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23315512Sdchagin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24315512Sdchagin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25315512Sdchagin */ 26315512Sdchagin#include "sh.h" 27315512Sdchagin 28315512Sdchagin#include <stdio.h> 29315512Sdchagin#ifndef O_SYNC 30315512Sdchagin#define O_SYNC 0 31315512Sdchagin#endif 32315512Sdchagin 33315512Sdchagin#include "dotlock.h" 34315512Sdchagin 35315512Sdchaginstatic int create_exclusive(const char *); 36315512Sdchagin/* 37315512Sdchagin * Create a unique file. O_EXCL does not really work over NFS so we follow 38315512Sdchagin * the following trick: [Inspired by S.R. van den Berg] 39315512Sdchagin * 40315512Sdchagin * - make a mostly unique filename and try to create it. 41315512Sdchagin * - link the unique filename to our target 42315512Sdchagin * - get the link count of the target 43315512Sdchagin * - unlink the mostly unique filename 44315512Sdchagin * - if the link count was 2, then we are ok; else we've failed. 45315512Sdchagin */ 46315512Sdchaginstatic int 47315512Sdchagincreate_exclusive(const char *fname) 48315512Sdchagin{ 49315512Sdchagin char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN + 1]; 50315512Sdchagin const char *ptr; 51315512Sdchagin struct timeval tv; 52315512Sdchagin pid_t pid; 53315512Sdchagin size_t ntries, cookie; 54315512Sdchagin int fd, serrno; 55315512Sdchagin struct stat st; 56315512Sdchagin 57315512Sdchagin (void)gettimeofday(&tv, NULL); 58315512Sdchagin (void)gethostname(hostname, sizeof(hostname)); 59315512Sdchagin hostname[sizeof(hostname) - 1] = '\0'; 60315512Sdchagin pid = getpid(); 61315512Sdchagin 62315512Sdchagin cookie = pid ^ tv.tv_usec; 63315512Sdchagin 64315512Sdchagin /* 65315512Sdchagin * We generate a semi-unique filename, from hostname.(pid ^ usec) 66315512Sdchagin */ 67315512Sdchagin if ((ptr = strrchr(fname, '/')) == NULL) 68315512Sdchagin ptr = fname; 69315512Sdchagin else 70315512Sdchagin ptr++; 71315512Sdchagin 72315512Sdchagin (void)snprintf(path, sizeof(path), "%.*s.%s.%lx", 73315512Sdchagin (int)(ptr - fname), fname, hostname, (u_long)cookie); 74315512Sdchagin 75315512Sdchagin /* 76315512Sdchagin * We try to create the unique filename. 77315512Sdchagin */ 78315512Sdchagin for (ntries = 0; ntries < 5; ntries++) { 79315512Sdchagin fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0); 80315512Sdchagin if (fd != -1) { 81315512Sdchagin (void)close(fd); 82315512Sdchagin break; 83315512Sdchagin } 84315512Sdchagin else if (errno == EEXIST) 85315512Sdchagin continue; 86315512Sdchagin else 87315512Sdchagin return -1; 88315512Sdchagin } 89315512Sdchagin 90315512Sdchagin /* 91315512Sdchagin * We link the path to the name 92315512Sdchagin */ 93315512Sdchagin if (link(path, fname) == -1) 94315512Sdchagin goto bad; 95315512Sdchagin 96315512Sdchagin /* 97315512Sdchagin * Note that we stat our own exclusively created name, not the 98315512Sdchagin * destination, since the destination can be affected by others. 99315512Sdchagin */ 100315512Sdchagin if (stat(path, &st) == -1) 101315512Sdchagin goto bad; 102315512Sdchagin 103315512Sdchagin (void)unlink(path); 104315512Sdchagin 105315512Sdchagin /* 106315512Sdchagin * If the number of links was two (one for the unique file and one 107315512Sdchagin * for the lock), we've won the race 108315512Sdchagin */ 109315512Sdchagin if (st.st_nlink != 2) { 110315512Sdchagin errno = EEXIST; 111315512Sdchagin return -1; 112315512Sdchagin } 113315512Sdchagin return 0; 114315512Sdchagin 115315512Sdchaginbad: 116315512Sdchagin serrno = errno; 117315512Sdchagin (void)unlink(path); 118315512Sdchagin errno = serrno; 119315512Sdchagin return -1; 120315512Sdchagin} 121315512Sdchagin 122315512Sdchagin/* 123315512Sdchagin * fname -- Pathname to lock 124315512Sdchagin * pollinterval -- Interval (miliseconds) to check for lock, -1 return 125315512Sdchagin */ 126315512Sdchaginint 127315512Sdchagindot_lock(const char *fname, int pollinterval) 128315512Sdchagin{ 129315512Sdchagin char path[MAXPATHLEN]; 130315512Sdchagin sigset_t nset, oset; 131315512Sdchagin int retval; 132315512Sdchagin 133315512Sdchagin (void)sigemptyset(&nset); 134315512Sdchagin (void)sigaddset(&nset, SIGHUP); 135315512Sdchagin (void)sigaddset(&nset, SIGINT); 136315512Sdchagin (void)sigaddset(&nset, SIGQUIT); 137315512Sdchagin (void)sigaddset(&nset, SIGTERM); 138315512Sdchagin (void)sigaddset(&nset, SIGTTIN); 139315512Sdchagin (void)sigaddset(&nset, SIGTTOU); 140315512Sdchagin (void)sigaddset(&nset, SIGTSTP); 141315512Sdchagin (void)sigaddset(&nset, SIGCHLD); 142315512Sdchagin 143315512Sdchagin (void)snprintf(path, sizeof(path), "%s.lock", fname); 144315512Sdchagin 145315512Sdchagin retval = -1; 146315512Sdchagin for (;;) { 147315512Sdchagin handle_pending_signals(); 148315512Sdchagin (void)sigprocmask(SIG_BLOCK, &nset, &oset); 149315512Sdchagin if (create_exclusive(path) != -1) { 150315512Sdchagin (void)sigprocmask(SIG_SETMASK, &oset, NULL); 151315512Sdchagin retval = 0; 152315512Sdchagin break; 153315512Sdchagin } 154315512Sdchagin else 155315512Sdchagin (void)sigprocmask(SIG_SETMASK, &oset, NULL); 156315512Sdchagin 157315512Sdchagin if (errno != EEXIST) 158315512Sdchagin break; 159315512Sdchagin 160315512Sdchagin if (pollinterval) { 161315512Sdchagin if (pollinterval == -1) { 162315512Sdchagin errno = EEXIST; 163315512Sdchagin break; 164315512Sdchagin } 165315512Sdchagin (void)usleep((unsigned int)pollinterval * 1000); 166315512Sdchagin } 167315512Sdchagin } 168315512Sdchagin handle_pending_signals(); 169315512Sdchagin return retval; 170315512Sdchagin} 171315512Sdchagin 172315512Sdchaginvoid 173315512Sdchagindot_unlock(const char *fname) 174315512Sdchagin{ 175315512Sdchagin char path[MAXPATHLEN]; 176315512Sdchagin 177315512Sdchagin (void)snprintf(path, sizeof(path), "%s.lock", fname); 178315512Sdchagin (void)unlink(path); 179315512Sdchagin} 180