mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 01:43:36 +01:00
common/configvar: routines to manage config settings.
These are gathered from the config files and the commandline, but the process is rather complex! We want to remember where the options came from in future (for a `setconfig` command), and also generalize and simplify handling. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
f7df7eeb42
commit
36200a6593
@ -8,6 +8,7 @@ ALL_PROGRAMS += cli/lightning-cli
|
||||
LIGHTNING_CLI_COMMON_OBJS := \
|
||||
bitcoin/chainparams.o \
|
||||
common/configdir.o \
|
||||
common/configvar.o \
|
||||
common/json_parse_simple.o \
|
||||
common/status_levels.o \
|
||||
common/utils.o \
|
||||
|
@ -23,6 +23,7 @@ COMMON_SRC_NOGEN := \
|
||||
common/close_tx.c \
|
||||
common/coin_mvt.c \
|
||||
common/configdir.c \
|
||||
common/configvar.c \
|
||||
common/cryptomsg.c \
|
||||
common/daemon.c \
|
||||
common/daemon_conn.c \
|
||||
|
@ -384,7 +384,9 @@ void initial_config_opts(const tal_t *ctx,
|
||||
opt_free_table();
|
||||
|
||||
opt_register_early_arg("--conf=<file>",
|
||||
opt_restricted_cmdline, NULL,
|
||||
opt_restricted_cmdline,
|
||||
/* This doesn't show if NULL! */
|
||||
opt_show_charp,
|
||||
config_filename,
|
||||
"Specify configuration file");
|
||||
|
||||
|
231
common/configvar.c
Normal file
231
common/configvar.c
Normal file
@ -0,0 +1,231 @@
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <ccan/cast/cast.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/configvar.h>
|
||||
#include <common/utils.h>
|
||||
|
||||
struct configvar *configvar_new(const tal_t *ctx,
|
||||
enum configvar_src src,
|
||||
const char *file,
|
||||
size_t linenum,
|
||||
const char *configline)
|
||||
{
|
||||
struct configvar *cv = tal(ctx, struct configvar);
|
||||
if (file)
|
||||
cv->file = tal_strdup(cv, file);
|
||||
else
|
||||
cv->file = NULL;
|
||||
cv->src = src;
|
||||
cv->linenum = linenum;
|
||||
cv->configline = tal_strdup(cv, configline);
|
||||
cv->overridden = false;
|
||||
cv->optvar = NULL;
|
||||
/* We fill in cv->optvar and cv->optarg when parsing! */
|
||||
return cv;
|
||||
}
|
||||
|
||||
const struct opt_table *configvar_unparsed(struct configvar *cv)
|
||||
{
|
||||
const struct opt_table *ot;
|
||||
|
||||
if (cv->src == CONFIGVAR_CMDLINE_SHORT) {
|
||||
ot = opt_find_short(cv->configline[0]);
|
||||
cv->optarg = NULL;
|
||||
} else {
|
||||
ot = opt_find_long(cv->configline, &cv->optarg);
|
||||
}
|
||||
if (!ot)
|
||||
return NULL;
|
||||
|
||||
/* We get called multiple times, but we're expected to always
|
||||
* finish the cv vars, even if they're added at the last minute
|
||||
* on the cmdline, so we check this is only done once, and we
|
||||
* do it even if we're not going to use it now. */
|
||||
if (!cv->optvar) {
|
||||
/* optvar is up to the = (i.e. one char before optarg) */
|
||||
if (!cv->optarg)
|
||||
cv->optvar = cv->configline;
|
||||
else
|
||||
cv->optvar = tal_strndup(cv, cv->configline,
|
||||
cv->optarg - cv->configline - 1);
|
||||
}
|
||||
return ot;
|
||||
}
|
||||
|
||||
const char *configvar_parse(struct configvar *cv,
|
||||
bool early,
|
||||
bool full_knowledge,
|
||||
bool developer)
|
||||
{
|
||||
const struct opt_table *ot;
|
||||
|
||||
ot = configvar_unparsed(cv);
|
||||
if (!ot) {
|
||||
/* Do we ignore unknown entries? */
|
||||
if (!full_knowledge)
|
||||
return NULL;
|
||||
return "unknown option";
|
||||
}
|
||||
|
||||
if ((ot->type & OPT_DEV) && !developer)
|
||||
return "requires DEVELOPER";
|
||||
|
||||
/* If we're early and we want late, or vv, ignore. */
|
||||
if (!!(ot->type & OPT_EARLY) != early)
|
||||
return NULL;
|
||||
|
||||
if (ot->type & OPT_NOARG) {
|
||||
/* MULTI doesn't make sense with single args */
|
||||
assert(!(ot->type & OPT_MULTI));
|
||||
if (cv->optarg)
|
||||
return "doesn't allow an argument";
|
||||
return ot->cb(ot->u.arg);
|
||||
} else {
|
||||
if (!cv->optarg)
|
||||
return "requires an argument";
|
||||
return ot->cb_arg(cv->optarg, ot->u.arg);
|
||||
}
|
||||
}
|
||||
|
||||
/* This is O(N^2) but nobody cares */
|
||||
void configvar_finalize_overrides(struct configvar **cvs)
|
||||
{
|
||||
/* Map to options: two different names can be the same option,
|
||||
* given aliases! */
|
||||
const struct opt_table **opts;
|
||||
|
||||
opts = tal_arr(tmpctx, const struct opt_table *, tal_count(cvs));
|
||||
for (size_t i = 0; i < tal_count(cvs); i++) {
|
||||
opts[i] = opt_find_long(cvs[i]->optvar, NULL);
|
||||
/* If you're allowed multiple, they don't override */
|
||||
if (opts[i]->type & OPT_MULTI)
|
||||
continue;
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
if (opts[j] == opts[i])
|
||||
cvs[j]->overridden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void configvar_remove(struct configvar ***cvs,
|
||||
const char *name,
|
||||
enum configvar_src src,
|
||||
const char *optarg)
|
||||
{
|
||||
/* We remove all from this source, potentially restoring an overridden */
|
||||
ssize_t prev = -1;
|
||||
bool removed;
|
||||
|
||||
removed = false;
|
||||
for (size_t i = 0; i < tal_count(*cvs); i++) {
|
||||
/* This can happen if plugin fails during startup! */
|
||||
if ((*cvs)[i]->optvar == NULL)
|
||||
continue;
|
||||
if (!streq((*cvs)[i]->optvar, name))
|
||||
continue;
|
||||
if (optarg && !streq((*cvs)[i]->optarg, optarg))
|
||||
continue;
|
||||
|
||||
if ((*cvs)[i]->src == src) {
|
||||
tal_free((*cvs)[i]);
|
||||
tal_arr_remove(cvs, i);
|
||||
i--;
|
||||
removed = true;
|
||||
continue;
|
||||
}
|
||||
/* Wrong type, correct name. */
|
||||
prev = i;
|
||||
}
|
||||
|
||||
/* Unmark prev if we removed overriding ones. If it's multi,
|
||||
* this is a noop. */
|
||||
if (removed && prev != -1)
|
||||
(*cvs)[prev]->overridden = false;
|
||||
}
|
||||
|
||||
struct configvar *configvar_dup(const tal_t *ctx, const struct configvar *cv)
|
||||
{
|
||||
struct configvar *ret;
|
||||
|
||||
if (taken(cv))
|
||||
return tal_steal(ctx, cast_const(struct configvar *, cv));
|
||||
|
||||
ret = tal_dup(ctx, struct configvar, cv);
|
||||
if (ret->file)
|
||||
ret->file = tal_strdup(ret, ret->file);
|
||||
if (ret->configline)
|
||||
ret->configline = tal_strdup(ret, ret->configline);
|
||||
if (ret->optvar) {
|
||||
ret->optvar = tal_strdup(ret, ret->optvar);
|
||||
/* Optarg, if non-NULL, points into cmdline! */
|
||||
if (ret->optarg) {
|
||||
size_t off = cv->optarg - cv->configline;
|
||||
assert(off < strlen(cv->configline));
|
||||
ret->optarg = ret->configline + off;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct configvar **configvar_join(const tal_t *ctx,
|
||||
struct configvar **first,
|
||||
struct configvar **second)
|
||||
{
|
||||
struct configvar **cvs;
|
||||
size_t n = tal_count(first);
|
||||
|
||||
if (taken(first)) {
|
||||
cvs = tal_steal(ctx, first);
|
||||
tal_resize(&cvs, n + tal_count(second));
|
||||
} else {
|
||||
cvs = tal_arr(ctx, struct configvar *, n + tal_count(second));
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
cvs[i] = configvar_dup(cvs, first[i]);
|
||||
}
|
||||
}
|
||||
if (taken(second)) {
|
||||
for (size_t i = 0; i < tal_count(second); i++)
|
||||
cvs[n + i] = tal_steal(cvs, second[i]);
|
||||
tal_free(second);
|
||||
} else {
|
||||
for (size_t i = 0; i < tal_count(second); i++)
|
||||
cvs[n + i] = configvar_dup(cvs, second[i]);
|
||||
}
|
||||
|
||||
return cvs;
|
||||
}
|
||||
|
||||
static struct configvar *configvar_iter(struct configvar **cvs,
|
||||
const char **names,
|
||||
const struct configvar *firstcv)
|
||||
{
|
||||
for (size_t i = 0; i < tal_count(cvs); i++) {
|
||||
/* Wait until we reach firstcv, if any */
|
||||
if (firstcv) {
|
||||
if (cvs[i] == firstcv)
|
||||
firstcv = NULL;
|
||||
continue;
|
||||
}
|
||||
for (size_t j = 0; j < tal_count(names); j++) {
|
||||
/* In case we iterate before initialization! */
|
||||
if (!cvs[i]->optvar)
|
||||
continue;
|
||||
if (streq(cvs[i]->optvar, names[j]) && !cvs[i]->overridden)
|
||||
return cvs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct configvar *configvar_first(struct configvar **cvs, const char **names)
|
||||
{
|
||||
return configvar_iter(cvs, names, NULL);
|
||||
}
|
||||
|
||||
struct configvar *configvar_next(struct configvar **cvs,
|
||||
const struct configvar *cv,
|
||||
const char **names)
|
||||
{
|
||||
return configvar_iter(cvs, names, cv);
|
||||
}
|
177
common/configvar.h
Normal file
177
common/configvar.h
Normal file
@ -0,0 +1,177 @@
|
||||
#ifndef LIGHTNING_COMMON_CONFIGVAR_H
|
||||
#define LIGHTNING_COMMON_CONFIGVAR_H
|
||||
#include "config.h"
|
||||
#include <ccan/opt/opt.h>
|
||||
#include <ccan/short_types/short_types.h>
|
||||
#include <ccan/tal/tal.h>
|
||||
|
||||
/* There are five possible sources of config options:
|
||||
* 1. The cmdline (and the cmdline, but it's a short option!)
|
||||
* 2. An explicitly named config (--conf=) (and its includes)
|
||||
* 3. An implied config (~/.lightning/config) (and includes)
|
||||
* 4. A network config ((~/.lightning/<network>/config) (and includes)
|
||||
* 5. A plugin start parameter.
|
||||
*
|
||||
* Turns out we care: you can't set network in a network config for
|
||||
* example.
|
||||
*/
|
||||
enum configvar_src {
|
||||
CONFIGVAR_CMDLINE,
|
||||
CONFIGVAR_CMDLINE_SHORT,
|
||||
CONFIGVAR_EXPLICIT_CONF,
|
||||
CONFIGVAR_BASE_CONF,
|
||||
CONFIGVAR_NETWORK_CONF,
|
||||
CONFIGVAR_PLUGIN_START,
|
||||
};
|
||||
|
||||
/* This represents the configuration variables specified; they are
|
||||
* matched with ccan/option's opt_table which contains the
|
||||
* available options. */
|
||||
struct configvar {
|
||||
/* NULL if CONFIGVAR_CMDLINE* or CONFIGVAR_PLUGIN_START */
|
||||
const char *file;
|
||||
/* 1-based line number (unused if !file) */
|
||||
u32 linenum;
|
||||
/* Where did we get this from? */
|
||||
enum configvar_src src;
|
||||
/* Never NULL, the whole line */
|
||||
const char *configline;
|
||||
|
||||
/* These are filled in by configvar_parse */
|
||||
/* The variable name (without any =) */
|
||||
const char *optvar;
|
||||
/* NULL for no-arg options, otherwise points after =. */
|
||||
const char *optarg;
|
||||
/* Was this overridden by a following option? */
|
||||
bool overridden;
|
||||
};
|
||||
|
||||
/* Set if multiple options accumulate (for listconfigs) */
|
||||
#define OPT_MULTI (1 << OPT_USER_START)
|
||||
/* Set if developer-only */
|
||||
#define OPT_DEV (1 << (OPT_USER_START+1))
|
||||
|
||||
/* Use this instead of opt_register_*_arg if you want OPT_MULTI/OPT_DEV */
|
||||
#define clnopt_witharg(names, type, cb, show, arg, desc) \
|
||||
_opt_register((names), \
|
||||
OPT_CB_ARG((cb), (type), (show), (arg)), \
|
||||
(arg), (desc))
|
||||
|
||||
#define clnopt_noarg(names, type, cb, arg, desc) \
|
||||
_opt_register((names), \
|
||||
OPT_CB_NOARG((cb), (type), (arg)), \
|
||||
(arg), (desc))
|
||||
|
||||
/**
|
||||
* configvar_new: allocate a fresh configvar
|
||||
* @ctx: parent to tallocate off
|
||||
* @src: where this came from
|
||||
* @file: filename (or NULL if cmdline)
|
||||
* @linenum: 1-based line number (or 0 for cmdline)
|
||||
* @configline: literal option (for argv[], after `--`)
|
||||
*
|
||||
* optvar/optarg/multi/overridden are only set by configvar_parse.
|
||||
*/
|
||||
struct configvar *configvar_new(const tal_t *ctx,
|
||||
enum configvar_src src,
|
||||
const char *file TAKES,
|
||||
size_t linenum,
|
||||
const char *configline TAKES)
|
||||
NON_NULL_ARGS(5);
|
||||
|
||||
/**
|
||||
* configvar_dup: copy a configvar
|
||||
* @ctx: parent to tallocate off
|
||||
* @cv: configvar to copy.
|
||||
*/
|
||||
struct configvar *configvar_dup(const tal_t *ctx,
|
||||
const struct configvar *cv TAKES)
|
||||
NON_NULL_ARGS(2);
|
||||
|
||||
/**
|
||||
* configvar_join: join two configvar arrays
|
||||
* @ctx: parent to tallocate off
|
||||
* @first: configvars to copy first
|
||||
* @second: configvars to copy second.
|
||||
*/
|
||||
struct configvar **configvar_join(const tal_t *ctx,
|
||||
struct configvar **first TAKES,
|
||||
struct configvar **second TAKES);
|
||||
|
||||
/**
|
||||
* configvar_parse: parse this configuration variable
|
||||
* @cv: the configuration setting.
|
||||
* @early: if we're doing early parsing.
|
||||
* @full_knowledge: error if we don't know this option.
|
||||
* @developer: if we're in developer mode (allow OPT_DEV options).
|
||||
*
|
||||
* This returns a string if parsing failed: if early_and_incomplete is
|
||||
* set, it doesn't complain about unknown options, and only parses
|
||||
* OPT_EARLY options. Otherwise it parses all non-OPT_EARLY options,
|
||||
* and returns an error if they don't exist.
|
||||
*
|
||||
* On NULL return (success), cv->optvar, cv->optarg, cv->mult are set.
|
||||
*/
|
||||
const char *configvar_parse(struct configvar *cv,
|
||||
bool early,
|
||||
bool full_knowledge,
|
||||
bool developer)
|
||||
NON_NULL_ARGS(1);
|
||||
|
||||
/**
|
||||
* configvar_unparsed: set up configuration variable, but don't parse it
|
||||
* @cv: the configuration setting.
|
||||
*
|
||||
* This returns the opt_table which matches this configvar, if any,
|
||||
* and if successful initializes cv->optvar and cv->optarg.
|
||||
*/
|
||||
const struct opt_table *configvar_unparsed(struct configvar *cv)
|
||||
NON_NULL_ARGS(1);
|
||||
|
||||
/**
|
||||
* configvar_finalize_overrides: figure out which vars were overridden
|
||||
* @cvs: the tal_arr of configuration settings.
|
||||
*
|
||||
* Any non-multi variables are overridden by successive ones. Sets
|
||||
* cv->overridden for each configvar.
|
||||
*/
|
||||
void configvar_finalize_overrides(struct configvar **cvs);
|
||||
|
||||
/**
|
||||
* configvar_remove: remove the last configvar with this name if any.
|
||||
* @cvs: pointer to tal_arr of configuration settings.
|
||||
* @name: name to remove.
|
||||
* @src: source type to remove.
|
||||
* @optarg: if non-NULL, the argument to match too.
|
||||
*
|
||||
* We have to un-override the now-last setting, if any.
|
||||
*/
|
||||
void configvar_remove(struct configvar ***cvs,
|
||||
const char *name,
|
||||
enum configvar_src src,
|
||||
const char *optarg)
|
||||
NON_NULL_ARGS(1, 2);
|
||||
|
||||
/**
|
||||
* configvar_first: get the first non-overridden configvar of this name.
|
||||
* @cvs: the tal_arr of configuration settings.
|
||||
* @names: the tal_arr() of names to look for.
|
||||
*
|
||||
* Returns NULL if it wasn't set.
|
||||
*/
|
||||
struct configvar *configvar_first(struct configvar **cvs, const char **names);
|
||||
|
||||
/**
|
||||
* configvar_next: get the next non-overridden configvar of same name.
|
||||
* @cvs: the tal_arr of configuration settings.
|
||||
* @prev: the non-NULL return from configvar_first/configvar_next
|
||||
* @names: the tal_arr() of names to look for.
|
||||
*
|
||||
* This can only return non-NULL for OPT_MULTI options which are actually
|
||||
* specified multiple times.
|
||||
*/
|
||||
struct configvar *configvar_next(struct configvar **cvs,
|
||||
const struct configvar *prev,
|
||||
const char **names);
|
||||
|
||||
#endif /* LIGHTNING_COMMON_CONFIGVAR_H */
|
Loading…
Reference in New Issue
Block a user