lightningd: move basic parameter parsing into common/configdir

lightning-cli is going to need to know what network we're on, so
it will need to parse the config files.  Move the code which does
the initial bootstrap parsing into common, as well as the config
file parsing core.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2019-11-23 12:15:53 +10:30
parent 5673607ebc
commit 8b1aa3ef8b
13 changed files with 357 additions and 324 deletions

View File

@@ -7,7 +7,6 @@
#include <ccan/read_write_all/read_write_all.h>
#include <ccan/short_types/short_types.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/tal/path/path.h>
#include <ccan/tal/str/str.h>
#include <common/configdir.h>
@@ -44,38 +43,12 @@
#include <wire/wire.h>
bool deprecated_apis = true;
static bool opt_table_alloced = false;
/* Declare opt_add_addr here, because we we call opt_add_addr
* and opt_announce_addr vice versa
*/
static char *opt_add_addr(const char *arg, struct lightningd *ld);
/* Tal wrappers for opt. */
static void *opt_allocfn(size_t size)
{
return tal_arr_label(NULL, char, size, TAL_LABEL("opt_allocfn", ""));
}
static void *tal_reallocfn(void *ptr, size_t size)
{
if (!ptr) {
/* realloc(NULL) call is to allocate opt_table */
if (!opt_table_alloced) {
opt_table_alloced = true;
return notleak(opt_allocfn(size));
}
return opt_allocfn(size);
}
tal_resize_(&ptr, 1, size, false);
return ptr;
}
static void tal_freefn(void *ptr)
{
tal_free(ptr);
}
/* FIXME: Put into ccan/time. */
#define TIME_FROM_SEC(sec) { { .tv_nsec = 0, .tv_sec = sec } }
#define TIME_FROM_MSEC(msec) \
@@ -250,38 +223,6 @@ static void opt_show_s32(char buf[OPT_SHOW_LEN], const s32 *u)
snprintf(buf, OPT_SHOW_LEN, "%"PRIi32, *u);
}
static char *opt_set_network(const char *arg, struct lightningd *ld)
{
assert(arg != NULL);
/* Set the global chainparams instance */
chainparams = chainparams_for_network(arg);
if (!chainparams)
return tal_fmt(NULL, "Unknown network name '%s'", arg);
return NULL;
}
static char *opt_set_testnet(struct lightningd *ld)
{
return opt_set_network("testnet", ld);
}
static char *opt_set_signet(struct lightningd *ld)
{
return opt_set_network("signet", ld);
}
static char *opt_set_mainnet(struct lightningd *ld)
{
return opt_set_network("bitcoin", ld);
}
static void opt_show_network(char buf[OPT_SHOW_LEN],
const struct lightningd *ld)
{
snprintf(buf, OPT_SHOW_LEN, "%s", chainparams->network_name);
}
static char *opt_set_rgb(const char *arg, struct lightningd *ld)
{
assert(arg != NULL);
@@ -688,121 +629,6 @@ static void check_config(struct lightningd *ld)
fatal("--always-use-proxy needs --proxy");
}
static void setup_default_config(struct lightningd *ld)
{
if (chainparams->testnet)
ld->config = testnet_config;
else
ld->config = mainnet_config;
/* Set default PID file name to be per-network */
tal_free(ld->pidfile);
ld->pidfile = tal_fmt(ld, "lightningd-%s.pid", chainparams->network_name);
}
static int config_parse_line_number;
static void config_log_stderr_exit(const char *fmt, ...)
{
char *msg;
va_list ap;
va_start(ap, fmt);
/* This is the format we expect:*/
if (streq(fmt, "%s: %.*s: %s")) {
const char *argv0 = va_arg(ap, const char *);
unsigned int len = va_arg(ap, unsigned int);
const char *arg = va_arg(ap, const char *);
const char *problem = va_arg(ap, const char *);
assert(argv0 != NULL);
assert(arg != NULL);
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);
} else {
msg = tal_vfmt(NULL, fmt, ap);
}
va_end(ap);
fatal("%s", msg);
}
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, include file, or NULL*/
char *argv[3];
int i, argc;
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 (must_exist)
fatal("Opening and reading %s: %s",
filename, strerror(errno));
return;
}
lines = tal_strsplit(contents, contents, "\r\n", STR_EMPTY_OK);
/* We have to keep all_args around, since opt will point into it */
all_args = notleak(tal_arr(ld, char *, tal_count(lines) - 1));
for (i = 0; i < tal_count(lines) - 1; i++) {
if (strstarts(lines[i], "#")) {
all_args[i] = NULL;
} 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]);
}
}
/*
For each line we construct a fake argc,argv commandline.
argv[1] is the only element that changes between iterations.
*/
argc = 2;
argv[0] = cast_const(char *, filename);
argv[argc] = NULL;
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;
}
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),
@@ -819,7 +645,7 @@ static void opt_parse_from_config(struct lightningd *ld, bool early)
else
filename = path_join(tmpctx, ld->config_dir, "config");
parse_include(ld, filename, ld->config_filename != NULL, early);
parse_include(filename, ld->config_filename != NULL, early);
}
static char *test_subdaemons_and_exit(struct lightningd *ld)
@@ -839,9 +665,6 @@ static char *list_features_and_exit(struct lightningd *ld)
static char *opt_lightningd_usage(struct lightningd *ld)
{
/* Reload config so that --help has the correct network defaults
* to display before it exits */
setup_default_config(ld);
char *extra = tal_fmt(NULL, "\nA bitcoin lightning daemon (default "
"values shown for network: %s).", chainparams->network_name);
opt_usage_and_exit(extra);
@@ -884,73 +707,6 @@ static char *opt_start_daemon(struct lightningd *ld)
errx(1, "Died with signal %u", WTERMSIG(exitcode));
}
static char *opt_ignore_talstr(const char *arg, char **p)
{
return NULL;
}
static char *opt_set_conf(const char *arg, struct lightningd *ld)
{
/* This is a pass-through if arg is absolute */
tal_free(ld->config_filename);
ld->config_filename = path_join(ld, path_cwd(tmpctx), arg);
return NULL;
}
/* Just enough parsing to find config file, and other maintenance options
* which don't want us to create the lightning dir */
static void handle_minimal_config_opts(struct lightningd *ld,
int argc, char *argv[])
{
/* First, they could specify a config, which specifies a lightning dir */
opt_register_early_arg("--conf=<file>", opt_set_conf, NULL,
ld,
"Specify configuration file (default: <lightning-dir>/config)");
ld->config_dir = NULL;
opt_register_early_arg("--lightning-dir=<dir>",
opt_set_talstr, NULL,
&ld->config_dir,
"Set working directory. All other files are relative to this");
/* List features immediately, before doing anything interesting */
opt_register_early_noarg("--list-features-only",
list_features_and_exit,
ld, opt_hidden);
/* Handle --version (and exit) here too: don't create lightning-dir for this */
opt_register_version();
opt_early_parse_incomplete(argc, argv, opt_log_stderr_exit);
/* Corner case: if they specified a config filename, and didn't set
* set lightning-dir, read config file to get it! */
if (ld->config_filename && !ld->config_dir)
opt_parse_from_config(ld, true);
if (!ld->config_dir)
ld->config_dir = default_configdir(ld);
/* Now, reset and ignore those options from now on. */
opt_free_table();
opt_table_alloced = false;
opt_register_early_arg("--conf=<file>", opt_ignore_talstr, NULL,
&ld->config_filename,
"Specify configuration file (default: <lightning-dir>/config)");
opt_register_early_arg("--lightning-dir=<dir>",
opt_ignore_talstr, opt_show_charp,
&ld->config_dir,
"Set working directory. All other files are relative to this");
ld->config_dir = path_join(ld, path_cwd(tmpctx), take(ld->config_dir));
ld->wallet_dsn = tal_fmt(ld, "sqlite3://%s/lightningd.sqlite3", ld->config_dir);
opt_register_early_arg("--wallet", opt_set_talstr, NULL,
&ld->wallet_dsn,
"Location of the wallet database.");
}
static void register_opts(struct lightningd *ld)
{
/* This happens before plugins started */
@@ -971,19 +727,6 @@ static void register_opts(struct lightningd *ld)
NULL, ld,
"Disable a particular plugin by filename/name");
/* We need to know network early, so we can set defaults (which normal
* options can change) */
opt_register_early_arg("--network", opt_set_network, opt_show_network,
ld,
"Select the network parameters (bitcoin, testnet,"
" regtest, litecoin or litecoin-testnet)");
opt_register_early_noarg("--testnet", opt_set_testnet, ld,
"Alias for --network=testnet");
opt_register_early_noarg("--signet", opt_set_signet, ld,
"Alias for --network=signet");
opt_register_early_noarg("--mainnet", opt_set_mainnet, ld,
"Alias for --network=bitcoin");
/* This can effect commandline parsing */
opt_register_early_arg("--allow-deprecated-apis",
opt_set_bool_arg, opt_show_bool,
@@ -998,10 +741,10 @@ static void register_opts(struct lightningd *ld)
/* This immediately makes is a daemon. */
opt_register_early_noarg("--daemon", opt_start_daemon, ld,
"Run in the background, suppress stdout/stderr");
opt_register_early_arg("--wallet", opt_set_talstr, NULL,
&ld->wallet_dsn,
"Location of the wallet database.");
opt_register_arg("--rpc-file", opt_set_talstr, opt_show_charp,
&ld->rpc_filename,
"Set JSON-RPC socket (or /dev/tty)");
opt_register_noarg("--help|-h", opt_lightningd_usage, ld,
"Print this message.");
opt_register_arg("--bitcoin-datadir", opt_set_talstr, NULL,
@@ -1177,11 +920,33 @@ void setup_color_and_alias(struct lightningd *ld)
void handle_early_opts(struct lightningd *ld, int argc, char *argv[])
{
/*~ These functions make ccan/opt use tal for allocations */
opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn);
/* Make ccan/opt use tal for allocations */
setup_option_allocators();
/*~ Handle --conf and --lightning-dir super-early. */
handle_minimal_config_opts(ld, argc, argv);
/*~ List features immediately, before doing anything interesting */
opt_register_early_noarg("--list-features-only",
list_features_and_exit,
ld, opt_hidden);
/*~ This does enough parsing to get us the base configuration options */
initial_config_opts(ld, argc, argv,
&ld->config_filename,
&ld->config_dir,
&ld->rpc_filename);
/* Copy in default config, to be modified by further options */
if (chainparams->testnet)
ld->config = testnet_config;
else
ld->config = mainnet_config;
/* Now we can initialize wallet_dsn */
ld->wallet_dsn = tal_fmt(ld, "sqlite3://%s/lightningd.sqlite3",
ld->config_dir);
/* Set default PID file name to be per-network */
ld->pidfile = tal_fmt(ld, "lightningd-%s.pid",
chainparams->network_name);
/*~ Move into config dir: this eases path manipulation and also
* gives plugins a good place to store their stuff. */
@@ -1207,9 +972,6 @@ void handle_early_opts(struct lightningd *ld, int argc, char *argv[])
/* Early cmdline options now override config file options. */
opt_early_parse_incomplete(argc, argv, opt_log_stderr_exit);
/* Now we know what network we're on, initialize defaults. */
setup_default_config(ld);
/* Finalize the logging subsystem now. */
logging_options_parsed(ld->log_book);
}
@@ -1282,10 +1044,7 @@ static void add_config(struct lightningd *ld,
/* Ignore hidden options (deprecated) */
} else if (opt->cb == (void *)opt_usage_and_exit
|| opt->cb == (void *)version_and_exit
/* These two show up as --network= */
|| opt->cb == (void *)opt_set_testnet
|| opt->cb == (void *)opt_set_signet
|| opt->cb == (void *)opt_set_mainnet
|| opt->cb == (void *)opt_ignore_noarg
|| opt->cb == (void *)opt_lightningd_usage
|| opt->cb == (void *)test_subdaemons_and_exit
/* FIXME: we can't recover this. */
@@ -1335,7 +1094,7 @@ static void add_config(struct lightningd *ld,
answer = buf;
} else if (opt->cb_arg == (void *)opt_set_talstr
|| opt->cb_arg == (void *)opt_set_charp
|| opt->cb_arg == (void *)opt_ignore_talstr) {
|| opt->cb_arg == (void *)opt_ignore) {
const char *arg = *(char **)opt->u.carg;
if (arg)
answer = tal_fmt(name0, "%s", arg);