improve diagnostics, security UX, and network tab refresh
Diagnostics & logging: - add verbose logging system (VERBOSE_LOGF) with toggle in Settings - forward app-level log messages to Console tab for in-UI visibility - add detailed connection attempt logging (attempt #, daemon state, config paths, auth failures, port owner identification) - detect HTTP 401 auth failures and show actionable error messages - identify port owner process (PID + name) on both Linux and Windows - demote noisy acrylic/shader traces from DEBUG_LOGF to VERBOSE_LOGF - persist verbose_logging preference in settings.json - link iphlpapi on Windows for GetExtendedTcpTable Security & encryption: - update local encryption state immediately after encryptwallet RPC so Settings reflects the change before daemon restarts - show notifications for encrypt success/failure and PIN skip - use dedicated RPC client for z_importwallet during decrypt flow to avoid blocking main rpc_ curl_mutex (which starved peer/tx refresh) - force full state refresh (addresses, transactions, peers) after successful wallet import Network tab: - redesign peers refresh button as glass-panel with icon + label, matching the mining button style - add spinning arc animation while peer data is loading (peer_refresh_in_progress_ atomic flag set/cleared in refreshPeerInfo) - prevent double-click spam during refresh - add refresh-button size to ui.toml Other: - use fast_rpc_ for rescan polling to avoid blocking on main rpc_ - enable DRAGONX_DEBUG in all build configs (was debug-only) - setup.sh: pull latest xmrig-hac when repo already exists
This commit is contained in:
@@ -17,12 +17,13 @@
|
||||
#include "../util/logger.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <shlobj.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@@ -224,6 +225,113 @@ std::vector<std::string> EmbeddedDaemon::getRecentLines(int maxLines) const
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Identify what process owns a given TCP port.
|
||||
// Returns a string like "PID 1234 (dragonxd.exe)" or "unknown" on failure.
|
||||
static std::string getPortOwnerInfo(int port)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD size = 0;
|
||||
// First call to get required buffer size
|
||||
GetExtendedTcpTable(nullptr, &size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
|
||||
if (size == 0) return "unknown (GetExtendedTcpTable size query failed)";
|
||||
std::vector<BYTE> buf(size);
|
||||
DWORD ret = GetExtendedTcpTable(buf.data(), &size, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
|
||||
if (ret != NO_ERROR) return "unknown (GetExtendedTcpTable failed, error " + std::to_string(ret) + ")";
|
||||
auto* table = reinterpret_cast<MIB_TCPTABLE_OWNER_PID*>(buf.data());
|
||||
DWORD ownerPid = 0;
|
||||
for (DWORD i = 0; i < table->dwNumEntries; i++) {
|
||||
auto& row = table->table[i];
|
||||
int rowPort = ntohs(static_cast<u_short>(row.dwLocalPort));
|
||||
// Match port in LISTEN state (MIB_TCP_STATE_LISTEN = 2)
|
||||
if (rowPort == port && row.dwState == MIB_TCP_STATE_LISTEN) {
|
||||
ownerPid = row.dwOwningPid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ownerPid == 0) {
|
||||
// Maybe it's in ESTABLISHED or another state from our connect probe — try any state
|
||||
for (DWORD i = 0; i < table->dwNumEntries; i++) {
|
||||
auto& row = table->table[i];
|
||||
int rowPort = ntohs(static_cast<u_short>(row.dwLocalPort));
|
||||
if (rowPort == port && row.dwOwningPid != 0) {
|
||||
ownerPid = row.dwOwningPid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ownerPid == 0) return "unknown (no PID found for port " + std::to_string(port) + ")";
|
||||
// Resolve PID to process name
|
||||
std::string procName = "<unknown>";
|
||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (snap != INVALID_HANDLE_VALUE) {
|
||||
PROCESSENTRY32 pe;
|
||||
pe.dwSize = sizeof(pe);
|
||||
if (Process32First(snap, &pe)) {
|
||||
do {
|
||||
if (pe.th32ProcessID == ownerPid) {
|
||||
procName = pe.szExeFile;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(snap, &pe));
|
||||
}
|
||||
CloseHandle(snap);
|
||||
}
|
||||
return "PID " + std::to_string(ownerPid) + " (" + procName + ")";
|
||||
#else
|
||||
// Linux: parse /proc/net/tcp to find the inode, then scan /proc/*/fd
|
||||
FILE* fp = fopen("/proc/net/tcp", "r");
|
||||
if (!fp) return "unknown (cannot read /proc/net/tcp)";
|
||||
char line[512];
|
||||
unsigned long inode = 0;
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
unsigned int localPort, state;
|
||||
unsigned long lineInode;
|
||||
if (sscanf(line, " %*d: %*X:%X %*X:%*X %X %*X:%*X %*X:%*X %*X %*u %*u %lu",
|
||||
&localPort, &state, &lineInode) == 3) {
|
||||
if (static_cast<int>(localPort) == port && state == 0x0A) { // 0x0A = LISTEN
|
||||
inode = lineInode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (inode == 0) return "unknown (no listener found for port " + std::to_string(port) + ")";
|
||||
// Scan /proc/*/fd/* for the matching inode
|
||||
for (const auto& entry : fs::directory_iterator("/proc")) {
|
||||
if (!entry.is_directory()) continue;
|
||||
std::string pidStr = entry.path().filename().string();
|
||||
if (pidStr.empty() || !std::isdigit(pidStr[0])) continue;
|
||||
std::string fdDir = "/proc/" + pidStr + "/fd";
|
||||
try {
|
||||
for (const auto& fdEntry : fs::directory_iterator(fdDir)) {
|
||||
char target[512];
|
||||
ssize_t len = readlink(fdEntry.path().c_str(), target, sizeof(target) - 1);
|
||||
if (len > 0) {
|
||||
target[len] = '\0';
|
||||
std::string t(target);
|
||||
if (t.find("socket:[" + std::to_string(inode) + "]") != std::string::npos) {
|
||||
// Found the PID, now get the process name
|
||||
std::string commPath = "/proc/" + pidStr + "/comm";
|
||||
FILE* cf = fopen(commPath.c_str(), "r");
|
||||
std::string procName = "<unknown>";
|
||||
if (cf) {
|
||||
char name[256];
|
||||
if (fgets(name, sizeof(name), cf)) {
|
||||
procName = name;
|
||||
while (!procName.empty() && procName.back() == '\n') procName.pop_back();
|
||||
}
|
||||
fclose(cf);
|
||||
}
|
||||
return "PID " + pidStr + " (" + procName + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) { /* permission denied — skip */ }
|
||||
}
|
||||
return "unknown (inode " + std::to_string(inode) + " found but no matching PID)";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check if a TCP port is already in use (something is LISTENING)
|
||||
static bool isPortInUse(int port)
|
||||
{
|
||||
@@ -279,10 +387,11 @@ bool EmbeddedDaemon::start(const std::string& binary_path)
|
||||
// Check if something is already listening on the RPC port
|
||||
int rpc_port = std::atoi(DRAGONX_DEFAULT_RPC_PORT);
|
||||
if (isPortInUse(rpc_port)) {
|
||||
DEBUG_LOGF("[INFO] Port %d is already in use — external daemon detected, will connect to it.\\n", rpc_port);
|
||||
std::string owner = getPortOwnerInfo(rpc_port);
|
||||
VERBOSE_LOGF("[INFO] Port %d is already in use by %s — external daemon detected, will connect to it.\\n", rpc_port, owner.c_str());
|
||||
external_daemon_detected_ = true;
|
||||
// Don't set Error — the wallet will connect to the running daemon.
|
||||
setState(State::Stopped, "External daemon detected on port " + std::string(DRAGONX_DEFAULT_RPC_PORT));
|
||||
setState(State::Stopped, "External daemon detected on port " + std::string(DRAGONX_DEFAULT_RPC_PORT) + " (owned by " + owner + ")");
|
||||
return false;
|
||||
}
|
||||
external_daemon_detected_ = false;
|
||||
@@ -519,7 +628,7 @@ void EmbeddedDaemon::drainOutput()
|
||||
std::lock_guard<std::mutex> lk(output_mutex_);
|
||||
appendOutput(buffer, bytes_read);
|
||||
}
|
||||
DEBUG_LOGF("[dragonxd] %s", buffer);
|
||||
VERBOSE_LOGF("[dragonxd] %s", buffer);
|
||||
debug_log_offset_ += bytes_read;
|
||||
}
|
||||
|
||||
@@ -895,7 +1004,7 @@ void EmbeddedDaemon::drainOutput()
|
||||
std::lock_guard<std::mutex> lk(output_mutex_);
|
||||
appendOutput(buffer, static_cast<size_t>(n));
|
||||
}
|
||||
DEBUG_LOGF("[dragonxd] %s", buffer);
|
||||
VERBOSE_LOGF("[dragonxd] %s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,7 +1096,7 @@ void EmbeddedDaemon::monitorProcess()
|
||||
std::lock_guard<std::mutex> lk(output_mutex_);
|
||||
appendOutput(buffer, static_cast<size_t>(bytes_read));
|
||||
}
|
||||
DEBUG_LOGF("[dragonxd] %s", buffer);
|
||||
VERBOSE_LOGF("[dragonxd] %s", buffer);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
Reference in New Issue
Block a user