1dff0c46cSDimitry Andric //===--- LockFileManager.cpp - File-level Locking Utility------------------===//
2dff0c46cSDimitry Andric //
3dff0c46cSDimitry Andric //                     The LLVM Compiler Infrastructure
4dff0c46cSDimitry Andric //
5dff0c46cSDimitry Andric // This file is distributed under the University of Illinois Open Source
6dff0c46cSDimitry Andric // License. See LICENSE.TXT for details.
7dff0c46cSDimitry Andric //
8dff0c46cSDimitry Andric //===----------------------------------------------------------------------===//
9d88c1a5aSDimitry Andric 
10dff0c46cSDimitry Andric #include "llvm/Support/LockFileManager.h"
11d88c1a5aSDimitry Andric #include "llvm/ADT/None.h"
12d88c1a5aSDimitry Andric #include "llvm/ADT/SmallVector.h"
13f785676fSDimitry Andric #include "llvm/ADT/StringExtras.h"
1491bc56edSDimitry Andric #include "llvm/Support/Errc.h"
15d88c1a5aSDimitry Andric #include "llvm/Support/ErrorOr.h"
16dff0c46cSDimitry Andric #include "llvm/Support/FileSystem.h"
17f785676fSDimitry Andric #include "llvm/Support/MemoryBuffer.h"
183dac3a9bSDimitry Andric #include "llvm/Support/Signals.h"
19db17bf38SDimitry Andric #include "llvm/Support/raw_ostream.h"
20d88c1a5aSDimitry Andric #include <cerrno>
21d88c1a5aSDimitry Andric #include <ctime>
22d88c1a5aSDimitry Andric #include <memory>
23dff0c46cSDimitry Andric #include <sys/stat.h>
24139f7f9bSDimitry Andric #include <sys/types.h>
25db17bf38SDimitry Andric #include <system_error>
26db17bf38SDimitry Andric #include <tuple>
27*b5893f02SDimitry Andric #ifdef _WIN32
28dff0c46cSDimitry Andric #include <windows.h>
29dff0c46cSDimitry Andric #endif
30dff0c46cSDimitry Andric #if LLVM_ON_UNIX
31dff0c46cSDimitry Andric #include <unistd.h>
32dff0c46cSDimitry Andric #endif
333dac3a9bSDimitry Andric 
343dac3a9bSDimitry Andric #if defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED > 1050)
353dac3a9bSDimitry Andric #define USE_OSX_GETHOSTUUID 1
363dac3a9bSDimitry Andric #else
373dac3a9bSDimitry Andric #define USE_OSX_GETHOSTUUID 0
383dac3a9bSDimitry Andric #endif
393dac3a9bSDimitry Andric 
403dac3a9bSDimitry Andric #if USE_OSX_GETHOSTUUID
413dac3a9bSDimitry Andric #include <uuid/uuid.h>
423dac3a9bSDimitry Andric #endif
43d88c1a5aSDimitry Andric 
44dff0c46cSDimitry Andric using namespace llvm;
45dff0c46cSDimitry Andric 
464ba319b5SDimitry Andric /// Attempt to read the lock file with the given name, if it exists.
47dff0c46cSDimitry Andric ///
48dff0c46cSDimitry Andric /// \param LockFileName The name of the lock file to read.
49dff0c46cSDimitry Andric ///
50dff0c46cSDimitry Andric /// \returns The process ID of the process that owns this lock file
51dff0c46cSDimitry Andric Optional<std::pair<std::string, int> >
readLockFile(StringRef LockFileName)52dff0c46cSDimitry Andric LockFileManager::readLockFile(StringRef LockFileName) {
53dff0c46cSDimitry Andric   // Read the owning host and PID out of the lock file. If it appears that the
54dff0c46cSDimitry Andric   // owning process is dead, the lock file is invalid.
5591bc56edSDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
5691bc56edSDimitry Andric       MemoryBuffer::getFile(LockFileName);
5791bc56edSDimitry Andric   if (!MBOrErr) {
5891bc56edSDimitry Andric     sys::fs::remove(LockFileName);
59f785676fSDimitry Andric     return None;
6091bc56edSDimitry Andric   }
6139d628a0SDimitry Andric   MemoryBuffer &MB = *MBOrErr.get();
62f785676fSDimitry Andric 
63f785676fSDimitry Andric   StringRef Hostname;
64f785676fSDimitry Andric   StringRef PIDStr;
6539d628a0SDimitry Andric   std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
66f785676fSDimitry Andric   PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
67f785676fSDimitry Andric   int PID;
6891bc56edSDimitry Andric   if (!PIDStr.getAsInteger(10, PID)) {
6991bc56edSDimitry Andric     auto Owner = std::make_pair(std::string(Hostname), PID);
7091bc56edSDimitry Andric     if (processStillExecuting(Owner.first, Owner.second))
7191bc56edSDimitry Andric       return Owner;
7291bc56edSDimitry Andric   }
73dff0c46cSDimitry Andric 
74dff0c46cSDimitry Andric   // Delete the lock file. It's invalid anyway.
75f785676fSDimitry Andric   sys::fs::remove(LockFileName);
76139f7f9bSDimitry Andric   return None;
77dff0c46cSDimitry Andric }
78dff0c46cSDimitry Andric 
getHostID(SmallVectorImpl<char> & HostID)793dac3a9bSDimitry Andric static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
803dac3a9bSDimitry Andric   HostID.clear();
813dac3a9bSDimitry Andric 
823dac3a9bSDimitry Andric #if USE_OSX_GETHOSTUUID
833dac3a9bSDimitry Andric   // On OS X, use the more stable hardware UUID instead of hostname.
843dac3a9bSDimitry Andric   struct timespec wait = {1, 0}; // 1 second.
853dac3a9bSDimitry Andric   uuid_t uuid;
863dac3a9bSDimitry Andric   if (gethostuuid(uuid, &wait) != 0)
873dac3a9bSDimitry Andric     return std::error_code(errno, std::system_category());
883dac3a9bSDimitry Andric 
893dac3a9bSDimitry Andric   uuid_string_t UUIDStr;
903dac3a9bSDimitry Andric   uuid_unparse(uuid, UUIDStr);
913dac3a9bSDimitry Andric   StringRef UUIDRef(UUIDStr);
923dac3a9bSDimitry Andric   HostID.append(UUIDRef.begin(), UUIDRef.end());
933dac3a9bSDimitry Andric 
943dac3a9bSDimitry Andric #elif LLVM_ON_UNIX
953dac3a9bSDimitry Andric   char HostName[256];
963dac3a9bSDimitry Andric   HostName[255] = 0;
973dac3a9bSDimitry Andric   HostName[0] = 0;
983dac3a9bSDimitry Andric   gethostname(HostName, 255);
993dac3a9bSDimitry Andric   StringRef HostNameRef(HostName);
1003dac3a9bSDimitry Andric   HostID.append(HostNameRef.begin(), HostNameRef.end());
1013dac3a9bSDimitry Andric 
1023dac3a9bSDimitry Andric #else
1033dac3a9bSDimitry Andric   StringRef Dummy("localhost");
1043dac3a9bSDimitry Andric   HostID.append(Dummy.begin(), Dummy.end());
1053dac3a9bSDimitry Andric #endif
1063dac3a9bSDimitry Andric 
1073dac3a9bSDimitry Andric   return std::error_code();
1083dac3a9bSDimitry Andric }
1093dac3a9bSDimitry Andric 
processStillExecuting(StringRef HostID,int PID)1103dac3a9bSDimitry Andric bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
1113861d79fSDimitry Andric #if LLVM_ON_UNIX && !defined(__ANDROID__)
1123dac3a9bSDimitry Andric   SmallString<256> StoredHostID;
1133dac3a9bSDimitry Andric   if (getHostID(StoredHostID))
1143dac3a9bSDimitry Andric     return true; // Conservatively assume it's executing on error.
1153dac3a9bSDimitry Andric 
116dff0c46cSDimitry Andric   // Check whether the process is dead. If so, we're done.
1173dac3a9bSDimitry Andric   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
118dff0c46cSDimitry Andric     return false;
119dff0c46cSDimitry Andric #endif
120dff0c46cSDimitry Andric 
121dff0c46cSDimitry Andric   return true;
122dff0c46cSDimitry Andric }
123dff0c46cSDimitry Andric 
1243dac3a9bSDimitry Andric namespace {
125d88c1a5aSDimitry Andric 
1264ba319b5SDimitry Andric /// An RAII helper object ensure that the unique lock file is removed.
1274ba319b5SDimitry Andric ///
1284ba319b5SDimitry Andric /// Ensures that if there is an error or a signal before we finish acquiring the
1294ba319b5SDimitry Andric /// lock, the unique file will be removed. And if we successfully take the lock,
1304ba319b5SDimitry Andric /// the signal handler is left in place so that signals while the lock is held
1314ba319b5SDimitry Andric /// will remove the unique lock file. The caller should ensure there is a
1324ba319b5SDimitry Andric /// matching call to sys::DontRemoveFileOnSignal when the lock is released.
1334ba319b5SDimitry Andric class RemoveUniqueLockFileOnSignal {
1344ba319b5SDimitry Andric   StringRef Filename;
1354ba319b5SDimitry Andric   bool RemoveImmediately;
1363dac3a9bSDimitry Andric public:
RemoveUniqueLockFileOnSignal(StringRef Name)1374ba319b5SDimitry Andric   RemoveUniqueLockFileOnSignal(StringRef Name)
1384ba319b5SDimitry Andric   : Filename(Name), RemoveImmediately(true) {
1394ba319b5SDimitry Andric     sys::RemoveFileOnSignal(Filename, nullptr);
1403dac3a9bSDimitry Andric   }
141d88c1a5aSDimitry Andric 
~RemoveUniqueLockFileOnSignal()1424ba319b5SDimitry Andric   ~RemoveUniqueLockFileOnSignal() {
1434ba319b5SDimitry Andric     if (!RemoveImmediately) {
1444ba319b5SDimitry Andric       // Leave the signal handler enabled. It will be removed when the lock is
1454ba319b5SDimitry Andric       // released.
1464ba319b5SDimitry Andric       return;
1474ba319b5SDimitry Andric     }
1484ba319b5SDimitry Andric     sys::fs::remove(Filename);
1494ba319b5SDimitry Andric     sys::DontRemoveFileOnSignal(Filename);
1504ba319b5SDimitry Andric   }
1514ba319b5SDimitry Andric 
lockAcquired()1524ba319b5SDimitry Andric   void lockAcquired() { RemoveImmediately = false; }
1533dac3a9bSDimitry Andric };
154d88c1a5aSDimitry Andric 
1553dac3a9bSDimitry Andric } // end anonymous namespace
1563dac3a9bSDimitry Andric 
LockFileManager(StringRef FileName)157dff0c46cSDimitry Andric LockFileManager::LockFileManager(StringRef FileName)
158dff0c46cSDimitry Andric {
159139f7f9bSDimitry Andric   this->FileName = FileName;
16091bc56edSDimitry Andric   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
1613ca95b02SDimitry Andric     std::string S("failed to obtain absolute path for ");
1623ca95b02SDimitry Andric     S.append(this->FileName.str());
1633ca95b02SDimitry Andric     setError(EC, S);
16491bc56edSDimitry Andric     return;
16591bc56edSDimitry Andric   }
16691bc56edSDimitry Andric   LockFileName = this->FileName;
167dff0c46cSDimitry Andric   LockFileName += ".lock";
168dff0c46cSDimitry Andric 
169dff0c46cSDimitry Andric   // If the lock file already exists, don't bother to try to create our own
170dff0c46cSDimitry Andric   // lock file; it won't work anyway. Just figure out who owns this lock file.
171dff0c46cSDimitry Andric   if ((Owner = readLockFile(LockFileName)))
172dff0c46cSDimitry Andric     return;
173dff0c46cSDimitry Andric 
174dff0c46cSDimitry Andric   // Create a lock file that is unique to this instance.
1754ba319b5SDimitry Andric   UniqueLockFileName = LockFileName;
1764ba319b5SDimitry Andric   UniqueLockFileName += "-%%%%%%%%";
1774ba319b5SDimitry Andric   int UniqueLockFileID;
1784ba319b5SDimitry Andric   if (std::error_code EC = sys::fs::createUniqueFile(
1794ba319b5SDimitry Andric           UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
1804ba319b5SDimitry Andric     std::string S("failed to create unique file ");
1814ba319b5SDimitry Andric     S.append(UniqueLockFileName.str());
1823ca95b02SDimitry Andric     setError(EC, S);
183dff0c46cSDimitry Andric     return;
184dff0c46cSDimitry Andric   }
185dff0c46cSDimitry Andric 
186dff0c46cSDimitry Andric   // Write our process ID to our unique lock file.
187dff0c46cSDimitry Andric   {
1883dac3a9bSDimitry Andric     SmallString<256> HostID;
1893dac3a9bSDimitry Andric     if (auto EC = getHostID(HostID)) {
1903ca95b02SDimitry Andric       setError(EC, "failed to get host id");
1913dac3a9bSDimitry Andric       return;
1923dac3a9bSDimitry Andric     }
193dff0c46cSDimitry Andric 
1944ba319b5SDimitry Andric     raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true);
1953dac3a9bSDimitry Andric     Out << HostID << ' ';
196dff0c46cSDimitry Andric #if LLVM_ON_UNIX
1973dac3a9bSDimitry Andric     Out << getpid();
198dff0c46cSDimitry Andric #else
1993dac3a9bSDimitry Andric     Out << "1";
200dff0c46cSDimitry Andric #endif
2014ba319b5SDimitry Andric     Out.close();
202dff0c46cSDimitry Andric 
203dff0c46cSDimitry Andric     if (Out.has_error()) {
2042cab237bSDimitry Andric       // We failed to write out PID, so report the error, remove the
205dff0c46cSDimitry Andric       // unique lock file, and fail.
2063ca95b02SDimitry Andric       std::string S("failed to write to ");
2074ba319b5SDimitry Andric       S.append(UniqueLockFileName.str());
2082cab237bSDimitry Andric       setError(Out.error(), S);
2094ba319b5SDimitry Andric       sys::fs::remove(UniqueLockFileName);
210dff0c46cSDimitry Andric       return;
211dff0c46cSDimitry Andric     }
212dff0c46cSDimitry Andric   }
213dff0c46cSDimitry Andric 
2144ba319b5SDimitry Andric   // Clean up the unique file on signal, which also releases the lock if it is
2154ba319b5SDimitry Andric   // held since the .lock symlink will point to a nonexistent file.
2164ba319b5SDimitry Andric   RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
2174ba319b5SDimitry Andric 
218d88c1a5aSDimitry Andric   while (true) {
21991bc56edSDimitry Andric     // Create a link from the lock file name. If this succeeds, we're done.
22091bc56edSDimitry Andric     std::error_code EC =
2214ba319b5SDimitry Andric         sys::fs::create_link(UniqueLockFileName, LockFileName);
2223dac3a9bSDimitry Andric     if (!EC) {
2234ba319b5SDimitry Andric       RemoveUniqueFile.lockAcquired();
224dff0c46cSDimitry Andric       return;
2253dac3a9bSDimitry Andric     }
226dff0c46cSDimitry Andric 
22791bc56edSDimitry Andric     if (EC != errc::file_exists) {
2283ca95b02SDimitry Andric       std::string S("failed to create link ");
2293ca95b02SDimitry Andric       raw_string_ostream OSS(S);
2304ba319b5SDimitry Andric       OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
2313ca95b02SDimitry Andric       setError(EC, OSS.str());
23291bc56edSDimitry Andric       return;
23391bc56edSDimitry Andric     }
23491bc56edSDimitry Andric 
23591bc56edSDimitry Andric     // Someone else managed to create the lock file first. Read the process ID
23691bc56edSDimitry Andric     // from the lock file.
2374ba319b5SDimitry Andric     if ((Owner = readLockFile(LockFileName))) {
2384ba319b5SDimitry Andric       // Wipe out our unique lock file (it's useless now)
2394ba319b5SDimitry Andric       sys::fs::remove(UniqueLockFileName);
2404ba319b5SDimitry Andric       return;
2414ba319b5SDimitry Andric     }
24291bc56edSDimitry Andric 
243ff0cc061SDimitry Andric     if (!sys::fs::exists(LockFileName)) {
24491bc56edSDimitry Andric       // The previous owner released the lock file before we could read it.
24591bc56edSDimitry Andric       // Try to get ownership again.
24691bc56edSDimitry Andric       continue;
24791bc56edSDimitry Andric     }
24891bc56edSDimitry Andric 
24991bc56edSDimitry Andric     // There is a lock file that nobody owns; try to clean it up and get
25091bc56edSDimitry Andric     // ownership.
251ff0cc061SDimitry Andric     if ((EC = sys::fs::remove(LockFileName))) {
2523ca95b02SDimitry Andric       std::string S("failed to remove lockfile ");
2534ba319b5SDimitry Andric       S.append(UniqueLockFileName.str());
2543ca95b02SDimitry Andric       setError(EC, S);
25591bc56edSDimitry Andric       return;
25691bc56edSDimitry Andric     }
25791bc56edSDimitry Andric   }
258dff0c46cSDimitry Andric }
259dff0c46cSDimitry Andric 
getState() const260dff0c46cSDimitry Andric LockFileManager::LockFileState LockFileManager::getState() const {
261dff0c46cSDimitry Andric   if (Owner)
262dff0c46cSDimitry Andric     return LFS_Shared;
263dff0c46cSDimitry Andric 
2642cab237bSDimitry Andric   if (ErrorCode)
265dff0c46cSDimitry Andric     return LFS_Error;
266dff0c46cSDimitry Andric 
267dff0c46cSDimitry Andric   return LFS_Owned;
268dff0c46cSDimitry Andric }
269dff0c46cSDimitry Andric 
getErrorMessage() const2703ca95b02SDimitry Andric std::string LockFileManager::getErrorMessage() const {
2712cab237bSDimitry Andric   if (ErrorCode) {
2723ca95b02SDimitry Andric     std::string Str(ErrorDiagMsg);
2732cab237bSDimitry Andric     std::string ErrCodeMsg = ErrorCode.message();
2743ca95b02SDimitry Andric     raw_string_ostream OSS(Str);
2753ca95b02SDimitry Andric     if (!ErrCodeMsg.empty())
2762cab237bSDimitry Andric       OSS << ": " << ErrCodeMsg;
2772cab237bSDimitry Andric     return OSS.str();
2783ca95b02SDimitry Andric   }
2793ca95b02SDimitry Andric   return "";
2803ca95b02SDimitry Andric }
2813ca95b02SDimitry Andric 
~LockFileManager()282dff0c46cSDimitry Andric LockFileManager::~LockFileManager() {
283dff0c46cSDimitry Andric   if (getState() != LFS_Owned)
284dff0c46cSDimitry Andric     return;
285dff0c46cSDimitry Andric 
286dff0c46cSDimitry Andric   // Since we own the lock, remove the lock file and our own unique lock file.
287ff0cc061SDimitry Andric   sys::fs::remove(LockFileName);
2884ba319b5SDimitry Andric   sys::fs::remove(UniqueLockFileName);
2894ba319b5SDimitry Andric   // The unique file is now gone, so remove it from the signal handler. This
2904ba319b5SDimitry Andric   // matches a sys::RemoveFileOnSignal() in LockFileManager().
2914ba319b5SDimitry Andric   sys::DontRemoveFileOnSignal(UniqueLockFileName);
292dff0c46cSDimitry Andric }
293dff0c46cSDimitry Andric 
waitForUnlock()29491bc56edSDimitry Andric LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
295dff0c46cSDimitry Andric   if (getState() != LFS_Shared)
29691bc56edSDimitry Andric     return Res_Success;
297dff0c46cSDimitry Andric 
298*b5893f02SDimitry Andric #ifdef _WIN32
299dff0c46cSDimitry Andric   unsigned long Interval = 1;
300dff0c46cSDimitry Andric #else
301dff0c46cSDimitry Andric   struct timespec Interval;
302dff0c46cSDimitry Andric   Interval.tv_sec = 0;
303dff0c46cSDimitry Andric   Interval.tv_nsec = 1000000;
304dff0c46cSDimitry Andric #endif
3057a7e6055SDimitry Andric   // Don't wait more than 40s per iteration. Total timeout for the file
3067a7e6055SDimitry Andric   // to appear is ~1.5 minutes.
3077a7e6055SDimitry Andric   const unsigned MaxSeconds = 40;
308dff0c46cSDimitry Andric   do {
309dff0c46cSDimitry Andric     // Sleep for the designated interval, to allow the owning process time to
310dff0c46cSDimitry Andric     // finish up and remove the lock file.
311dff0c46cSDimitry Andric     // FIXME: Should we hook in to system APIs to get a notification when the
312dff0c46cSDimitry Andric     // lock file is deleted?
313*b5893f02SDimitry Andric #ifdef _WIN32
314dff0c46cSDimitry Andric     Sleep(Interval);
315dff0c46cSDimitry Andric #else
31691bc56edSDimitry Andric     nanosleep(&Interval, nullptr);
317dff0c46cSDimitry Andric #endif
318284c1978SDimitry Andric 
31939d628a0SDimitry Andric     if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
32039d628a0SDimitry Andric         errc::no_such_file_or_directory) {
321ff0cc061SDimitry Andric       // If the original file wasn't created, somone thought the lock was dead.
322ff0cc061SDimitry Andric       if (!sys::fs::exists(FileName))
323ff0cc061SDimitry Andric         return Res_OwnerDied;
32491bc56edSDimitry Andric       return Res_Success;
325139f7f9bSDimitry Andric     }
326dff0c46cSDimitry Andric 
327ff0cc061SDimitry Andric     // If the process owning the lock died without cleaning up, just bail out.
328ff0cc061SDimitry Andric     if (!processStillExecuting((*Owner).first, (*Owner).second))
32991bc56edSDimitry Andric       return Res_OwnerDied;
330dff0c46cSDimitry Andric 
331dff0c46cSDimitry Andric     // Exponentially increase the time we wait for the lock to be removed.
332*b5893f02SDimitry Andric #ifdef _WIN32
333dff0c46cSDimitry Andric     Interval *= 2;
334dff0c46cSDimitry Andric #else
335dff0c46cSDimitry Andric     Interval.tv_sec *= 2;
336dff0c46cSDimitry Andric     Interval.tv_nsec *= 2;
337dff0c46cSDimitry Andric     if (Interval.tv_nsec >= 1000000000) {
338dff0c46cSDimitry Andric       ++Interval.tv_sec;
339dff0c46cSDimitry Andric       Interval.tv_nsec -= 1000000000;
340dff0c46cSDimitry Andric     }
341dff0c46cSDimitry Andric #endif
342dff0c46cSDimitry Andric   } while (
343*b5893f02SDimitry Andric #ifdef _WIN32
344dff0c46cSDimitry Andric            Interval < MaxSeconds * 1000
345dff0c46cSDimitry Andric #else
346dff0c46cSDimitry Andric            Interval.tv_sec < (time_t)MaxSeconds
347dff0c46cSDimitry Andric #endif
348dff0c46cSDimitry Andric            );
349dff0c46cSDimitry Andric 
350dff0c46cSDimitry Andric   // Give up.
35191bc56edSDimitry Andric   return Res_Timeout;
352dff0c46cSDimitry Andric }
353ff0cc061SDimitry Andric 
unsafeRemoveLockFile()354ff0cc061SDimitry Andric std::error_code LockFileManager::unsafeRemoveLockFile() {
355ff0cc061SDimitry Andric   return sys::fs::remove(LockFileName);
356ff0cc061SDimitry Andric }
357