Skip to content

Commit 989cf17

Browse files
committed
setconfig: put vars in separate "config.setconfig" file.
This is neater than appending to some random file: we only do that once if there's no "include" line to include a ".setconfig" file. Signed-off-by: Rusty Russell <[email protected]>
1 parent f4872f9 commit 989cf17

File tree

5 files changed

+112
-118
lines changed

5 files changed

+112
-118
lines changed

contrib/msggen/msggen/schema.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32637,9 +32637,9 @@
3263732637
"description": [
3263832638
"The **setconfig** RPC command allows you set the (dynamic) configuration option named by `config`: options which take a value (as separate from simple flag options) also need a `val` parameter.",
3263932639
"",
32640-
"This new value will *also* be written at the end of the config file, for persistence across restarts (and any old value commented out).",
32640+
"This new value will *also* be written at the end of the `config.setconfig` file (but see lightningd-config), for persistence across restarts (and any old value commented out if they were set in other config files).",
3264132641
"",
32642-
"You can see what options are dynamically adjustable using lightning- listconfigs(7). Note that you can also adjust existing options for stopped plugins; they will have an effect when the plugin is restarted."
32642+
"You can see what options are dynamically adjustable using lightning-listconfigs(7). Note that you can also adjust existing options for stopped plugins; they will have an effect when the plugin is restarted."
3264332643
],
3264432644
"request": {
3264532645
"required": [

doc/lightningd-config.5.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ DESCRIPTION
1111

1212
When lightningd(8) starts up it usually reads a general configuration
1313
file (default: **$HOME/.lightning/config**) then a network-specific
14-
configuration file (default: **$HOME/.lightning/testnet/config**). This can
14+
configuration file (default: **$HOME/.lightning/bitcoin/config**). This can
1515
be changed: see *--conf* and *--lightning-dir*.
1616

17-
Note that some configuration options, marked *dynamic*m can be changed at runtime: see lightning-setconfig(7).
17+
Note that some configuration options, marked *dynamic* can be changed at runtime: see lightning-setconfig(7): by default this will write to a file called **config.setconfig** in the network-specific lightning directory, unless there is another config file with a name ending in ".setconfig".
1818

1919
General configuration files are processed first, then network-specific
2020
ones, then command line options: later options override earlier ones

doc/schemas/lightning-setconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
"description": [
88
"The **setconfig** RPC command allows you set the (dynamic) configuration option named by `config`: options which take a value (as separate from simple flag options) also need a `val` parameter.",
99
"",
10-
"This new value will *also* be written at the end of the config file, for persistence across restarts (and any old value commented out).",
10+
"This new value will *also* be written at the end of the `config.setconfig` file (but see lightningd-config), for persistence across restarts (and any old value commented out if they were set in other config files).",
1111
"",
12-
"You can see what options are dynamically adjustable using lightning- listconfigs(7). Note that you can also adjust existing options for stopped plugins; they will have an effect when the plugin is restarted."
12+
"You can see what options are dynamically adjustable using lightning-listconfigs(7). Note that you can also adjust existing options for stopped plugins; they will have an effect when the plugin is restarted."
1313
],
1414
"request": {
1515
"required": [

lightningd/configs.c

Lines changed: 85 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -355,17 +355,6 @@ static struct command_result *param_opt_dynamic_config(struct command *cmd,
355355
return NULL;
356356
}
357357

358-
/* FIXME: put in ccan/mem! */
359-
static size_t memcount(const void *mem, size_t len, char c)
360-
{
361-
size_t count = 0;
362-
for (size_t i = 0; i < len; i++) {
363-
if (((char *)mem)[i] == c)
364-
count++;
365-
}
366-
return count;
367-
}
368-
369358
static void configvar_updated(struct lightningd *ld,
370359
enum configvar_src src,
371360
const char *fname,
@@ -385,20 +374,13 @@ static void configvar_updated(struct lightningd *ld,
385374
configvar_finalize_overrides(ld->configvars);
386375
}
387376

388-
/* Marker for our own insertions */
389-
#define INSERTED_BY_SETCONFIG "# Inserted by setconfig "
390-
391-
static void configvar_append_file(struct lightningd *ld,
392-
const char *fname,
393-
enum configvar_src src,
394-
const char *confline,
395-
bool must_exist)
377+
static size_t append_to_file(struct lightningd *ld,
378+
const char *fname,
379+
const char *str,
380+
bool must_exist)
396381
{
397382
int fd;
398-
size_t num_lines;
399-
const char *buffer, *insert;
400-
bool needs_term;
401-
time_t now = time(NULL);
383+
const char *buffer;
402384

403385
fd = open(fname, O_RDWR|O_APPEND);
404386
if (fd < 0) {
@@ -416,42 +398,39 @@ static void configvar_append_file(struct lightningd *ld,
416398
if (!buffer)
417399
fatal("Error reading %s: %s", fname, strerror(errno));
418400

419-
num_lines = memcount(buffer, tal_bytelen(buffer)-1, '\n');
420-
421401
/* If there's a last character and it's not \n, add one */
422-
if (tal_bytelen(buffer) == 1)
423-
needs_term = false;
424-
else
425-
needs_term = (buffer[tal_bytelen(buffer)-2] != '\n');
402+
if (tal_bytelen(buffer) > 1
403+
&& buffer[tal_bytelen(buffer)-2] != '\n')
404+
str = tal_strcat(tmpctx, "\n", str);
426405

427-
/* Note: ctime() contains a \n! */
428-
insert = tal_fmt(tmpctx, "%s"INSERTED_BY_SETCONFIG"%s%s\n",
429-
needs_term ? "\n": "",
430-
ctime(&now), confline);
431-
if (write(fd, insert, strlen(insert)) != strlen(insert))
406+
/* Always append a \n ourselves */
407+
str = tal_strcat(tmpctx, str, "\n");
408+
if (write(fd, str, strlen(str)) != strlen(str))
432409
fatal("Could not write to config file %s: %s",
433410
fname, strerror(errno));
411+
if (fsync(fd) != 0)
412+
fatal("Syncing %s: %s", fname, strerror(errno));
413+
close(fd);
434414

435-
configvar_updated(ld, src, fname, num_lines+2, confline);
415+
/* 1-based counter of where new stuff appeared */
416+
return strcount(buffer, "\n") + 1;
436417
}
437418

438-
/* Returns true if it rewrote in place, otherwise it just comments out
439-
* if necessary */
440-
static bool configfile_replace_var(struct lightningd *ld,
419+
/* This comments out the config file entry or maybe replace one */
420+
static void configfile_replace_var(struct lightningd *ld,
441421
const struct configvar *cv,
442-
const char *confline)
422+
const char *replace)
443423
{
444424
char *contents, **lines, *template;
445425
int outfd;
446-
bool replaced;
447426

448427
switch (cv->src) {
449428
case CONFIGVAR_CMDLINE:
450429
case CONFIGVAR_CMDLINE_SHORT:
451430
case CONFIGVAR_PLUGIN_START:
452431
case CONFIGVAR_SETCONFIG_TRANSIENT:
453432
/* These can't be commented out */
454-
return false;
433+
abort();
455434
case CONFIGVAR_EXPLICIT_CONF:
456435
case CONFIGVAR_BASE_CONF:
457436
case CONFIGVAR_NETWORK_CONF:
@@ -474,28 +453,15 @@ static bool configfile_replace_var(struct lightningd *ld,
474453
cv->configline,
475454
lines[cv->linenum - 1]);
476455

477-
/* If we already have # Inserted by setconfig above, just replace
478-
* those two! */
479-
if (cv->linenum > 1
480-
&& strstarts(lines[cv->linenum - 2], INSERTED_BY_SETCONFIG)) {
481-
time_t now = time(NULL);
482-
lines[cv->linenum - 2] = tal_fmt(lines,
483-
INSERTED_BY_SETCONFIG"%s",
484-
ctime(&now));
485-
/* But trim final \n! (thanks ctime!) */
486-
assert(strends(lines[cv->linenum - 2], "\n"));
487-
lines[cv->linenum - 2][strlen(lines[cv->linenum - 2])-1] = '\0';
488-
lines[cv->linenum - 1] = cast_const(char *, confline);
489-
replaced = true;
490-
} else {
491-
/* Comment out, in-place */
492-
lines[cv->linenum - 1]
493-
= tal_fmt(lines, "# setconfig commented out: %s",
494-
lines[cv->linenum - 1]);
495-
log_info(ld->log, "setconfig: commented out line %u of %s (%s)",
496-
cv->linenum, cv->file, cv->configline);
497-
replaced = false;
498-
}
456+
if (replace)
457+
lines[cv->linenum - 1] = cast_const(char *, replace);
458+
else
459+
lines[cv->linenum - 1] = tal_fmt(lines, "# setconfig commented out (see config.setconfig): %s",
460+
lines[cv->linenum - 1]);
461+
462+
log_info(ld->log, "setconfig: %s line %u of %s (%s)",
463+
replace ? "replaced" : "commented out",
464+
cv->linenum, cv->file, cv->configline);
499465

500466
template = tal_fmt(tmpctx, "%s.setconfig.XXXXXX", cv->file);
501467
outfd = mkstemp(template);
@@ -512,12 +478,45 @@ static bool configfile_replace_var(struct lightningd *ld,
512478
fatal("Renaming %s over %s: %s",
513479
template, cv->file, strerror(errno));
514480
close(outfd);
481+
}
515482

516-
if (replaced) {
517-
configvar_updated(ld, cv->src, cv->file, cv->linenum, confline);
518-
return true;
483+
static const char *base_conf_file(const tal_t *ctx,
484+
struct lightningd *ld,
485+
bool *must_exist)
486+
{
487+
/* Explicit --conf? Edit that, otherwise network-specific config. */
488+
if (ld->config_filename) {
489+
if (must_exist)
490+
*must_exist = true;
491+
return ld->config_filename;
492+
} else {
493+
if (must_exist)
494+
*must_exist = false;
495+
return path_join(ctx, ld->config_netdir, "config");
519496
}
520-
return false;
497+
}
498+
499+
static void create_setconfig_include(struct lightningd *ld)
500+
{
501+
const char *lines;
502+
time_t now = time(NULL);
503+
const char *fname;
504+
bool must_exist;
505+
506+
/* Usually config.setconfig, but could be different with --conf */
507+
fname = base_conf_file(tmpctx, ld, &must_exist);
508+
ld->setconfig_file = tal_fmt(ld, "%s.setconfig", fname);
509+
510+
/* We want to use a relative path here (ctime() includes \n!). */
511+
lines = tal_fmt(tmpctx,
512+
"# Inserted by setconfig %sinclude %s.setconfig",
513+
ctime(&now), path_basename(tmpctx, fname));
514+
append_to_file(ld, fname, lines, must_exist);
515+
516+
/* This creates the file */
517+
append_to_file(ld, ld->setconfig_file,
518+
"# Created and update by setconfig, but you can edit this manually when node is stopped.",
519+
false);
521520
}
522521

523522
static void configvar_save(struct lightningd *ld,
@@ -526,30 +525,29 @@ static void configvar_save(struct lightningd *ld,
526525
{
527526
/* Simple case: set in a config file. */
528527
struct configvar *oldcv;
528+
size_t linenum;
529+
530+
/* If we don't already have "include config.setconfig" add it */
531+
if (!ld->setconfig_file)
532+
create_setconfig_include(ld);
529533

534+
/* Is it already set in the config? */
530535
oldcv = configvar_first(ld->configvars, names);
531-
if (oldcv) {
532-
/* At least comment out, maybe replace */
533-
if (configfile_replace_var(ld, oldcv, confline))
534-
return;
536+
if (oldcv && oldcv->file) {
537+
/* If it's already in config.setconfig, replace */
538+
if (streq(oldcv->file, ld->setconfig_file)) {
539+
configfile_replace_var(ld, oldcv, confline);
540+
linenum = oldcv->linenum;
541+
goto replaced;
542+
}
543+
configfile_replace_var(ld, oldcv, NULL);
535544
}
536545

537-
/* If they used --conf then append to that */
538-
if (ld->config_filename)
539-
configvar_append_file(ld,
540-
ld->config_filename,
541-
CONFIGVAR_EXPLICIT_CONF,
542-
confline, true);
543-
else {
544-
const char *fname;
545-
546-
fname = path_join(tmpctx, ld->config_netdir, "config");
547-
configvar_append_file(ld,
548-
fname,
549-
CONFIGVAR_NETWORK_CONF,
550-
confline,
551-
false);
552-
}
546+
linenum = append_to_file(ld, ld->setconfig_file, confline, true);
547+
548+
replaced:
549+
configvar_updated(ld, CONFIGVAR_NETWORK_CONF,
550+
ld->setconfig_file, linenum, confline);
553551
}
554552

555553
static struct command_result *setconfig_success(struct command *cmd,

tests/test_misc.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4058,6 +4058,7 @@ def test_config_whitespace(node_factory):
40584058
def test_setconfig(node_factory, bitcoind):
40594059
l1, l2 = node_factory.line_graph(2, fundchannel=False)
40604060
configfile = os.path.join(l2.daemon.opts.get("lightning-dir"), TEST_NETWORK, 'config')
4061+
setconfigfile = configfile + ".setconfig"
40614062

40624063
assert (l2.rpc.listconfigs('min-capacity-sat')['configs']
40634064
== {'min-capacity-sat':
@@ -4093,16 +4094,13 @@ def test_setconfig(node_factory, bitcoind):
40934094
ret = l2.rpc.setconfig(config='min-capacity-sat', val=500000)
40944095
assert ret == {'config':
40954096
{'config': 'min-capacity-sat',
4096-
'source': '{}:2'.format(configfile),
4097+
'source': '{}:2'.format(setconfigfile),
40974098
'value_int': 500000,
40984099
'dynamic': True}}
40994100

4100-
with open(configfile, 'r') as f:
4101+
with open(setconfigfile, 'r') as f:
41014102
lines = f.read().splitlines()
4102-
timeline = lines[0]
4103-
assert lines[0].startswith('# Inserted by setconfig ')
4104-
assert lines[1] == 'min-capacity-sat=500000'
4105-
assert len(lines) == 2
4103+
assert lines == ["# Created and update by setconfig, but you can edit this manually when node is stopped.", "min-capacity-sat=500000"]
41064104

41074105
# Now we need to meet minumum
41084106
with pytest.raises(RpcError, match='which is below 500000sat'):
@@ -4119,7 +4117,7 @@ def test_setconfig(node_factory, bitcoind):
41194117

41204118
assert (l2.rpc.listconfigs('min-capacity-sat')['configs']
41214119
== {'min-capacity-sat':
4122-
{'source': '{}:2'.format(configfile),
4120+
{'source': '{}:2'.format(setconfigfile),
41234121
'value_int': 500000,
41244122
'dynamic': True}})
41254123

@@ -4128,24 +4126,21 @@ def test_setconfig(node_factory, bitcoind):
41284126
with pytest.raises(RpcError, match='which is below 500000sat'):
41294127
l1.fundchannel(l2, 400000)
41304128

4131-
# Now, changing again will comment that one out!
4129+
# Now, changing again will replace that one!
41324130
ret = l2.rpc.setconfig(config='min-capacity-sat', val=400000)
41334131
assert ret == {'config':
41344132
{'config': 'min-capacity-sat',
4135-
'source': '{}:2'.format(configfile),
4133+
'source': '{}:2'.format(setconfigfile),
41364134
'value_int': 400000,
41374135
'dynamic': True}}
41384136

4139-
with open(configfile, 'r') as f:
4137+
with open(setconfigfile, 'r') as f:
41404138
lines = f.read().splitlines()
4141-
assert lines[0].startswith('# Inserted by setconfig ')
4142-
# It will have changed timestamp since last time!
4143-
assert lines[0] != timeline
4144-
assert lines[1] == 'min-capacity-sat=400000'
4145-
assert len(lines) == 2
4139+
assert lines == ["# Created and update by setconfig, but you can edit this manually when node is stopped.", "min-capacity-sat=400000"]
41464140

41474141
# If it's not set by setconfig, it will comment it out instead.
41484142
l2.stop()
4143+
os.unlink(setconfigfile)
41494144

41504145
with open(configfile, 'w') as f:
41514146
f.write('min-capacity-sat=500000\n')
@@ -4154,16 +4149,20 @@ def test_setconfig(node_factory, bitcoind):
41544149
ret = l2.rpc.setconfig(config='min-capacity-sat', val=400000)
41554150
assert ret == {'config':
41564151
{'config': 'min-capacity-sat',
4157-
'source': '{}:3'.format(configfile),
4152+
'source': '{}:2'.format(setconfigfile),
41584153
'value_int': 400000,
41594154
'dynamic': True}}
41604155

4156+
with open(setconfigfile, 'r') as f:
4157+
lines = f.read().splitlines()
4158+
assert lines == ["# Created and update by setconfig, but you can edit this manually when node is stopped.", "min-capacity-sat=400000"]
4159+
41614160
with open(configfile, 'r') as f:
41624161
lines = f.read().splitlines()
4163-
assert lines[0].startswith('# setconfig commented out: min-capacity-sat=500000')
4164-
assert lines[1].startswith('# Inserted by setconfig ')
4165-
assert lines[2] == 'min-capacity-sat=400000'
4166-
assert len(lines) == 3
4162+
assert lines[1].startswith("# Inserted by setconfig ")
4163+
assert lines == ['# setconfig commented out (see config.setconfig): min-capacity-sat=500000',
4164+
lines[1],
4165+
'include config.setconfig']
41674166

41684167
# We can also set it transiently.
41694168
ret = l2.rpc.setconfig(config='min-capacity-sat', val=400001, transient=True)
@@ -4174,12 +4173,9 @@ def test_setconfig(node_factory, bitcoind):
41744173
'dynamic': True}}
41754174

41764175
# So this won't change.
4177-
with open(configfile, 'r') as f:
4176+
with open(setconfigfile, 'r') as f:
41784177
lines = f.read().splitlines()
4179-
assert lines[0].startswith('# setconfig commented out: min-capacity-sat=500000')
4180-
assert lines[1].startswith('# Inserted by setconfig ')
4181-
assert lines[2] == 'min-capacity-sat=400000'
4182-
assert len(lines) == 3
4178+
assert lines == ["# Created and update by setconfig, but you can edit this manually when node is stopped.", "min-capacity-sat=400000"]
41834179

41844180

41854181
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3")

0 commit comments

Comments
 (0)