1 //===--- LockFileManager.cpp - File-level Locking Utility------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/Support/LockFileManager.h" 11 #include "llvm/ADT/None.h" 12 #include "llvm/ADT/ScopeExit.h" 13 #include "llvm/ADT/SmallVector.h" 14 #include "llvm/ADT/StringExtras.h" 15 #include "llvm/Config/llvm-config.h" 16 #include "llvm/Support/Errc.h" 17 #include "llvm/Support/ErrorOr.h" 18 #include "llvm/Support/FileSystem.h" 19 #include "llvm/Support/MemoryBuffer.h" 20 #include "llvm/Support/Signals.h" 21 #include "llvm/Support/raw_ostream.h" 22 #include <cerrno> 23 #include <ctime> 24 #include <memory> 25 #include <sys/stat.h> 26 #include <sys/types.h> 27 #include <system_error> 28 #include <tuple> 29 #if _WIN32 30 #include <windows.h> 31 #endif 32 #if LLVM_ON_UNIX 33 #include <unistd.h> 34 #endif 35 36 #if defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) 37 #define USE_OSX_GETHOSTUUID 1 38 #else 39 #define USE_OSX_GETHOSTUUID 0 40 #endif 41 42 #if USE_OSX_GETHOSTUUID 43 #include <uuid/uuid.h> 44 #endif 45 46 using namespace llvm; 47 48 /// Attempt to read the lock file with the given name, if it exists. 49 /// 50 /// \param LockFileName The name of the lock file to read. 51 /// 52 /// \returns The process ID of the process that owns this lock file 53 Optional<std::pair<std::string, int> > 54 LockFileManager::readLockFile(StringRef LockFileName) { 55 // Read the owning host and PID out of the lock file. If it appears that the 56 // owning process is dead, the lock file is invalid. 57 ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 58 MemoryBuffer::getFile(LockFileName); 59 if (!MBOrErr) { 60 sys::fs::remove(LockFileName); 61 return None; 62 } 63 MemoryBuffer &MB = *MBOrErr.get(); 64 65 StringRef Hostname; 66 StringRef PIDStr; 67 std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " "); 68 PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" ")); 69 int PID; 70 if (!PIDStr.getAsInteger(10, PID)) { 71 auto Owner = std::make_pair(std::string(Hostname), PID); 72 if (processStillExecuting(Owner.first, Owner.second)) 73 return Owner; 74 } 75 76 // Delete the lock file. It's invalid anyway. 77 sys::fs::remove(LockFileName); 78 return None; 79 } 80 81 static std::error_code getHostID(SmallVectorImpl<char> &HostID) { 82 HostID.clear(); 83 84 #if USE_OSX_GETHOSTUUID 85 // On OS X, use the more stable hardware UUID instead of hostname. 86 struct timespec wait = {1, 0}; // 1 second. 87 uuid_t uuid; 88 if (gethostuuid(uuid, &wait) != 0) 89 return std::error_code(errno, std::system_category()); 90 91 uuid_string_t UUIDStr; 92 uuid_unparse(uuid, UUIDStr); 93 StringRef UUIDRef(UUIDStr); 94 HostID.append(UUIDRef.begin(), UUIDRef.end()); 95 96 #elif LLVM_ON_UNIX 97 char HostName[256]; 98 HostName[255] = 0; 99 HostName[0] = 0; 100 gethostname(HostName, 255); 101 StringRef HostNameRef(HostName); 102 HostID.append(HostNameRef.begin(), HostNameRef.end()); 103 104 #else 105 StringRef Dummy("localhost"); 106 HostID.append(Dummy.begin(), Dummy.end()); 107 #endif 108 109 return std::error_code(); 110 } 111 112 bool LockFileManager::processStillExecuting(StringRef HostID, int PID) { 113 #if LLVM_ON_UNIX && !defined(__ANDROID__) 114 SmallString<256> StoredHostID; 115 if (getHostID(StoredHostID)) 116 return true; // Conservatively assume it's executing on error. 117 118 // Check whether the process is dead. If so, we're done. 119 if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH) 120 return false; 121 #endif 122 123 return true; 124 } 125 126 LockFileManager::LockFileManager(StringRef FileName) 127 { 128 this->FileName = FileName; 129 if (std::error_code EC = sys::fs::make_absolute(this->FileName)) { 130 std::string S("failed to obtain absolute path for "); 131 S.append(this->FileName.str()); 132 setError(EC, S); 133 return; 134 } 135 LockFileName = this->FileName; 136 LockFileName += ".lock"; 137 138 // If the lock file already exists, don't bother to try to create our own 139 // lock file; it won't work anyway. Just figure out who owns this lock file. 140 if ((Owner = readLockFile(LockFileName))) 141 return; 142 143 // Create a lock file that is unique to this instance. 144 Expected<sys::fs::TempFile> Temp = 145 sys::fs::TempFile::create(LockFileName + "-%%%%%%%%"); 146 if (!Temp) { 147 std::error_code EC = errorToErrorCode(Temp.takeError()); 148 std::string S("failed to create unique file with prefix "); 149 S.append(LockFileName.str()); 150 setError(EC, S); 151 return; 152 } 153 UniqueLockFile = std::move(*Temp); 154 155 // Make sure we discard the temporary file on exit. 156 auto RemoveTempFile = llvm::make_scope_exit([&]() { 157 if (Error E = UniqueLockFile->discard()) 158 setError(errorToErrorCode(std::move(E))); 159 }); 160 161 // Write our process ID to our unique lock file. 162 { 163 SmallString<256> HostID; 164 if (auto EC = getHostID(HostID)) { 165 setError(EC, "failed to get host id"); 166 return; 167 } 168 169 raw_fd_ostream Out(UniqueLockFile->FD, /*shouldClose=*/false); 170 Out << HostID << ' '; 171 #if LLVM_ON_UNIX 172 Out << getpid(); 173 #else 174 Out << "1"; 175 #endif 176 Out.flush(); 177 178 if (Out.has_error()) { 179 // We failed to write out PID, so report the error, remove the 180 // unique lock file, and fail. 181 std::string S("failed to write to "); 182 S.append(UniqueLockFile->TmpName); 183 setError(Out.error(), S); 184 return; 185 } 186 } 187 188 while (true) { 189 // Create a link from the lock file name. If this succeeds, we're done. 190 std::error_code EC = 191 sys::fs::create_link(UniqueLockFile->TmpName, LockFileName); 192 if (!EC) { 193 RemoveTempFile.release(); 194 return; 195 } 196 197 if (EC != errc::file_exists) { 198 std::string S("failed to create link "); 199 raw_string_ostream OSS(S); 200 OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName; 201 setError(EC, OSS.str()); 202 return; 203 } 204 205 // Someone else managed to create the lock file first. Read the process ID 206 // from the lock file. 207 if ((Owner = readLockFile(LockFileName))) 208 return; // RemoveTempFile will delete out our unique lock file. 209 210 if (!sys::fs::exists(LockFileName)) { 211 // The previous owner released the lock file before we could read it. 212 // Try to get ownership again. 213 continue; 214 } 215 216 // There is a lock file that nobody owns; try to clean it up and get 217 // ownership. 218 if ((EC = sys::fs::remove(LockFileName))) { 219 std::string S("failed to remove lockfile "); 220 S.append(LockFileName.str()); 221 setError(EC, S); 222 return; 223 } 224 } 225 } 226 227 LockFileManager::LockFileState LockFileManager::getState() const { 228 if (Owner) 229 return LFS_Shared; 230 231 if (ErrorCode) 232 return LFS_Error; 233 234 return LFS_Owned; 235 } 236 237 std::string LockFileManager::getErrorMessage() const { 238 if (ErrorCode) { 239 std::string Str(ErrorDiagMsg); 240 std::string ErrCodeMsg = ErrorCode.message(); 241 raw_string_ostream OSS(Str); 242 if (!ErrCodeMsg.empty()) 243 OSS << ": " << ErrCodeMsg; 244 return OSS.str(); 245 } 246 return ""; 247 } 248 249 LockFileManager::~LockFileManager() { 250 if (getState() != LFS_Owned) 251 return; 252 253 // Since we own the lock, remove the lock file and our own unique lock file. 254 sys::fs::remove(LockFileName); 255 consumeError(UniqueLockFile->discard()); 256 } 257 258 LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { 259 if (getState() != LFS_Shared) 260 return Res_Success; 261 262 #if _WIN32 263 unsigned long Interval = 1; 264 #else 265 struct timespec Interval; 266 Interval.tv_sec = 0; 267 Interval.tv_nsec = 1000000; 268 #endif 269 // Don't wait more than 40s per iteration. Total timeout for the file 270 // to appear is ~1.5 minutes. 271 const unsigned MaxSeconds = 40; 272 do { 273 // Sleep for the designated interval, to allow the owning process time to 274 // finish up and remove the lock file. 275 // FIXME: Should we hook in to system APIs to get a notification when the 276 // lock file is deleted? 277 #if _WIN32 278 Sleep(Interval); 279 #else 280 nanosleep(&Interval, nullptr); 281 #endif 282 283 if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) == 284 errc::no_such_file_or_directory) { 285 // If the original file wasn't created, somone thought the lock was dead. 286 if (!sys::fs::exists(FileName)) 287 return Res_OwnerDied; 288 return Res_Success; 289 } 290 291 // If the process owning the lock died without cleaning up, just bail out. 292 if (!processStillExecuting((*Owner).first, (*Owner).second)) 293 return Res_OwnerDied; 294 295 // Exponentially increase the time we wait for the lock to be removed. 296 #if _WIN32 297 Interval *= 2; 298 #else 299 Interval.tv_sec *= 2; 300 Interval.tv_nsec *= 2; 301 if (Interval.tv_nsec >= 1000000000) { 302 ++Interval.tv_sec; 303 Interval.tv_nsec -= 1000000000; 304 } 305 #endif 306 } while ( 307 #if _WIN32 308 Interval < MaxSeconds * 1000 309 #else 310 Interval.tv_sec < (time_t)MaxSeconds 311 #endif 312 ); 313 314 // Give up. 315 return Res_Timeout; 316 } 317 318 std::error_code LockFileManager::unsafeRemoveLockFile() { 319 return sys::fs::remove(LockFileName); 320 } 321