dotlock.c revision 315512
1315512Sdchagin/* $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" 27315512SdchaginRCSID("$tcsh: dotlock.c,v 3.4 2015/11/03 21:04:13 christos Exp $") 28315512Sdchagin 29315512Sdchagin#include <stdio.h> 30315512Sdchagin#ifndef O_SYNC 31315512Sdchagin#define O_SYNC 0 32315512Sdchagin#endif 33315512Sdchagin 34315512Sdchagin#include "dotlock.h" 35315512Sdchagin 36315512Sdchaginstatic int create_exclusive(const char *); 37315512Sdchagin/* 38315512Sdchagin * Create a unique file. O_EXCL does not really work over NFS so we follow 39315512Sdchagin * the following trick: [Inspired by S.R. van den Berg] 40315512Sdchagin * 41315512Sdchagin * - make a mostly unique filename and try to create it. 42315512Sdchagin * - link the unique filename to our target 43315512Sdchagin * - get the link count of the target 44315512Sdchagin * - unlink the mostly unique filename 45315512Sdchagin * - if the link count was 2, then we are ok; else we've failed. 46315512Sdchagin */ 47315512Sdchaginstatic int 48315512Sdchagincreate_exclusive(const char *fname) 49315512Sdchagin{ 50315512Sdchagin char path[MAXPATHLEN], hostname[MAXHOSTNAMELEN + 1]; 51315512Sdchagin const char *ptr; 52315512Sdchagin struct timeval tv; 53315512Sdchagin pid_t pid; 54315512Sdchagin size_t ntries, cookie; 55315512Sdchagin int fd, serrno; 56315512Sdchagin struct stat st; 57315512Sdchagin 58315512Sdchagin (void)gettimeofday(&tv, NULL); 59315512Sdchagin (void)gethostname(hostname, sizeof(hostname)); 60315512Sdchagin hostname[sizeof(hostname) - 1] = '\0'; 61315512Sdchagin pid = getpid(); 62315512Sdchagin 63315512Sdchagin cookie = pid ^ tv.tv_usec; 64315512Sdchagin 65315512Sdchagin /* 66315512Sdchagin * We generate a semi-unique filename, from hostname.(pid ^ usec) 67315512Sdchagin */ 68315512Sdchagin if ((ptr = strrchr(fname, '/')) == NULL) 69315512Sdchagin ptr = fname; 70315512Sdchagin else 71315512Sdchagin ptr++; 72315512Sdchagin 73315512Sdchagin (void)snprintf(path, sizeof(path), "%.*s.%s.%lx", 74315512Sdchagin (int)(ptr - fname), fname, hostname, (u_long)cookie); 75315512Sdchagin 76315512Sdchagin /* 77315512Sdchagin * We try to create the unique filename. 78315512Sdchagin */ 79315512Sdchagin for (ntries = 0; ntries < 5; ntries++) { 80315512Sdchagin fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0); 81315512Sdchagin if (fd != -1) { 82315512Sdchagin (void)close(fd); 83315512Sdchagin break; 84315512Sdchagin } 85315512Sdchagin else if (errno == EEXIST) 86315512Sdchagin continue; 87315512Sdchagin else 88315512Sdchagin return -1; 89315512Sdchagin } 90315512Sdchagin 91315512Sdchagin /* 92315512Sdchagin * We link the path to the name 93315512Sdchagin */ 94315512Sdchagin if (link(path, fname) == -1) 95315512Sdchagin goto bad; 96315512Sdchagin 97315512Sdchagin /* 98315512Sdchagin * Note that we stat our own exclusively created name, not the 99315512Sdchagin * destination, since the destination can be affected by others. 100315512Sdchagin */ 101315512Sdchagin if (stat(path, &st) == -1) 102315512Sdchagin goto bad; 103315512Sdchagin 104315512Sdchagin (void)unlink(path); 105315512Sdchagin 106315512Sdchagin /* 107315512Sdchagin * If the number of links was two (one for the unique file and one 108315512Sdchagin * for the lock), we've won the race 109315512Sdchagin */ 110315512Sdchagin if (st.st_nlink != 2) { 111315512Sdchagin errno = EEXIST; 112315512Sdchagin return -1; 113315512Sdchagin } 114315512Sdchagin return 0; 115315512Sdchagin 116315512Sdchaginbad: 117315512Sdchagin serrno = errno; 118315512Sdchagin (void)unlink(path); 119315512Sdchagin errno = serrno; 120315512Sdchagin return -1; 121315512Sdchagin} 122315512Sdchagin 123315512Sdchagin/* 124315512Sdchagin * fname -- Pathname to lock 125315512Sdchagin * pollinterval -- Interval (miliseconds) to check for lock, -1 return 126315512Sdchagin */ 127315512Sdchaginint 128315512Sdchagindot_lock(const char *fname, int pollinterval) 129315512Sdchagin{ 130315512Sdchagin char path[MAXPATHLEN]; 131315512Sdchagin sigset_t nset, oset; 132315512Sdchagin int retval; 133315512Sdchagin 134315512Sdchagin (void)sigemptyset(&nset); 135315512Sdchagin (void)sigaddset(&nset, SIGHUP); 136315512Sdchagin (void)sigaddset(&nset, SIGINT); 137315512Sdchagin (void)sigaddset(&nset, SIGQUIT); 138315512Sdchagin (void)sigaddset(&nset, SIGTERM); 139315512Sdchagin (void)sigaddset(&nset, SIGTTIN); 140315512Sdchagin (void)sigaddset(&nset, SIGTTOU); 141315512Sdchagin (void)sigaddset(&nset, SIGTSTP); 142315512Sdchagin (void)sigaddset(&nset, SIGCHLD); 143315512Sdchagin 144315512Sdchagin (void)snprintf(path, sizeof(path), "%s.lock", fname); 145315512Sdchagin 146315512Sdchagin retval = -1; 147315512Sdchagin for (;;) { 148315512Sdchagin handle_pending_signals(); 149315512Sdchagin (void)sigprocmask(SIG_BLOCK, &nset, &oset); 150315512Sdchagin if (create_exclusive(path) != -1) { 151315512Sdchagin (void)sigprocmask(SIG_SETMASK, &oset, NULL); 152315512Sdchagin retval = 0; 153315512Sdchagin break; 154315512Sdchagin } 155315512Sdchagin else 156315512Sdchagin (void)sigprocmask(SIG_SETMASK, &oset, NULL); 157315512Sdchagin 158315512Sdchagin if (errno != EEXIST) 159315512Sdchagin break; 160315512Sdchagin 161315512Sdchagin if (pollinterval) { 162315512Sdchagin if (pollinterval == -1) { 163315512Sdchagin errno = EEXIST; 164315512Sdchagin break; 165315512Sdchagin } 166315512Sdchagin (void)usleep((unsigned int)pollinterval * 1000); 167315512Sdchagin } 168315512Sdchagin } 169315512Sdchagin handle_pending_signals(); 170315512Sdchagin return retval; 171315512Sdchagin} 172315512Sdchagin 173315512Sdchaginvoid 174315512Sdchagindot_unlock(const char *fname) 175315512Sdchagin{ 176315512Sdchagin char path[MAXPATHLEN]; 177315512Sdchagin 178315512Sdchagin (void)snprintf(path, sizeof(path), "%s.lock", fname); 179315512Sdchagin (void)unlink(path); 180315512Sdchagin} 181