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