mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +01:00
JSONRPC: listconfigs command.
With the new 'human-readable' mode of lightning-cli, this actually produces a valid config file. It's a bit hacky though... Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
Christian Decker
parent
0600aac68f
commit
327e2b8fd8
@@ -414,7 +414,7 @@ static void log_to_file(const char *prefix,
|
|||||||
fflush(logf);
|
fflush(logf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *arg_log_to_file(const char *arg, struct lightningd *ld)
|
char *arg_log_to_file(const char *arg, struct lightningd *ld)
|
||||||
{
|
{
|
||||||
FILE *logf;
|
FILE *logf;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
struct lightningd;
|
struct lightningd;
|
||||||
struct timerel;
|
struct timerel;
|
||||||
|
struct lightningd;
|
||||||
|
|
||||||
enum log_level {
|
enum log_level {
|
||||||
/* Logging all IO. */
|
/* Logging all IO. */
|
||||||
@@ -89,6 +90,8 @@ void log_dump_to_file(int fd, const struct log_book *lr);
|
|||||||
void opt_register_logging(struct lightningd *ld);
|
void opt_register_logging(struct lightningd *ld);
|
||||||
void crashlog_activate(const char *argv0, struct log *log);
|
void crashlog_activate(const char *argv0, struct log *log);
|
||||||
|
|
||||||
|
char *arg_log_to_file(const char *arg, struct lightningd *ld);
|
||||||
|
|
||||||
/* Convenience parent for temporary allocations (eg. type_to_string)
|
/* Convenience parent for temporary allocations (eg. type_to_string)
|
||||||
* during log calls; freed after every log_*() */
|
* during log calls; freed after every log_*() */
|
||||||
const tal_t *ltmp;
|
const tal_t *ltmp;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include <bitcoin/chainparams.h>
|
#include <bitcoin/chainparams.h>
|
||||||
#include <ccan/array_size/array_size.h>
|
#include <ccan/array_size/array_size.h>
|
||||||
#include <ccan/err/err.h>
|
#include <ccan/err/err.h>
|
||||||
|
#include <ccan/mem/mem.h>
|
||||||
#include <ccan/opt/opt.h>
|
#include <ccan/opt/opt.h>
|
||||||
|
#include <ccan/opt/private.h>
|
||||||
#include <ccan/read_write_all/read_write_all.h>
|
#include <ccan/read_write_all/read_write_all.h>
|
||||||
#include <ccan/short_types/short_types.h>
|
#include <ccan/short_types/short_types.h>
|
||||||
#include <ccan/tal/grab_file/grab_file.h>
|
#include <ccan/tal/grab_file/grab_file.h>
|
||||||
@@ -15,6 +17,7 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <lightningd/bitcoind.h>
|
#include <lightningd/bitcoind.h>
|
||||||
#include <lightningd/chaintopology.h>
|
#include <lightningd/chaintopology.h>
|
||||||
|
#include <lightningd/jsonrpc.h>
|
||||||
#include <lightningd/lightningd.h>
|
#include <lightningd/lightningd.h>
|
||||||
#include <lightningd/log.h>
|
#include <lightningd/log.h>
|
||||||
#include <lightningd/netaddress.h>
|
#include <lightningd/netaddress.h>
|
||||||
@@ -663,3 +666,169 @@ bool handle_opts(struct lightningd *ld, int argc, char *argv[])
|
|||||||
|
|
||||||
return newdir;
|
return newdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: This is a hack! Expose somehow in ccan/opt.*/
|
||||||
|
/* Returns string after first '-'. */
|
||||||
|
static const char *first_name(const char *names, unsigned *len)
|
||||||
|
{
|
||||||
|
*len = strcspn(names + 1, "|= ");
|
||||||
|
return names + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *next_name(const char *names, unsigned *len)
|
||||||
|
{
|
||||||
|
names += *len;
|
||||||
|
if (names[0] == ' ' || names[0] == '=' || names[0] == '\0')
|
||||||
|
return NULL;
|
||||||
|
return first_name(names + 1, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_config(struct lightningd *ld,
|
||||||
|
struct json_result *response,
|
||||||
|
const struct opt_table *opt,
|
||||||
|
const char *name, size_t len)
|
||||||
|
{
|
||||||
|
char *name0 = tal_strndup(response, name, len);
|
||||||
|
const char *answer = NULL;
|
||||||
|
|
||||||
|
if (opt->type & OPT_NOARG) {
|
||||||
|
if (opt->cb == (void *)opt_usage_and_exit
|
||||||
|
|| opt->cb == (void *)version_and_exit
|
||||||
|
|| opt->cb == (void *)test_daemons_and_exit) {
|
||||||
|
/* These are not important */
|
||||||
|
} else if (opt->cb == (void *)opt_set_bool) {
|
||||||
|
const bool *b = opt->u.carg;
|
||||||
|
answer = tal_fmt(name0, "%s", *b ? "true" : "false");
|
||||||
|
} else {
|
||||||
|
/* Insert more decodes here! */
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else if (opt->type & OPT_HASARG) {
|
||||||
|
if (opt->show) {
|
||||||
|
char *buf = tal_arr(name0, char, OPT_SHOW_LEN+1);
|
||||||
|
opt->show(buf, opt->u.carg);
|
||||||
|
|
||||||
|
if (streq(buf, "true") || streq(buf, "false")
|
||||||
|
|| strspn(buf, "0123456789.") == strlen(buf)) {
|
||||||
|
/* Let pure numbers and true/false through as
|
||||||
|
* literals. */
|
||||||
|
json_add_literal(response, name0,
|
||||||
|
buf, strlen(buf));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* opt_show_charp surrounds with "", strip them */
|
||||||
|
if (strstarts(buf, "\"")) {
|
||||||
|
buf[strlen(buf)-1] = '\0';
|
||||||
|
answer = buf + 1;
|
||||||
|
} else
|
||||||
|
answer = buf;
|
||||||
|
} else if (opt->cb_arg == (void *)opt_set_talstr
|
||||||
|
|| opt->cb_arg == (void *)opt_set_charp) {
|
||||||
|
const char *arg = *(char **)opt->u.carg;
|
||||||
|
if (arg)
|
||||||
|
answer = tal_fmt(name0, "%s", arg);
|
||||||
|
} else if (opt->cb_arg == (void *)opt_set_rgb) {
|
||||||
|
if (ld->rgb)
|
||||||
|
answer = tal_hexstr(name0, ld->rgb, 3);
|
||||||
|
} else if (opt->cb_arg == (void *)opt_set_alias) {
|
||||||
|
answer = (const char *)ld->alias;
|
||||||
|
} else if (opt->cb_arg == (void *)arg_log_to_file) {
|
||||||
|
answer = ld->logfile;
|
||||||
|
} else if (opt->cb_arg == (void *)opt_set_fee_rates) {
|
||||||
|
struct chain_topology *topo = ld->topology;
|
||||||
|
if (topo->override_fee_rate)
|
||||||
|
answer = tal_fmt(name0, "%u/%u/%u",
|
||||||
|
topo->override_fee_rate[0],
|
||||||
|
topo->override_fee_rate[1],
|
||||||
|
topo->override_fee_rate[2]);
|
||||||
|
} else if (opt->cb_arg == (void *)opt_add_ipaddr) {
|
||||||
|
/* This is a bit weird, we can have multiple args */
|
||||||
|
for (size_t i = 0; i < tal_count(ld->wireaddrs); i++) {
|
||||||
|
json_add_string(response,
|
||||||
|
name0,
|
||||||
|
fmt_wireaddr(name0,
|
||||||
|
ld->wireaddrs+i));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
#if DEVELOPER
|
||||||
|
} else if (strstarts(name, "dev-")) {
|
||||||
|
/* Ignore dev settings */
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
/* Insert more decodes here! */
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (answer)
|
||||||
|
json_add_string_escape(response, name0, answer);
|
||||||
|
tal_free(name0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void json_listconfigs(struct command *cmd,
|
||||||
|
const char *buffer, const jsmntok_t *params)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
struct json_result *response = new_json_result(cmd);
|
||||||
|
jsmntok_t *configtok;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (!json_get_params(buffer, params, "?config", &configtok, NULL)) {
|
||||||
|
command_fail(cmd, "Invalid arguments");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_start(response, NULL);
|
||||||
|
if (!configtok)
|
||||||
|
json_add_string(response, "# version", version());
|
||||||
|
|
||||||
|
for (i = 0; i < opt_count; i++) {
|
||||||
|
unsigned int len;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
/* FIXME: Print out comment somehow? */
|
||||||
|
if (opt_table[i].type == OPT_SUBTABLE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (name = first_name(opt_table[i].names, &len);
|
||||||
|
name;
|
||||||
|
name = next_name(name, &len)) {
|
||||||
|
/* Skips over first -, so just need to look for one */
|
||||||
|
if (name[0] != '-')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (configtok
|
||||||
|
&& !memeq(buffer + configtok->start,
|
||||||
|
configtok->end - configtok->start,
|
||||||
|
name + 1, len - 1))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
add_config(cmd->ld, response, &opt_table[i],
|
||||||
|
name+1, len-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json_object_end(response);
|
||||||
|
|
||||||
|
if (configtok && !found) {
|
||||||
|
command_fail(cmd, "Unknown config option '%.*s'",
|
||||||
|
configtok->end - configtok->start,
|
||||||
|
buffer + configtok->start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
command_success(cmd, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct json_command listconfigs_command = {
|
||||||
|
"listconfigs",
|
||||||
|
json_listconfigs,
|
||||||
|
"List all configuration options, or with [config], just that one.",
|
||||||
|
|
||||||
|
.verbose = "listconfigs [config]\n"
|
||||||
|
"Outputs an object, with each field a config options\n"
|
||||||
|
"(Option names which start with # are comments)\n"
|
||||||
|
"With [config], object only has that field"
|
||||||
|
};
|
||||||
|
AUTODATA(json_command, &listconfigs_command);
|
||||||
|
|
||||||
|
|||||||
@@ -3161,5 +3161,25 @@ class LightningDTests(BaseLightningDTests):
|
|||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
l1.daemon.wait_for_log('ONCHAIND_.*_UNILATERAL')
|
l1.daemon.wait_for_log('ONCHAIND_.*_UNILATERAL')
|
||||||
|
|
||||||
|
def test_listconfigs(self):
|
||||||
|
l1 = self.node_factory.get_node()
|
||||||
|
|
||||||
|
configs = l1.rpc.listconfigs()
|
||||||
|
# See utils.py
|
||||||
|
assert configs['bitcoin-datadir'] == bitcoind.bitcoin_dir
|
||||||
|
assert configs['lightning-dir'] == l1.daemon.lightning_dir
|
||||||
|
assert configs['port'] == l1.info['port']
|
||||||
|
assert configs['allow-deprecated-apis'] == False
|
||||||
|
assert configs['override-fee-rates'] == '15000/7500/1000'
|
||||||
|
assert configs['network'] == 'regtest'
|
||||||
|
assert configs['ignore-fee-limits'] == False
|
||||||
|
|
||||||
|
# Test one at a time.
|
||||||
|
for c in configs.keys():
|
||||||
|
if c.startswith('#'):
|
||||||
|
continue
|
||||||
|
oneconfig = l1.rpc.listconfigs(c)
|
||||||
|
assert(oneconfig[c] == configs[c])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
|
|||||||
Reference in New Issue
Block a user