fix(daemon): never kill a dragonxd the wallet didn't launch
On Windows, stop() with no live process of its own looked up the first "dragonxd.exe" by name and TerminateProcess'd it after the grace period: an old node still shutting down (how an in-progress shutdown was killed), a node on another datadir, or the user's main node. It was reached on a lock-gated close, for an external node, after the tracked process had exited, and from ~EmbeddedDaemon's stop(3000). - Windows stop(): drop the by-name branch; with no tracked process it logs and returns. findProcessByName is detection only. - ~EmbeddedDaemon no longer calls stop(); it stops watching and releases its handles. stop() returns whether the process exited on its own. - SeedWalletCreator never sent its isolated node an RPC stop, so on Windows that node was always force-killed holding the wallet.dat it had just created, and the stack object's destructor could then kill the main node by name. It now stops the node over the node's own RPC on every path. A node that still had to be force-killed fails the creation (seed wiped; nothing has been swept into it yet), so an uncleanly closed wallet is never adopted. - Reinstall waited only for the RPC port, which closes long before the process exits (locked exe on Windows, held datadir lock everywhere). It now waits for the process to exit and otherwise aborts without touching the binaries or starting a second node. - Linux isDaemonProcessRunning also matches /proc/<pid>/exe, so a node whose main thread is already renamed "hush-shutoff" (still holding the datadir lock) counts as running. Tests: a Windows real-process test (a fake dragonxd.exe on an isolated datadir survives stop() and the destructor), a POSIX test that the destructor leaves the process running, and the pure identity rule. scripts/test-windows.sh passes the stub path to the Windows suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+16
-5
@@ -5391,8 +5391,9 @@ void App::stopEmbeddedDaemon()
|
||||
|
||||
// Do we hold a live process handle for this node? Capture it BEFORE stop() reaps the pid. If owned,
|
||||
// daemon_controller_->stop() below BLOCKS until the process actually exits. If NOT owned (external /
|
||||
// adopted / direct-connected), stop() returns at once — we can only ask it over RPC and then watch
|
||||
// for it to disappear (handled after the stop() call).
|
||||
// adopted / direct-connected / a lock-gated start that never spawned), stop() returns at once on every
|
||||
// platform — it only ever waits on (or kills) a process this app launched, never one found by name —
|
||||
// so we can only ask it over RPC and then watch for it to disappear (handled after the stop() call).
|
||||
const bool owned = daemon_controller_->isRunning();
|
||||
|
||||
// Any exit from here on is the stop we are asking for, not a crash — even one that lands before
|
||||
@@ -5677,11 +5678,21 @@ void App::reinstallBundledDaemon()
|
||||
sendStopCommandSafely(*rpc_, "reinstall-daemon");
|
||||
}
|
||||
runtime.stopDaemonWithPolicy();
|
||||
// Wait (bounded, ~12s) for the process to exit and release the binary before overwriting.
|
||||
for (int i = 0; i < 120 && daemon::EmbeddedDaemon::isRpcPortInUse()
|
||||
&& !ctx.cancelled() && !ctx.shuttingDown(); ++i) {
|
||||
// Wait (bounded, ~120s) for the old node's PROCESS to exit, not just its RPC port: dragonxd unbinds
|
||||
// RPC first and can take a minute or more to flush and close its wallet. Until it exits its exe is
|
||||
// locked on Windows (the overwrite would fail) and it holds the datadir lock (a restart would fail).
|
||||
auto stillUp = [] {
|
||||
return daemon::EmbeddedDaemon::isRpcPortInUse() || daemon::EmbeddedDaemon::isDaemonProcessRunning();
|
||||
};
|
||||
for (int i = 0; i < 1200 && stillUp() && !ctx.cancelled() && !ctx.shuttingDown(); ++i) {
|
||||
ctx.sleepForMs(100);
|
||||
}
|
||||
if (ctx.cancelled() || ctx.shuttingDown()) return;
|
||||
if (stillUp()) {
|
||||
DEBUG_LOGF("[App] Reinstall bundled daemon: aborted — a dragonxd process is still running; "
|
||||
"binaries left untouched and no new node started\n");
|
||||
return;
|
||||
}
|
||||
// Overwrite the installed dragonx binaries (dragonxd/cli/tx) with the bundled ones.
|
||||
const bool ok = resources::reextractBundledDaemon();
|
||||
DEBUG_LOGF("[App] Reinstall bundled daemon: %s\n", ok ? "ok" : "FAILED");
|
||||
|
||||
Reference in New Issue
Block a user