setconfig: replace fatal() with graceful error return on persistence failure - #9356
setconfig: replace fatal() with graceful error return on persistence failure#9356zhuyh1606-oss wants to merge 1 commit into
Conversation
…failure When append_to_file fails (e.g. config file not writable), the RPC handler should return a JSON-RPC error instead of calling fatal() which SIGABRTs the entire daemon. Fixes ElementsProject#9351 Changelog-Fixed: setconfig no longer crashes lightningd when it cannot persist the config change.
Andezion
left a comment
There was a problem hiding this comment.
The core idea (return errors instead of fatal()) is right, and the append_to_file fix itself is solid. But the pr only patches part of the persistence chain - configfile_replace_var() still aborts the daemon on the exact "repeat setconfig" scenario from the issue - and it introduces a new failure mode
| "# Inserted by setconfig %sinclude %s.setconfig", | ||
| ctime(&now), path_basename(tmpctx, fname)); | ||
| append_to_file(ld, fname, lines, must_exist); | ||
| if (append_to_file(ld, fname, lines, must_exist) < 0) |
There was a problem hiding this comment.
If the first append_to_file (line 502, appends the include line to the main config) succeeds but the second (line 506, creates the .setconfig file itself) fails the main config is left with an include X.setconfig line pointing at a file that doesn't exist. On next start common/configdir.c (gather_file_configvars, must_exist=true for includes) calls err(1) - the daemon won't boot at all
| if (oldcv && oldcv->file) { | ||
| /* If it's already in config.setconfig, replace */ | ||
| if (streq(oldcv->file, ld->setconfig_file)) { | ||
| configfile_replace_var(ld, oldcv, confline); |
There was a problem hiding this comment.
This function is untouched by the PR and still calls fatal() on grab_and_check/mkstemp/write_all/fsync/rename failures. This is exactly the branch taken on a repeated setconfig for an already-set value - the issue's own repro (calling setconfig twice) can still abort the daemon
| /* 1-based counter of where new stuff appeared */ | ||
| return strcount(buffer, "\n") + 1; | ||
|
|
||
| fail: |
There was a problem hiding this comment.
See the errno analysis above - save errno before close()
|
|
||
| if (!transient) { | ||
| configvar_save_multi(cmd->ld, names, conflines, nvals); | ||
| if (!configvar_save_multi(cmd->ld, names, conflines, nvals)) |
There was a problem hiding this comment.
tests/test_misc.py::test_setconfig_access only covers the pre-existing config_not_writable() pre-check, not the new failure paths this PR actually adds. Maybe we sould create a test that triggers a failure after the pre-check passes, something like pass the writability check, then revoke permissions before the real write?
Fixes #9351
Problem
When \setconfig\ is called on a node whose config file is not writable, \�ppend_to_file\ calls \atal(), which calls \�bort()\ and kills the entire \lightningd\ process. This crashes any LN node whose operator runs a hardened systemd deployment with \ProtectSystem=full.
Fix
Replace all \atal()\ calls in the \�ppend_to_file\ persistence chain with graceful error returns:
Testing
Changelog-Fixed: setconfig no longer crashes lightningd when it cannot persist the config change.