config: Add include directive support.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Config: configuration files now support `include`.
This commit is contained in:
Rusty Russell 2019-11-23 12:14:51 +10:30
parent 6defc69482
commit 34c89cb226
4 changed files with 74 additions and 37 deletions

View File

@ -18,6 +18,10 @@ options: later options override earlier ones except \fIaddr\fR options
and \fIlog-level\fR with subsystems, which accumulate\.
\fIinclude \fR followed by a filename includes another configuration file at that
point, relative to the current configuration file\.
All these options are mirrored as commandline arguments to
\fBlightningd\fR(8), so \fI--foo\fR becomes simply \fIfoo\fR in the configuration
file, and \fI--foo=bar\fR becomes \fIfoo=bar\fR in the configuration file\.

View File

@ -18,6 +18,9 @@ Configuration file options are processed first, then command line
options: later options override earlier ones except *addr* options
and *log-level* with subsystems, which accumulate.
*include * followed by a filename includes another configuration file at that
point, relative to the current configuration file.
All these options are mirrored as commandline arguments to
lightningd(8), so *--foo* becomes simply *foo* in the configuration
file, and *--foo=bar* becomes *foo=bar* in the configuration file.

View File

@ -700,9 +700,7 @@ static void setup_default_config(struct lightningd *ld)
ld->pidfile = tal_fmt(ld, "lightningd-%s.pid", chainparams->network_name);
}
/* FIXME: make this nicer! */
static int config_parse_line_number = 0;
static int config_parse_line_number;
static void config_log_stderr_exit(const char *fmt, ...)
{
@ -723,7 +721,8 @@ static void config_log_stderr_exit(const char *fmt, ...)
assert(problem != NULL);
/*mangle it to remove '--' and add the line number.*/
msg = tal_fmt(NULL, "%s line %d: %.*s: %s",
argv0, config_parse_line_number, len-2, arg+2, problem);
argv0,
config_parse_line_number, len-2, arg+2, problem);
} else {
msg = tal_vfmt(NULL, fmt, ap);
}
@ -732,30 +731,22 @@ static void config_log_stderr_exit(const char *fmt, ...)
fatal("%s", msg);
}
/**
* We turn the config file into cmdline arguments. @early tells us
* whether to parse early options only (and ignore any unknown ones),
* or the non-early options.
*/
static void opt_parse_from_config(struct lightningd *ld, bool early)
static void parse_include(struct lightningd *ld,
const char *filename,
bool must_exist,
bool early)
{
char *contents, **lines;
char **all_args; /*For each line: either argument string or NULL*/
char **all_args; /*For each line: either `--`argument, include file, or NULL*/
char *argv[3];
int i, argc;
char *filename;
if (ld->config_filename != NULL)
filename = ld->config_filename;
else
filename = path_join(tmpctx, ld->config_dir, "config");
contents = grab_file(ld, filename);
/* The default config doesn't have to exist, but if the config was
* specified on the command line it has to exist. */
if (!contents) {
if ((errno != ENOENT) || (ld->config_filename != NULL))
if (must_exist)
fatal("Opening and reading %s: %s",
filename, strerror(errno));
return;
@ -769,8 +760,13 @@ static void opt_parse_from_config(struct lightningd *ld, bool early)
for (i = 0; i < tal_count(lines) - 1; i++) {
if (strstarts(lines[i], "#")) {
all_args[i] = NULL;
}
else {
} else if (strstarts(lines[i], "include ")) {
/* If relative, it's relative to current config file */
all_args[i] = path_join(all_args,
take(path_dirname(NULL,
filename)),
lines[i] + strlen("include "));
} else {
/* Only valid forms are "foo" and "foo=bar" */
all_args[i] = tal_fmt(all_args, "--%s", lines[i]);
}
@ -781,32 +777,51 @@ static void opt_parse_from_config(struct lightningd *ld, bool early)
argv[1] is the only element that changes between iterations.
*/
argc = 2;
argv[0] = "lightning config file";
argv[0] = cast_const(char *, filename);
argv[argc] = NULL;
if (early) {
for (i = 0; i < tal_count(all_args); i++) {
if (all_args[i] != NULL) {
config_parse_line_number = i + 1;
argv[1] = all_args[i];
opt_early_parse_incomplete(argc, argv,
config_log_stderr_exit);
}
for (i = 0; i < tal_count(all_args); i++) {
if (all_args[i] == NULL)
continue;
if (!strstarts(all_args[i], "--")) {
parse_include(ld, all_args[i], true, early);
continue;
}
} else {
for (i = 0; i < tal_count(all_args); i++) {
if (all_args[i] != NULL) {
config_parse_line_number = i + 1;
argv[1] = all_args[i];
opt_parse(&argc, argv, config_log_stderr_exit);
argc = 2; /* opt_parse might have changed it */
}
config_parse_line_number = i + 1;
argv[1] = all_args[i];
if (early) {
opt_early_parse_incomplete(argc, argv,
config_log_stderr_exit);
} else {
opt_parse(&argc, argv, config_log_stderr_exit);
argc = 2; /* opt_parse might have changed it */
}
}
tal_free(contents);
}
/**
* We turn the config file into cmdline arguments. @early tells us
* whether to parse early options only (and ignore any unknown ones),
* or the non-early options.
*/
static void opt_parse_from_config(struct lightningd *ld, bool early)
{
const char *filename;
/* The default config doesn't have to exist, but if the config was
* specified on the command line it has to exist. */
if (ld->config_filename != NULL)
filename = ld->config_filename;
else
filename = path_join(tmpctx, ld->config_dir, "config");
parse_include(ld, filename, ld->config_filename != NULL, early);
}
static char *test_subdaemons_and_exit(struct lightningd *ld)
{
test_subdaemons(ld);

View File

@ -1767,3 +1767,18 @@ def test_signmessage(node_factory):
checknokey = l2.rpc.checkmessage(message="message for you", zbase=zm)
assert checknokey['pubkey'] == l1.info['id']
assert checknokey['verified']
def test_include(node_factory):
l1 = node_factory.get_node(start=False)
subdir = os.path.join(l1.daemon.opts.get("lightning-dir"), "subdir")
os.makedirs(subdir)
with open(os.path.join(subdir, "conf1"), 'w') as f:
f.write('include conf2')
with open(os.path.join(subdir, "conf2"), 'w') as f:
f.write('alias=conf2')
l1.daemon.opts['conf'] = os.path.join(subdir, "conf1")
l1.start()
assert l1.rpc.listconfigs('alias')['alias'] == 'conf2'