// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "single_instance.h" #include #include "../util/logger.h" #ifdef _WIN32 #include #else #include #include #include #include #include #endif namespace dragonx { namespace util { SingleInstance::SingleInstance(const std::string& app_name) : app_name_(app_name) { #ifndef _WIN32 // Construct lock file path const char* tmpdir = getenv("TMPDIR"); if (!tmpdir) tmpdir = "/tmp"; lock_path_ = std::string(tmpdir) + "/" + app_name_ + ".lock"; #endif } SingleInstance::~SingleInstance() { unlock(); } bool SingleInstance::tryLock() { if (locked_) { return true; // Already locked by us } #ifdef _WIN32 // Windows: Use named mutex std::string mutex_name = "Global\\" + app_name_; mutex_handle_ = CreateMutexA(NULL, TRUE, mutex_name.c_str()); if (mutex_handle_ == NULL) { DEBUG_LOGF("Failed to create mutex: %lu\n", GetLastError()); return false; } if (GetLastError() == ERROR_ALREADY_EXISTS) { // Another instance is running CloseHandle(mutex_handle_); mutex_handle_ = nullptr; DEBUG_LOGF("Another instance of %s is already running (mutex exists)\n", app_name_.c_str()); return false; } locked_ = true; DEBUG_LOGF("Single instance lock acquired (mutex)\n"); return true; #else // Linux/macOS: Use file lock (flock) lock_fd_ = open(lock_path_.c_str(), O_CREAT | O_RDWR, 0666); if (lock_fd_ < 0) { DEBUG_LOGF("Failed to open lock file %s: %s\n", lock_path_.c_str(), strerror(errno)); return false; } // Try non-blocking exclusive lock if (flock(lock_fd_, LOCK_EX | LOCK_NB) != 0) { if (errno == EWOULDBLOCK) { // Another instance has the lock DEBUG_LOGF("Another instance of %s is already running (lock file: %s)\n", app_name_.c_str(), lock_path_.c_str()); } else { DEBUG_LOGF("Failed to acquire lock: %s\n", strerror(errno)); } close(lock_fd_); lock_fd_ = -1; return false; } // Write PID to lock file (useful for debugging) char pid_str[32]; int len = snprintf(pid_str, sizeof(pid_str), "%d\n", getpid()); if (ftruncate(lock_fd_, 0) == 0) { lseek(lock_fd_, 0, SEEK_SET); ssize_t written = write(lock_fd_, pid_str, len); (void)written; // Ignore result, not critical } locked_ = true; DEBUG_LOGF("Single instance lock acquired (lock file: %s)\n", lock_path_.c_str()); return true; #endif } void SingleInstance::unlock() { if (!locked_) { return; } #ifdef _WIN32 if (mutex_handle_ != nullptr) { ReleaseMutex(mutex_handle_); CloseHandle(mutex_handle_); mutex_handle_ = nullptr; DEBUG_LOGF("Single instance lock released (mutex)\n"); } #else if (lock_fd_ >= 0) { flock(lock_fd_, LOCK_UN); close(lock_fd_); lock_fd_ = -1; // Remove the lock file unlink(lock_path_.c_str()); DEBUG_LOGF("Single instance lock released (lock file removed)\n"); } #endif locked_ = false; } } // namespace util } // namespace dragonx