lockfile.c (4023B)
1 /* Copyright (c) 2003-2004, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file lockfile.c 8 * 9 * \brief Implements lock files to prevent two Tor processes from using the 10 * same data directory at the same time. 11 **/ 12 13 #include "orconfig.h" 14 #include "lib/fs/files.h" 15 #include "lib/fs/lockfile.h" 16 #include "lib/log/log.h" 17 #include "lib/log/util_bug.h" 18 #include "lib/malloc/malloc.h" 19 20 #ifdef HAVE_SYS_FILE_H 21 #include <sys/file.h> 22 #endif 23 #ifdef HAVE_FCNTL_H 24 #include <fcntl.h> 25 #endif 26 #ifdef HAVE_UNISTD_H 27 #include <unistd.h> 28 #endif 29 #ifdef _WIN32 30 #include <windows.h> 31 #include <sys/locking.h> 32 #endif 33 34 #include <errno.h> 35 #include <string.h> 36 37 /** Represents a lockfile on which we hold the lock. */ 38 struct tor_lockfile_t { 39 /** Name of the file */ 40 char *filename; 41 /** File descriptor used to hold the file open */ 42 int fd; 43 }; 44 45 /** Try to get a lock on the lockfile <b>filename</b>, creating it as 46 * necessary. If someone else has the lock and <b>blocking</b> is true, 47 * wait until the lock is available. Otherwise return immediately whether 48 * we succeeded or not. 49 * 50 * Set *<b>locked_out</b> to true if somebody else had the lock, and to false 51 * otherwise. 52 * 53 * Return a <b>tor_lockfile_t</b> on success, NULL on failure. 54 * 55 * (Implementation note: because we need to fall back to fcntl on some 56 * platforms, these locks are per-process, not per-thread. If you want 57 * to do in-process locking, use tor_mutex_t like a normal person. 58 * On Windows, when <b>blocking</b> is true, the maximum time that 59 * is actually waited is 10 seconds, after which NULL is returned 60 * and <b>locked_out</b> is set to 1.) 61 */ 62 tor_lockfile_t * 63 tor_lockfile_lock(const char *filename, int blocking, int *locked_out) 64 { 65 tor_lockfile_t *result; 66 int fd; 67 *locked_out = 0; 68 69 log_info(LD_FS, "Locking \"%s\"", filename); 70 fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); 71 if (fd < 0) { 72 log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename, 73 strerror(errno)); 74 return NULL; 75 } 76 77 #ifdef _WIN32 78 _lseek(fd, 0, SEEK_SET); 79 if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) { 80 if (errno != EACCES && errno != EDEADLOCK) 81 log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); 82 else 83 *locked_out = 1; 84 close(fd); 85 return NULL; 86 } 87 #elif defined(HAVE_FLOCK) 88 if (flock(fd, LOCK_EX|(blocking ? 0 : LOCK_NB)) < 0) { 89 if (errno != EWOULDBLOCK) 90 log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); 91 else 92 *locked_out = 1; 93 close(fd); 94 return NULL; 95 } 96 #else 97 { 98 struct flock lock; 99 memset(&lock, 0, sizeof(lock)); 100 lock.l_type = F_WRLCK; 101 lock.l_whence = SEEK_SET; 102 if (fcntl(fd, blocking ? F_SETLKW : F_SETLK, &lock) < 0) { 103 if (errno != EACCES && errno != EAGAIN) 104 log_warn(LD_FS, "Couldn't lock \"%s\": %s", filename, strerror(errno)); 105 else 106 *locked_out = 1; 107 close(fd); 108 return NULL; 109 } 110 } 111 #endif /* defined(_WIN32) || ... */ 112 113 result = tor_malloc(sizeof(tor_lockfile_t)); 114 result->filename = tor_strdup(filename); 115 result->fd = fd; 116 return result; 117 } 118 119 /** Release the lock held as <b>lockfile</b>. */ 120 void 121 tor_lockfile_unlock(tor_lockfile_t *lockfile) 122 { 123 tor_assert(lockfile); 124 125 log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename); 126 #ifdef _WIN32 127 _lseek(lockfile->fd, 0, SEEK_SET); 128 if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) { 129 log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename, 130 strerror(errno)); 131 } 132 #elif defined(HAVE_FLOCK) 133 if (flock(lockfile->fd, LOCK_UN) < 0) { 134 log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename, 135 strerror(errno)); 136 } 137 #else 138 /* Closing the lockfile is sufficient. */ 139 #endif /* defined(_WIN32) || ... */ 140 141 close(lockfile->fd); 142 lockfile->fd = -1; 143 tor_free(lockfile->filename); 144 tor_free(lockfile); 145 }