fix: large-wallet sync starvation + shutdown/console-flash UX (Windows full node) #2
@@ -5043,21 +5043,12 @@ void App::rebuildWalletDatabase()
|
|||||||
const std::string tmpOut = datadir + "/wallet.rebuilt-" + std::string(ts) + ".tmp";
|
const std::string tmpOut = datadir + "/wallet.rebuilt-" + std::string(ts) + ".tmp";
|
||||||
{ std::error_code ec; fs::remove(tmpOut, ec); } // helper uses DB_EXCL — path must be fresh
|
{ std::error_code ec; fs::remove(tmpOut, ec); } // helper uses DB_EXCL — path must be fresh
|
||||||
|
|
||||||
// 3. Run the helper (src -> tmpOut). Quote both paths; capture its JSON line.
|
// 3. Run the helper (src -> tmpOut). Quote both paths; capture its JSON line. Windowless
|
||||||
std::string cmd = "\"" + helper + "\" \"" + src + "\" \"" + tmpOut + "\"";
|
// (runHiddenCapture) so a wallet rebuild never flashes a cmd.exe console; it runs the
|
||||||
#ifdef _WIN32
|
// helper via CreateProcess directly on Windows, so no cmd.exe outer-quote wrap is needed.
|
||||||
cmd = "\"" + cmd + "\""; // cmd.exe strips the outermost quotes
|
const std::string cmd = "\"" + helper + "\" \"" + src + "\" \"" + tmpOut + "\"";
|
||||||
FILE* fp = _popen(cmd.c_str(), "r");
|
int rc = -1;
|
||||||
#else
|
const std::string jout = util::Platform::runHiddenCapture(cmd, /*mergeStderr=*/false, &rc);
|
||||||
FILE* fp = popen(cmd.c_str(), "r");
|
|
||||||
#endif
|
|
||||||
std::string jout;
|
|
||||||
if (fp) { char b[512]; while (std::fgets(b, sizeof b, fp)) jout += b; }
|
|
||||||
#ifdef _WIN32
|
|
||||||
const int rc = fp ? _pclose(fp) : -1;
|
|
||||||
#else
|
|
||||||
const int rc = fp ? pclose(fp) : -1;
|
|
||||||
#endif
|
|
||||||
DEBUG_LOGF("[App] wallet-rebuild helper rc=%d out=%s\n", rc, jout.c_str());
|
DEBUG_LOGF("[App] wallet-rebuild helper rc=%d out=%s\n", rc, jout.c_str());
|
||||||
|
|
||||||
// 4. Verify-before-swap: the output must be a readable BDB with the fund-critical keys.
|
// 4. Verify-before-swap: the output must be a readable BDB with the fund-critical keys.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#include "../util/logger.h"
|
#include "../util/logger.h"
|
||||||
|
#include "../util/platform.h"
|
||||||
#include "../util/pool_registry.h"
|
#include "../util/pool_registry.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -145,32 +146,18 @@ std::string XmrigManager::findXmrigBinary() {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: system PATH
|
// Fallback: system PATH — windowless so it never flashes a console.
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FILE* f = _popen("where xmrig.exe 2>nul", "r");
|
std::string out = util::Platform::runHiddenCapture("where xmrig.exe");
|
||||||
#else
|
#else
|
||||||
FILE* f = popen("which xmrig 2>/dev/null", "r");
|
std::string out = util::Platform::runHiddenCapture("which xmrig");
|
||||||
#endif
|
|
||||||
if (f) {
|
|
||||||
char line[512];
|
|
||||||
if (fgets(line, sizeof(line), f)) {
|
|
||||||
std::string s(line);
|
|
||||||
while (!s.empty() && (s.back() == '\n' || s.back() == '\r'))
|
|
||||||
s.pop_back();
|
|
||||||
if (!s.empty() && fs::exists(s)) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
_pclose(f);
|
|
||||||
#else
|
|
||||||
pclose(f);
|
|
||||||
#endif
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
_pclose(f);
|
|
||||||
#else
|
|
||||||
pclose(f);
|
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
std::string s = out;
|
||||||
|
const auto nl = s.find_first_of("\r\n"); // first line only
|
||||||
|
if (nl != std::string::npos) s.erase(nl);
|
||||||
|
while (!s.empty() && (s.back() == ' ' || s.back() == '\t')) s.pop_back();
|
||||||
|
if (!s.empty() && fs::exists(s)) return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -927,24 +914,10 @@ void XmrigManager::startVersionDetection()
|
|||||||
const bool binShellSafe =
|
const bool binShellSafe =
|
||||||
!bin.empty() && bin.find_first_of("\"'`$;&|<>^%\n\r") == std::string::npos;
|
!bin.empty() && bin.find_first_of("\"'`$;&|<>^%\n\r") == std::string::npos;
|
||||||
if (binShellSafe) {
|
if (binShellSafe) {
|
||||||
const std::string cmd = "\"" + bin + "\" --version 2>&1";
|
// Windowless capture (mergeStderr: xmrig may print --version to stderr) — never flashes.
|
||||||
#ifdef _WIN32
|
const std::string cmd = "\"" + bin + "\" --version";
|
||||||
FILE* fp = _popen(cmd.c_str(), "r");
|
const std::string out = util::Platform::runHiddenCapture(cmd, /*mergeStderr=*/true);
|
||||||
#else
|
if (!out.empty()) ver = parseMinerVersion(out);
|
||||||
FILE* fp = popen(cmd.c_str(), "r");
|
|
||||||
#endif
|
|
||||||
if (fp) {
|
|
||||||
std::string out;
|
|
||||||
char buf[256];
|
|
||||||
size_t n;
|
|
||||||
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) out.append(buf, n);
|
|
||||||
#ifdef _WIN32
|
|
||||||
_pclose(fp);
|
|
||||||
#else
|
|
||||||
pclose(fp);
|
|
||||||
#endif
|
|
||||||
ver = parseMinerVersion(out);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
std::lock_guard<std::mutex> lk(g_installed_ver_mutex);
|
std::lock_guard<std::mutex> lk(g_installed_ver_mutex);
|
||||||
g_installed_ver = ver;
|
g_installed_ver = ver;
|
||||||
|
|||||||
@@ -867,6 +867,70 @@ int Platform::getSystemIdleSeconds()
|
|||||||
// GPU utilization detection
|
// GPU utilization detection
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
std::string Platform::runHiddenCapture(const std::string& cmdLine, bool mergeStderr, int* exitCode)
|
||||||
|
{
|
||||||
|
if (exitCode) *exitCode = -1;
|
||||||
|
#ifdef _WIN32
|
||||||
|
SECURITY_ATTRIBUTES sa;
|
||||||
|
ZeroMemory(&sa, sizeof(sa));
|
||||||
|
sa.nLength = sizeof(sa);
|
||||||
|
sa.bInheritHandle = TRUE;
|
||||||
|
HANDLE hRead = NULL, hWrite = NULL;
|
||||||
|
if (!CreatePipe(&hRead, &hWrite, &sa, 0)) return {};
|
||||||
|
SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0); // parent's read end stays private
|
||||||
|
|
||||||
|
HANDLE hNul = INVALID_HANDLE_VALUE;
|
||||||
|
if (!mergeStderr) {
|
||||||
|
hNul = CreateFileA("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
STARTUPINFOA si;
|
||||||
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
|
si.wShowWindow = SW_HIDE;
|
||||||
|
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
si.hStdOutput = hWrite;
|
||||||
|
si.hStdError = mergeStderr ? hWrite : hNul;
|
||||||
|
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
ZeroMemory(&pi, sizeof(pi));
|
||||||
|
std::string cl = cmdLine; // CreateProcessA may modify lpCommandLine → needs a mutable buffer
|
||||||
|
std::string out;
|
||||||
|
if (CreateProcessA(NULL, cl.empty() ? NULL : &cl[0], NULL, NULL, TRUE,
|
||||||
|
CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
|
||||||
|
CloseHandle(hWrite); hWrite = NULL; // close our copy so ReadFile hits EOF when the child exits
|
||||||
|
if (hNul != INVALID_HANDLE_VALUE) { CloseHandle(hNul); hNul = INVALID_HANDLE_VALUE; }
|
||||||
|
char buf[4096];
|
||||||
|
DWORD n = 0;
|
||||||
|
while (ReadFile(hRead, buf, sizeof(buf), &n, NULL) && n > 0) out.append(buf, n);
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
if (exitCode) {
|
||||||
|
DWORD code = 0;
|
||||||
|
if (GetExitCodeProcess(pi.hProcess, &code)) *exitCode = static_cast<int>(code);
|
||||||
|
}
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
}
|
||||||
|
if (hWrite != NULL) CloseHandle(hWrite);
|
||||||
|
if (hNul != INVALID_HANDLE_VALUE) CloseHandle(hNul);
|
||||||
|
CloseHandle(hRead);
|
||||||
|
return out;
|
||||||
|
#else
|
||||||
|
const std::string full = cmdLine + (mergeStderr ? " 2>&1" : " 2>/dev/null");
|
||||||
|
std::string out;
|
||||||
|
FILE* f = popen(full.c_str(), "r");
|
||||||
|
if (!f) return out;
|
||||||
|
char buf[512];
|
||||||
|
size_t n;
|
||||||
|
while ((n = fread(buf, 1, sizeof(buf), f)) > 0) out.append(buf, n);
|
||||||
|
const int st = pclose(f);
|
||||||
|
if (exitCode) *exitCode = st; // raw status (matches prior pclose-based rc checks)
|
||||||
|
return out;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int Platform::getGpuUtilization()
|
int Platform::getGpuUtilization()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -877,23 +941,16 @@ int Platform::getGpuUtilization()
|
|||||||
static bool s_has_nvidia = false;
|
static bool s_has_nvidia = false;
|
||||||
if (!s_tried_nvidia) {
|
if (!s_tried_nvidia) {
|
||||||
s_tried_nvidia = true;
|
s_tried_nvidia = true;
|
||||||
FILE* f = _popen("where nvidia-smi 2>nul", "r");
|
// Windowless (runHiddenCapture) so GPU-aware idle detection never flashes a cmd.exe console.
|
||||||
if (f) {
|
const std::string w = runHiddenCapture("where nvidia-smi");
|
||||||
char buf[256];
|
s_has_nvidia = (w.find_first_not_of(" \t\r\n") != std::string::npos);
|
||||||
s_has_nvidia = (fgets(buf, sizeof(buf), f) != nullptr);
|
|
||||||
_pclose(f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (s_has_nvidia) {
|
if (s_has_nvidia) {
|
||||||
FILE* f = _popen("nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>nul", "r");
|
const std::string o = runHiddenCapture(
|
||||||
if (f) {
|
"nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits");
|
||||||
char buf[64];
|
if (!o.empty()) {
|
||||||
int util = -1;
|
int util = atoi(o.c_str());
|
||||||
if (fgets(buf, sizeof(buf), f)) {
|
if (util < 0 || util > 100) util = -1;
|
||||||
util = atoi(buf);
|
|
||||||
if (util < 0 || util > 100) util = -1;
|
|
||||||
}
|
|
||||||
_pclose(f);
|
|
||||||
return util;
|
return util;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,15 @@ public:
|
|||||||
* @return GPU busy percent, or -1 if unavailable.
|
* @return GPU busy percent, or -1 if unavailable.
|
||||||
*/
|
*/
|
||||||
static int getGpuUtilization();
|
static int getGpuUtilization();
|
||||||
|
|
||||||
|
// Run a command line and capture its stdout WITHOUT ever popping a console window: Windows uses
|
||||||
|
// CreateProcess + CREATE_NO_WINDOW (a plain popen()/_popen() flashes a cmd.exe console), POSIX uses
|
||||||
|
// popen(). Use this instead of _popen for anything run while the GUI is up. `mergeStderr` folds the
|
||||||
|
// child's stderr into the result (like "2>&1"); otherwise stderr is discarded. `exitCode`, if given,
|
||||||
|
// receives the child's exit status (raw pclose() status on POSIX, GetExitCodeProcess on Windows; -1
|
||||||
|
// if the process could not be launched).
|
||||||
|
static std::string runHiddenCapture(const std::string& cmdLine, bool mergeStderr = false,
|
||||||
|
int* exitCode = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user