Files
ObsidianDragon/src/util/single_instance.cpp
DanS 3aee55b49c ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency.
Built with Dear ImGui, SDL3, and OpenGL3/DX11.

Features:
- Send/receive shielded and transparent transactions
- Autoshield with merged transaction display
- Built-in CPU mining (xmrig)
- Peer management and network monitoring
- Wallet encryption with PIN lock
- QR code generation for receive addresses
- Transaction history with pagination
- Console for direct RPC commands
- Cross-platform (Linux, Windows)
2026-02-27 00:26:01 -06:00

137 lines
3.3 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#include "single_instance.h"
#include <cstdio>
#include "../util/logger.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/file.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>
#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