17039e358SDouglas Gregor //===--- LockFileManager.cpp - File-level Locking Utility------------------===//
27039e358SDouglas Gregor //
37039e358SDouglas Gregor //                     The LLVM Compiler Infrastructure
47039e358SDouglas Gregor //
57039e358SDouglas Gregor // This file is distributed under the University of Illinois Open Source
67039e358SDouglas Gregor // License. See LICENSE.TXT for details.
77039e358SDouglas Gregor //
87039e358SDouglas Gregor //===----------------------------------------------------------------------===//
933d7b762SEugene Zelenko 
107039e358SDouglas Gregor #include "llvm/Support/LockFileManager.h"
1133d7b762SEugene Zelenko #include "llvm/ADT/None.h"
1233d7b762SEugene Zelenko #include "llvm/ADT/SmallVector.h"
13d78273f4SReid Kleckner #include "llvm/ADT/StringExtras.h"
142a826e40SRafael Espindola #include "llvm/Support/Errc.h"
1533d7b762SEugene Zelenko #include "llvm/Support/ErrorOr.h"
167039e358SDouglas Gregor #include "llvm/Support/FileSystem.h"
17d78273f4SReid Kleckner #include "llvm/Support/MemoryBuffer.h"
1863aa8c5dSBen Langmuir #include "llvm/Support/Signals.h"
196bda14b3SChandler Carruth #include "llvm/Support/raw_ostream.h"
2033d7b762SEugene Zelenko #include <cerrno>
2133d7b762SEugene Zelenko #include <ctime>
2233d7b762SEugene Zelenko #include <memory>
237039e358SDouglas Gregor #include <sys/stat.h>
24ed0881b2SChandler Carruth #include <sys/types.h>
256bda14b3SChandler Carruth #include <system_error>
266bda14b3SChandler Carruth #include <tuple>
277039e358SDouglas Gregor #if LLVM_ON_WIN32
287039e358SDouglas Gregor #include <windows.h>
297039e358SDouglas Gregor #endif
307039e358SDouglas Gregor #if LLVM_ON_UNIX
317039e358SDouglas Gregor #include <unistd.h>
327039e358SDouglas Gregor #endif
33450461cbSBen Langmuir 
34450461cbSBen Langmuir #if defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED > 1050)
35450461cbSBen Langmuir #define USE_OSX_GETHOSTUUID 1
36450461cbSBen Langmuir #else
37450461cbSBen Langmuir #define USE_OSX_GETHOSTUUID 0
38450461cbSBen Langmuir #endif
39450461cbSBen Langmuir 
40450461cbSBen Langmuir #if USE_OSX_GETHOSTUUID
41450461cbSBen Langmuir #include <uuid/uuid.h>
42450461cbSBen Langmuir #endif
4333d7b762SEugene Zelenko 
447039e358SDouglas Gregor using namespace llvm;
457039e358SDouglas Gregor 
467039e358SDouglas Gregor /// \brief Attempt to read the lock file with the given name, if it exists.
477039e358SDouglas Gregor ///
487039e358SDouglas Gregor /// \param LockFileName The name of the lock file to read.
497039e358SDouglas Gregor ///
507039e358SDouglas Gregor /// \returns The process ID of the process that owns this lock file
517039e358SDouglas Gregor Optional<std::pair<std::string, int> >
527039e358SDouglas Gregor LockFileManager::readLockFile(StringRef LockFileName) {
537039e358SDouglas Gregor   // Read the owning host and PID out of the lock file. If it appears that the
547039e358SDouglas Gregor   // owning process is dead, the lock file is invalid.
55adf21f2aSRafael Espindola   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
56adf21f2aSRafael Espindola       MemoryBuffer::getFile(LockFileName);
57adf21f2aSRafael Espindola   if (!MBOrErr) {
5837575693SArgyrios Kyrtzidis     sys::fs::remove(LockFileName);
597de8ea3dSReid Kleckner     return None;
6037575693SArgyrios Kyrtzidis   }
613f6481d0SRafael Espindola   MemoryBuffer &MB = *MBOrErr.get();
627de8ea3dSReid Kleckner 
63d78273f4SReid Kleckner   StringRef Hostname;
64d78273f4SReid Kleckner   StringRef PIDStr;
653f6481d0SRafael Espindola   std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
667de8ea3dSReid Kleckner   PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
67d78273f4SReid Kleckner   int PID;
6844ec0a7dSArgyrios Kyrtzidis   if (!PIDStr.getAsInteger(10, PID)) {
6944ec0a7dSArgyrios Kyrtzidis     auto Owner = std::make_pair(std::string(Hostname), PID);
7044ec0a7dSArgyrios Kyrtzidis     if (processStillExecuting(Owner.first, Owner.second))
7144ec0a7dSArgyrios Kyrtzidis       return Owner;
7244ec0a7dSArgyrios Kyrtzidis   }
737039e358SDouglas Gregor 
747039e358SDouglas Gregor   // Delete the lock file. It's invalid anyway.
75d78273f4SReid Kleckner   sys::fs::remove(LockFileName);
76ef04593dSDavid Blaikie   return None;
777039e358SDouglas Gregor }
787039e358SDouglas Gregor 
79450461cbSBen Langmuir static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
80450461cbSBen Langmuir   HostID.clear();
81450461cbSBen Langmuir 
82450461cbSBen Langmuir #if USE_OSX_GETHOSTUUID
83450461cbSBen Langmuir   // On OS X, use the more stable hardware UUID instead of hostname.
84450461cbSBen Langmuir   struct timespec wait = {1, 0}; // 1 second.
85450461cbSBen Langmuir   uuid_t uuid;
86450461cbSBen Langmuir   if (gethostuuid(uuid, &wait) != 0)
87450461cbSBen Langmuir     return std::error_code(errno, std::system_category());
88450461cbSBen Langmuir 
89450461cbSBen Langmuir   uuid_string_t UUIDStr;
90450461cbSBen Langmuir   uuid_unparse(uuid, UUIDStr);
91450461cbSBen Langmuir   StringRef UUIDRef(UUIDStr);
92450461cbSBen Langmuir   HostID.append(UUIDRef.begin(), UUIDRef.end());
93450461cbSBen Langmuir 
94450461cbSBen Langmuir #elif LLVM_ON_UNIX
95450461cbSBen Langmuir   char HostName[256];
96450461cbSBen Langmuir   HostName[255] = 0;
97450461cbSBen Langmuir   HostName[0] = 0;
98450461cbSBen Langmuir   gethostname(HostName, 255);
99450461cbSBen Langmuir   StringRef HostNameRef(HostName);
100450461cbSBen Langmuir   HostID.append(HostNameRef.begin(), HostNameRef.end());
101450461cbSBen Langmuir 
102450461cbSBen Langmuir #else
103450461cbSBen Langmuir   StringRef Dummy("localhost");
104450461cbSBen Langmuir   HostID.append(Dummy.begin(), Dummy.end());
105450461cbSBen Langmuir #endif
106450461cbSBen Langmuir 
107450461cbSBen Langmuir   return std::error_code();
108450461cbSBen Langmuir }
109450461cbSBen Langmuir 
110450461cbSBen Langmuir bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
111c439a426SEvgeniy Stepanov #if LLVM_ON_UNIX && !defined(__ANDROID__)
112450461cbSBen Langmuir   SmallString<256> StoredHostID;
113450461cbSBen Langmuir   if (getHostID(StoredHostID))
114450461cbSBen Langmuir     return true; // Conservatively assume it's executing on error.
115450461cbSBen Langmuir 
1167039e358SDouglas Gregor   // Check whether the process is dead. If so, we're done.
117450461cbSBen Langmuir   if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
1187039e358SDouglas Gregor     return false;
1197039e358SDouglas Gregor #endif
1207039e358SDouglas Gregor 
1217039e358SDouglas Gregor   return true;
1227039e358SDouglas Gregor }
1237039e358SDouglas Gregor 
12463aa8c5dSBen Langmuir namespace {
12533d7b762SEugene Zelenko 
126*51c63bb7SRafael Espindola /// An RAII helper object for cleanups.
127*51c63bb7SRafael Espindola class RAIICleanup {
128*51c63bb7SRafael Espindola   std::function<void()> Fn;
129*51c63bb7SRafael Espindola   bool Canceled = false;
130*51c63bb7SRafael Espindola 
13163aa8c5dSBen Langmuir public:
132*51c63bb7SRafael Espindola   RAIICleanup(std::function<void()> Fn) : Fn(Fn) {}
13333d7b762SEugene Zelenko 
134*51c63bb7SRafael Espindola   ~RAIICleanup() {
135*51c63bb7SRafael Espindola     if (Canceled)
13663aa8c5dSBen Langmuir       return;
137*51c63bb7SRafael Espindola     Fn();
13863aa8c5dSBen Langmuir   }
13933d7b762SEugene Zelenko 
140*51c63bb7SRafael Espindola   void cancel() { Canceled = true; }
14163aa8c5dSBen Langmuir };
14233d7b762SEugene Zelenko 
14363aa8c5dSBen Langmuir } // end anonymous namespace
14463aa8c5dSBen Langmuir 
1457039e358SDouglas Gregor LockFileManager::LockFileManager(StringRef FileName)
1467039e358SDouglas Gregor {
147056eafd4SDouglas Gregor   this->FileName = FileName;
148db4ed0bdSRafael Espindola   if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
14942b1f65fSBruno Cardoso Lopes     std::string S("failed to obtain absolute path for ");
15042b1f65fSBruno Cardoso Lopes     S.append(this->FileName.str());
15142b1f65fSBruno Cardoso Lopes     setError(EC, S);
152900e9a3dSArgyrios Kyrtzidis     return;
153900e9a3dSArgyrios Kyrtzidis   }
154900e9a3dSArgyrios Kyrtzidis   LockFileName = this->FileName;
1557039e358SDouglas Gregor   LockFileName += ".lock";
1567039e358SDouglas Gregor 
1577039e358SDouglas Gregor   // If the lock file already exists, don't bother to try to create our own
1587039e358SDouglas Gregor   // lock file; it won't work anyway. Just figure out who owns this lock file.
1597039e358SDouglas Gregor   if ((Owner = readLockFile(LockFileName)))
1607039e358SDouglas Gregor     return;
1617039e358SDouglas Gregor 
1627039e358SDouglas Gregor   // Create a lock file that is unique to this instance.
163*51c63bb7SRafael Espindola   Expected<sys::fs::TempFile> Temp =
164*51c63bb7SRafael Espindola       sys::fs::TempFile::create(LockFileName + "-%%%%%%%%");
165*51c63bb7SRafael Espindola   if (!Temp) {
166*51c63bb7SRafael Espindola     std::error_code EC = errorToErrorCode(Temp.takeError());
167*51c63bb7SRafael Espindola     std::string S("failed to create unique file with prefix ");
168*51c63bb7SRafael Espindola     S.append(LockFileName.str());
16942b1f65fSBruno Cardoso Lopes     setError(EC, S);
1707039e358SDouglas Gregor     return;
1717039e358SDouglas Gregor   }
172*51c63bb7SRafael Espindola   UniqueLockFile = std::move(*Temp);
173*51c63bb7SRafael Espindola 
174*51c63bb7SRafael Espindola   // Make sure we discard the temporary file on exit.
175*51c63bb7SRafael Espindola   RAIICleanup RemoveTempFile([&]() {
176*51c63bb7SRafael Espindola     if (Error E = UniqueLockFile->discard())
177*51c63bb7SRafael Espindola       setError(errorToErrorCode(std::move(E)));
178*51c63bb7SRafael Espindola   });
1797039e358SDouglas Gregor 
1807039e358SDouglas Gregor   // Write our process ID to our unique lock file.
1817039e358SDouglas Gregor   {
182450461cbSBen Langmuir     SmallString<256> HostID;
183450461cbSBen Langmuir     if (auto EC = getHostID(HostID)) {
18442b1f65fSBruno Cardoso Lopes       setError(EC, "failed to get host id");
185450461cbSBen Langmuir       return;
186450461cbSBen Langmuir     }
1875123eecdSBen Langmuir 
188*51c63bb7SRafael Espindola     raw_fd_ostream Out(UniqueLockFile->FD, /*shouldClose=*/false);
189450461cbSBen Langmuir     Out << HostID << ' ';
1907039e358SDouglas Gregor #if LLVM_ON_UNIX
191450461cbSBen Langmuir     Out << getpid();
1927039e358SDouglas Gregor #else
193450461cbSBen Langmuir     Out << "1";
1947039e358SDouglas Gregor #endif
195*51c63bb7SRafael Espindola     Out.flush();
1967039e358SDouglas Gregor 
1977039e358SDouglas Gregor     if (Out.has_error()) {
1989ce2d03eSBob Haarman       // We failed to write out PID, so report the error, remove the
1997039e358SDouglas Gregor       // unique lock file, and fail.
20042b1f65fSBruno Cardoso Lopes       std::string S("failed to write to ");
201*51c63bb7SRafael Espindola       S.append(UniqueLockFile->TmpName);
2029ce2d03eSBob Haarman       setError(Out.error(), S);
2037039e358SDouglas Gregor       return;
2047039e358SDouglas Gregor     }
2057039e358SDouglas Gregor   }
2067039e358SDouglas Gregor 
20733d7b762SEugene Zelenko   while (true) {
20883f858e5SRafael Espindola     // Create a link from the lock file name. If this succeeds, we're done.
209db4ed0bdSRafael Espindola     std::error_code EC =
210*51c63bb7SRafael Espindola         sys::fs::create_link(UniqueLockFile->TmpName, LockFileName);
21163aa8c5dSBen Langmuir     if (!EC) {
212*51c63bb7SRafael Espindola       RemoveTempFile.cancel();
2137039e358SDouglas Gregor       return;
21463aa8c5dSBen Langmuir     }
2157039e358SDouglas Gregor 
2162a826e40SRafael Espindola     if (EC != errc::file_exists) {
21742b1f65fSBruno Cardoso Lopes       std::string S("failed to create link ");
21842b1f65fSBruno Cardoso Lopes       raw_string_ostream OSS(S);
219*51c63bb7SRafael Espindola       OSS << LockFileName.str() << " to " << UniqueLockFile->TmpName;
22042b1f65fSBruno Cardoso Lopes       setError(EC, OSS.str());
2214147978cSArgyrios Kyrtzidis       return;
2224147978cSArgyrios Kyrtzidis     }
2234147978cSArgyrios Kyrtzidis 
2244147978cSArgyrios Kyrtzidis     // Someone else managed to create the lock file first. Read the process ID
2254147978cSArgyrios Kyrtzidis     // from the lock file.
226*51c63bb7SRafael Espindola     if ((Owner = readLockFile(LockFileName)))
227*51c63bb7SRafael Espindola       return; // RemoveTempFile will delete out our unique lock file.
2284147978cSArgyrios Kyrtzidis 
22992e1b62dSYaron Keren     if (!sys::fs::exists(LockFileName)) {
2304147978cSArgyrios Kyrtzidis       // The previous owner released the lock file before we could read it.
2314147978cSArgyrios Kyrtzidis       // Try to get ownership again.
2324147978cSArgyrios Kyrtzidis       continue;
2334147978cSArgyrios Kyrtzidis     }
2344147978cSArgyrios Kyrtzidis 
2354147978cSArgyrios Kyrtzidis     // There is a lock file that nobody owns; try to clean it up and get
2364147978cSArgyrios Kyrtzidis     // ownership.
23792e1b62dSYaron Keren     if ((EC = sys::fs::remove(LockFileName))) {
23842b1f65fSBruno Cardoso Lopes       std::string S("failed to remove lockfile ");
239*51c63bb7SRafael Espindola       S.append(LockFileName.str());
24042b1f65fSBruno Cardoso Lopes       setError(EC, S);
2414147978cSArgyrios Kyrtzidis       return;
2424147978cSArgyrios Kyrtzidis     }
2434147978cSArgyrios Kyrtzidis   }
2447039e358SDouglas Gregor }
2457039e358SDouglas Gregor 
2467039e358SDouglas Gregor LockFileManager::LockFileState LockFileManager::getState() const {
2477039e358SDouglas Gregor   if (Owner)
2487039e358SDouglas Gregor     return LFS_Shared;
2497039e358SDouglas Gregor 
2508c42d323SRafael Espindola   if (ErrorCode)
2517039e358SDouglas Gregor     return LFS_Error;
2527039e358SDouglas Gregor 
2537039e358SDouglas Gregor   return LFS_Owned;
2547039e358SDouglas Gregor }
2557039e358SDouglas Gregor 
25642b1f65fSBruno Cardoso Lopes std::string LockFileManager::getErrorMessage() const {
2578c42d323SRafael Espindola   if (ErrorCode) {
25842b1f65fSBruno Cardoso Lopes     std::string Str(ErrorDiagMsg);
2598c42d323SRafael Espindola     std::string ErrCodeMsg = ErrorCode.message();
26042b1f65fSBruno Cardoso Lopes     raw_string_ostream OSS(Str);
26142b1f65fSBruno Cardoso Lopes     if (!ErrCodeMsg.empty())
262c8434103SRafael Espindola       OSS << ": " << ErrCodeMsg;
263c8434103SRafael Espindola     return OSS.str();
26442b1f65fSBruno Cardoso Lopes   }
26542b1f65fSBruno Cardoso Lopes   return "";
26642b1f65fSBruno Cardoso Lopes }
26742b1f65fSBruno Cardoso Lopes 
2687039e358SDouglas Gregor LockFileManager::~LockFileManager() {
2697039e358SDouglas Gregor   if (getState() != LFS_Owned)
2707039e358SDouglas Gregor     return;
2717039e358SDouglas Gregor 
2727039e358SDouglas Gregor   // Since we own the lock, remove the lock file and our own unique lock file.
27392e1b62dSYaron Keren   sys::fs::remove(LockFileName);
274*51c63bb7SRafael Espindola   consumeError(UniqueLockFile->discard());
2757039e358SDouglas Gregor }
2767039e358SDouglas Gregor 
27744ec0a7dSArgyrios Kyrtzidis LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() {
2787039e358SDouglas Gregor   if (getState() != LFS_Shared)
27944ec0a7dSArgyrios Kyrtzidis     return Res_Success;
2807039e358SDouglas Gregor 
2817039e358SDouglas Gregor #if LLVM_ON_WIN32
2827039e358SDouglas Gregor   unsigned long Interval = 1;
2837039e358SDouglas Gregor #else
2847039e358SDouglas Gregor   struct timespec Interval;
2857039e358SDouglas Gregor   Interval.tv_sec = 0;
2867039e358SDouglas Gregor   Interval.tv_nsec = 1000000;
2877039e358SDouglas Gregor #endif
2888fef5556SBruno Cardoso Lopes   // Don't wait more than 40s per iteration. Total timeout for the file
2898fef5556SBruno Cardoso Lopes   // to appear is ~1.5 minutes.
2908fef5556SBruno Cardoso Lopes   const unsigned MaxSeconds = 40;
2917039e358SDouglas Gregor   do {
2927039e358SDouglas Gregor     // Sleep for the designated interval, to allow the owning process time to
2937039e358SDouglas Gregor     // finish up and remove the lock file.
2947039e358SDouglas Gregor     // FIXME: Should we hook in to system APIs to get a notification when the
2957039e358SDouglas Gregor     // lock file is deleted?
2967039e358SDouglas Gregor #if LLVM_ON_WIN32
2977039e358SDouglas Gregor     Sleep(Interval);
2987039e358SDouglas Gregor #else
299c10719f5SCraig Topper     nanosleep(&Interval, nullptr);
3007039e358SDouglas Gregor #endif
3010cb68460SDouglas Gregor 
302281f23adSRafael Espindola     if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
303281f23adSRafael Espindola         errc::no_such_file_or_directory) {
30408970917SBen Langmuir       // If the original file wasn't created, somone thought the lock was dead.
30592e1b62dSYaron Keren       if (!sys::fs::exists(FileName))
30608970917SBen Langmuir         return Res_OwnerDied;
30744ec0a7dSArgyrios Kyrtzidis       return Res_Success;
308056eafd4SDouglas Gregor     }
3097039e358SDouglas Gregor 
31008970917SBen Langmuir     // If the process owning the lock died without cleaning up, just bail out.
31108970917SBen Langmuir     if (!processStillExecuting((*Owner).first, (*Owner).second))
31244ec0a7dSArgyrios Kyrtzidis       return Res_OwnerDied;
3137039e358SDouglas Gregor 
3147039e358SDouglas Gregor     // Exponentially increase the time we wait for the lock to be removed.
3157039e358SDouglas Gregor #if LLVM_ON_WIN32
3167039e358SDouglas Gregor     Interval *= 2;
3177039e358SDouglas Gregor #else
3187039e358SDouglas Gregor     Interval.tv_sec *= 2;
3197039e358SDouglas Gregor     Interval.tv_nsec *= 2;
3207039e358SDouglas Gregor     if (Interval.tv_nsec >= 1000000000) {
3217039e358SDouglas Gregor       ++Interval.tv_sec;
3227039e358SDouglas Gregor       Interval.tv_nsec -= 1000000000;
3237039e358SDouglas Gregor     }
3247039e358SDouglas Gregor #endif
3257039e358SDouglas Gregor   } while (
3267039e358SDouglas Gregor #if LLVM_ON_WIN32
3277039e358SDouglas Gregor            Interval < MaxSeconds * 1000
3287039e358SDouglas Gregor #else
3297039e358SDouglas Gregor            Interval.tv_sec < (time_t)MaxSeconds
3307039e358SDouglas Gregor #endif
3317039e358SDouglas Gregor            );
3327039e358SDouglas Gregor 
3337039e358SDouglas Gregor   // Give up.
33444ec0a7dSArgyrios Kyrtzidis   return Res_Timeout;
3357039e358SDouglas Gregor }
336d2d52de2SBen Langmuir 
337d2d52de2SBen Langmuir std::error_code LockFileManager::unsafeRemoveLockFile() {
33892e1b62dSYaron Keren   return sys::fs::remove(LockFileName);
339d2d52de2SBen Langmuir }
340