#include "llvm/Support/LockFileManager.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <cerrno>
#include <chrono>
#include <ctime>
#include <memory>
#include <random>
#include <sys/stat.h>
#include <sys/types.h>
#include <system_error>
#include <thread>
#include <tuple>
#ifdef _WIN32
#include <windows.h>
#endif
#if LLVM_ON_UNIX
#include <unistd.h>
#endif
#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1050)
#define USE_OSX_GETHOSTUUID 1
#else
#define USE_OSX_GETHOSTUUID 0
#endif
#if USE_OSX_GETHOSTUUID
#include <uuid/uuid.h>
#endif
using namespace llvm;
Optional<std::pair<std::string, int> >
LockFileManager::readLockFile(StringRef LockFileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
MemoryBuffer::getFile(LockFileName);
if (!MBOrErr) {
sys::fs::remove(LockFileName);
return None;
}
MemoryBuffer &MB = *MBOrErr.get();
StringRef Hostname;
StringRef PIDStr;
std::tie(Hostname, PIDStr) = getToken(MB.getBuffer(), " ");
PIDStr = PIDStr.substr(PIDStr.find_first_not_of(" "));
int PID;
if (!PIDStr.getAsInteger(10, PID)) {
auto Owner = std::make_pair(std::string(Hostname), PID);
if (processStillExecuting(Owner.first, Owner.second))
return Owner;
}
sys::fs::remove(LockFileName);
return None;
}
static std::error_code getHostID(SmallVectorImpl<char> &HostID) {
HostID.clear();
#if USE_OSX_GETHOSTUUID
struct timespec wait = {1, 0}; uuid_t uuid;
if (gethostuuid(uuid, &wait) != 0)
return std::error_code(errno, std::system_category());
uuid_string_t UUIDStr;
uuid_unparse(uuid, UUIDStr);
StringRef UUIDRef(UUIDStr);
HostID.append(UUIDRef.begin(), UUIDRef.end());
#elif LLVM_ON_UNIX
char HostName[256];
HostName[255] = 0;
HostName[0] = 0;
gethostname(HostName, 255);
StringRef HostNameRef(HostName);
HostID.append(HostNameRef.begin(), HostNameRef.end());
#else
StringRef Dummy("localhost");
HostID.append(Dummy.begin(), Dummy.end());
#endif
return std::error_code();
}
bool LockFileManager::processStillExecuting(StringRef HostID, int PID) {
#if LLVM_ON_UNIX && !defined(__ANDROID__)
SmallString<256> StoredHostID;
if (getHostID(StoredHostID))
return true;
if (StoredHostID == HostID && getsid(PID) == -1 && errno == ESRCH)
return false;
#endif
return true;
}
namespace {
class RemoveUniqueLockFileOnSignal {
StringRef Filename;
bool RemoveImmediately;
public:
RemoveUniqueLockFileOnSignal(StringRef Name)
: Filename(Name), RemoveImmediately(true) {
sys::RemoveFileOnSignal(Filename, nullptr);
}
~RemoveUniqueLockFileOnSignal() {
if (!RemoveImmediately) {
return;
}
sys::fs::remove(Filename);
sys::DontRemoveFileOnSignal(Filename);
}
void lockAcquired() { RemoveImmediately = false; }
};
}
LockFileManager::LockFileManager(StringRef FileName)
{
this->FileName = FileName;
if (std::error_code EC = sys::fs::make_absolute(this->FileName)) {
std::string S("failed to obtain absolute path for ");
S.append(std::string(this->FileName.str()));
setError(EC, S);
return;
}
LockFileName = this->FileName;
LockFileName += ".lock";
if ((Owner = readLockFile(LockFileName)))
return;
UniqueLockFileName = LockFileName;
UniqueLockFileName += "-%%%%%%%%";
int UniqueLockFileID;
if (std::error_code EC = sys::fs::createUniqueFile(
UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) {
std::string S("failed to create unique file ");
S.append(std::string(UniqueLockFileName.str()));
setError(EC, S);
return;
}
{
SmallString<256> HostID;
if (auto EC = getHostID(HostID)) {
setError(EC, "failed to get host id");
return;
}
raw_fd_ostream Out(UniqueLockFileID, true);
Out << HostID << ' ' << sys::Process::getProcessId();
Out.close();
if (Out.has_error()) {
std::string S("failed to write to ");
S.append(std::string(UniqueLockFileName.str()));
setError(Out.error(), S);
sys::fs::remove(UniqueLockFileName);
return;
}
}
RemoveUniqueLockFileOnSignal RemoveUniqueFile(UniqueLockFileName);
while (true) {
std::error_code EC =
sys::fs::create_link(UniqueLockFileName, LockFileName);
if (!EC) {
RemoveUniqueFile.lockAcquired();
return;
}
if (EC != errc::file_exists) {
std::string S("failed to create link ");
raw_string_ostream OSS(S);
OSS << LockFileName.str() << " to " << UniqueLockFileName.str();
setError(EC, OSS.str());
return;
}
if ((Owner = readLockFile(LockFileName))) {
sys::fs::remove(UniqueLockFileName);
return;
}
if (!sys::fs::exists(LockFileName)) {
continue;
}
if ((EC = sys::fs::remove(LockFileName))) {
std::string S("failed to remove lockfile ");
S.append(std::string(UniqueLockFileName.str()));
setError(EC, S);
return;
}
}
}
LockFileManager::LockFileState LockFileManager::getState() const {
if (Owner)
return LFS_Shared;
if (ErrorCode)
return LFS_Error;
return LFS_Owned;
}
std::string LockFileManager::getErrorMessage() const {
if (ErrorCode) {
std::string Str(ErrorDiagMsg);
std::string ErrCodeMsg = ErrorCode.message();
raw_string_ostream OSS(Str);
if (!ErrCodeMsg.empty())
OSS << ": " << ErrCodeMsg;
return OSS.str();
}
return "";
}
LockFileManager::~LockFileManager() {
if (getState() != LFS_Owned)
return;
sys::fs::remove(LockFileName);
sys::fs::remove(UniqueLockFileName);
sys::DontRemoveFileOnSignal(UniqueLockFileName);
}
LockFileManager::WaitForUnlockResult
LockFileManager::waitForUnlock(const unsigned MaxSeconds) {
if (getState() != LFS_Shared)
return Res_Success;
const unsigned long MinWaitDurationMS = 10;
const unsigned long MaxWaitMultiplier = 50; unsigned long WaitMultiplier = 1;
unsigned long ElapsedTimeSeconds = 0;
std::random_device Device;
std::default_random_engine Engine(Device());
auto StartTime = std::chrono::steady_clock::now();
do {
std::uniform_int_distribution<unsigned long> Distribution(1,
WaitMultiplier);
unsigned long WaitDurationMS = MinWaitDurationMS * Distribution(Engine);
std::this_thread::sleep_for(std::chrono::milliseconds(WaitDurationMS));
if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) ==
errc::no_such_file_or_directory) {
if (!sys::fs::exists(FileName))
return Res_OwnerDied;
return Res_Success;
}
if (!processStillExecuting((*Owner).first, (*Owner).second))
return Res_OwnerDied;
WaitMultiplier *= 2;
if (WaitMultiplier > MaxWaitMultiplier) {
WaitMultiplier = MaxWaitMultiplier;
}
ElapsedTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - StartTime)
.count();
} while (ElapsedTimeSeconds < MaxSeconds);
return Res_Timeout;
}
std::error_code LockFileManager::unsafeRemoveLockFile() {
return sys::fs::remove(LockFileName);
}