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)
137 lines
3.3 KiB
C++
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
|