signal+main: exit with code 1 on programmatic shutdown#10944
Conversation
When lnd shuts itself down due to a health check failure (e.g. the chain backend becomes unreachable), it previously exited with code 0. This caused systemd and other process managers to treat the exit as intentional, so Restart=on-failure would not trigger and the node would remain offline. Add a requestedShutdown flag to Interceptor that is set atomically when shutdown is triggered via RequestShutdown(), as opposed to an OS signal such as SIGINT or SIGTERM. After lnd.Main() returns cleanly, main() checks the flag and calls os.Exit(1) so that process managers can automatically restart the daemon. Fixes lightningnetwork#5625 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🟡 PR Severity: MEDIUM
🟡 Medium (2 files)
🟢 Low (1 file, excluded from count)
AnalysisNo files fall under the CRITICAL or HIGH package lists (lnwallet, htlcswitch, contractcourt, sweep, peer, keychain, input, channeldb, funding, lnwire, routing, invoices, discovery, graph, watchtower, feature, lnrpc, macaroons, etc.). No bump conditions apply: only 2 non-test files changed (well under 20) and 25 non-test lines changed (well under 500), and no multiple distinct critical packages are touched. The change itself is low-risk in scope (adds a flag + exit code branch for process-manager compatibility on abnormal shutdown) but does touch the process shutdown path shared by all of To override, add a |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Fixes #5625
When lnd shuts itself down due to a health check failure (e.g. the chain backend becomes unreachable after the configured number of retries), it previously exited with code
0. This caused systemd and other process managers to treat the exit as intentional — meaningRestart=on-failurewould not trigger and the node would remain offline until manually restarted.Changes:
signal/signal.go: Add arequestedShutdown uint32field toInterceptor, set atomically inmainInterruptHandlerwhen shutdown originates fromshutdownRequestChannel(i.e.RequestShutdown()) rather than from an OS signal. Expose it via a newRequestedShutdown() boolmethod.cmd/lnd/main.go: Afterlnd.Main()returns without error, checkshutdownInterceptor.RequestedShutdown(). If set, callos.Exit(1)so process managers can distinguish an abnormal shutdown from a clean user-initiated one.signal/signal_test.go: New test covering both cases — OS signal shutdown leavesRequestedShutdown()false; programmatic shutdown viaRequestShutdown()sets it true.Behaviour
RequestShutdown()Test plan
go test ./signal/...passesmake installcompiles cleanlyecho $?)🤖 Generated with Claude Code