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\. 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 All these options are mirrored as commandline arguments to
\fBlightningd\fR(8), so \fI--foo\fR becomes simply \fIfoo\fR in the configuration \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\. 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 options: later options override earlier ones except *addr* options
and *log-level* with subsystems, which accumulate. 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 All these options are mirrored as commandline arguments to
lightningd(8), so *--foo* becomes simply *foo* in the configuration lightningd(8), so *--foo* becomes simply *foo* in the configuration
file, and *--foo=bar* becomes *foo=bar* in the configuration file. 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); ld->pidfile = tal_fmt(ld, "lightningd-%s.pid", chainparams->network_name);
} }
static int config_parse_line_number;
/* FIXME: make this nicer! */
static int config_parse_line_number = 0;
static void config_log_stderr_exit(const char *fmt, ...) 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); assert(problem != NULL);
/*mangle it to remove '--' and add the line number.*/ /*mangle it to remove '--' and add the line number.*/
msg = tal_fmt(NULL, "%s line %d: %.*s: %s", 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 { } else {
msg = tal_vfmt(NULL, fmt, ap); msg = tal_vfmt(NULL, fmt, ap);
} }
@ -732,30 +731,22 @@ static void config_log_stderr_exit(const char *fmt, ...)
fatal("%s", msg); fatal("%s", msg);
} }
/** static void parse_include(struct lightningd *ld,
* We turn the config file into cmdline arguments. @early tells us const char *filename,
* whether to parse early options only (and ignore any unknown ones), bool must_exist,
* or the non-early options. bool early)
*/
static void opt_parse_from_config(struct lightningd *ld, bool early)
{ {
char *contents, **lines; 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]; char *argv[3];
int i, argc; 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); contents = grab_file(ld, filename);
/* The default config doesn't have to exist, but if the config was /* The default config doesn't have to exist, but if the config was
* specified on the command line it has to exist. */ * specified on the command line it has to exist. */
if (!contents) { if (!contents) {
if ((errno != ENOENT) || (ld->config_filename != NULL)) if (must_exist)
fatal("Opening and reading %s: %s", fatal("Opening and reading %s: %s",
filename, strerror(errno)); filename, strerror(errno));
return; 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++) { for (i = 0; i < tal_count(lines) - 1; i++) {
if (strstarts(lines[i], "#")) { if (strstarts(lines[i], "#")) {
all_args[i] = NULL; all_args[i] = NULL;
} } else if (strstarts(lines[i], "include ")) {
else { /* 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" */ /* Only valid forms are "foo" and "foo=bar" */
all_args[i] = tal_fmt(all_args, "--%s", lines[i]); 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. argv[1] is the only element that changes between iterations.
*/ */
argc = 2; argc = 2;
argv[0] = "lightning config file"; argv[0] = cast_const(char *, filename);
argv[argc] = NULL; argv[argc] = NULL;
if (early) { for (i = 0; i < tal_count(all_args); i++) {
for (i = 0; i < tal_count(all_args); i++) { if (all_args[i] == NULL)
if (all_args[i] != NULL) { continue;
config_parse_line_number = i + 1;
argv[1] = all_args[i]; if (!strstarts(all_args[i], "--")) {
opt_early_parse_incomplete(argc, argv, parse_include(ld, all_args[i], true, early);
config_log_stderr_exit); continue;
}
} }
} else {
for (i = 0; i < tal_count(all_args); i++) { config_parse_line_number = i + 1;
if (all_args[i] != NULL) { argv[1] = all_args[i];
config_parse_line_number = i + 1; if (early) {
argv[1] = all_args[i]; opt_early_parse_incomplete(argc, argv,
opt_parse(&argc, argv, config_log_stderr_exit); config_log_stderr_exit);
argc = 2; /* opt_parse might have changed it */ } else {
} opt_parse(&argc, argv, config_log_stderr_exit);
argc = 2; /* opt_parse might have changed it */
} }
} }
tal_free(contents); 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) static char *test_subdaemons_and_exit(struct lightningd *ld)
{ {
test_subdaemons(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) checknokey = l2.rpc.checkmessage(message="message for you", zbase=zm)
assert checknokey['pubkey'] == l1.info['id'] assert checknokey['pubkey'] == l1.info['id']
assert checknokey['verified'] 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'