jsonrpc: probe sites for usage information once, at start.

We store it in a strmap.  This means we call the jsonrpc handler earlier,
so all callers need to call param() before they do anything else; only
json_listaddrs and json_help needed fixing.

Plugins still use '[usage]' for now.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2019-02-04 21:25:42 +10:30
parent e5c80f63d7
commit 5770e0c700
6 changed files with 85 additions and 23 deletions

View File

@@ -21,6 +21,7 @@
#include <ccan/err/err.h>
#include <ccan/io/io.h>
#include <ccan/str/hex/hex.h>
#include <ccan/strmap/strmap.h>
#include <ccan/tal/str/str.h>
#include <common/bech32.h>
#include <common/json_command.h>
@@ -38,6 +39,7 @@
#include <lightningd/json.h>
#include <lightningd/jsonrpc.h>
#include <lightningd/log.h>
#include <lightningd/memdump.h>
#include <lightningd/options.h>
#include <stdio.h>
#include <sys/socket.h>
@@ -106,6 +108,10 @@ struct jsonrpc {
struct io_listener *rpc_listener;
struct json_command **commands;
struct log *log;
/* Map from json command names to usage strings: we don't put this inside
* struct json_command as it's good practice to have those const. */
STRMAP(const char *) usagemap;
};
/* The command itself usually owns the stream, because jcon may get closed.
@@ -304,10 +310,11 @@ static void json_add_help_command(struct command *cmd,
struct json_command *json_command)
{
char *usage;
cmd->mode = CMD_USAGE;
json_command->dispatch(cmd, NULL, NULL, NULL);
usage = tal_fmt(cmd, "%s %s", json_command->name, cmd->usage);
usage = tal_fmt(cmd, "%s %s",
json_command->name,
strmap_get(&cmd->ld->jsonrpc->usagemap,
json_command->name));
json_object_start(response, NULL);
json_add_string(response, "command", usage);
@@ -347,7 +354,7 @@ static struct command_result *json_help(struct command *cmd,
{
struct json_stream *response;
const jsmntok_t *cmdtok;
struct json_command **commands = cmd->ld->jsonrpc->commands;
struct json_command **commands;
const struct json_command *one_cmd;
if (!param(cmd, buffer, params,
@@ -355,6 +362,7 @@ static struct command_result *json_help(struct command *cmd,
NULL))
return command_param_failed();
commands = cmd->ld->jsonrpc->commands;
if (cmdtok) {
one_cmd = find_command(commands, buffer, cmdtok);
if (!one_cmd)
@@ -771,6 +779,7 @@ static struct io_plan *incoming_jcon_connected(struct io_conn *conn,
static void destroy_json_command(struct json_command *command, struct jsonrpc *rpc)
{
strmap_del(&rpc->usagemap, command->name, NULL);
for (size_t i = 0; i < tal_count(rpc->commands); i++) {
if (rpc->commands[i] == command) {
tal_arr_remove(&rpc->commands, i);
@@ -780,9 +789,7 @@ static void destroy_json_command(struct json_command *command, struct jsonrpc *r
abort();
}
/* For built-in ones, they're not tal objects, so no destructor */
static bool jsonrpc_command_add_perm(struct jsonrpc *rpc,
struct json_command *command)
static bool command_add(struct jsonrpc *rpc, struct json_command *command)
{
size_t count = tal_count(rpc->commands);
@@ -795,23 +802,54 @@ static bool jsonrpc_command_add_perm(struct jsonrpc *rpc,
return true;
}
bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command)
/* Built-in commands get called to construct usage string via param() */
static void setup_command_usage(struct lightningd *ld,
struct json_command *command)
{
if (!jsonrpc_command_add_perm(rpc, command))
const struct command_result *res;
struct command *dummy;
/* Call it with minimal cmd, to fill out usagemap */
dummy = tal(tmpctx, struct command);
dummy->mode = CMD_USAGE;
dummy->ld = ld;
dummy->json_cmd = command;
res = command->dispatch(dummy, NULL, NULL, NULL);
assert(res == &param_failed);
assert(strmap_get(&ld->jsonrpc->usagemap, command->name));
}
bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command,
const char *usage TAKES)
{
if (!command_add(rpc, command))
return false;
usage = tal_strdup(command, usage);
strmap_add(&rpc->usagemap, command->name, usage);
tal_add_destructor2(command, destroy_json_command, rpc);
return true;
}
static bool jsonrpc_command_add_perm(struct lightningd *ld,
struct jsonrpc *rpc,
struct json_command *command)
{
if (!command_add(rpc, command))
return false;
setup_command_usage(ld, command);
return true;
}
void jsonrpc_setup(struct lightningd *ld)
{
struct json_command **commands = get_cmdlist();
ld->jsonrpc = tal(ld, struct jsonrpc);
strmap_init(&ld->jsonrpc->usagemap);
ld->jsonrpc->commands = tal_arr(ld->jsonrpc, struct json_command *, 0);
ld->jsonrpc->log = new_log(ld->jsonrpc, ld->log_book, "jsonrpc");
for (size_t i=0; i<num_cmdlist; i++) {
if (!jsonrpc_command_add_perm(ld->jsonrpc, commands[i]))
if (!jsonrpc_command_add_perm(ld, ld->jsonrpc, commands[i]))
fatal("Cannot add duplicate command %s",
commands[i]->name);
}
@@ -823,9 +861,11 @@ bool command_usage_only(const struct command *cmd)
return cmd->mode == CMD_USAGE;
}
void command_set_usage(struct command *cmd, const char *usage)
void command_set_usage(struct command *cmd, const char *usage TAKES)
{
cmd->usage = usage;
usage = tal_strdup(cmd->ld, usage);
if (!strmap_add(&cmd->ld->jsonrpc->usagemap, cmd->json_cmd->name, usage))
fatal("Two usages for command %s?", cmd->json_cmd->name);
}
bool command_check_only(const struct command *cmd)
@@ -1141,3 +1181,11 @@ static const struct json_command check_command = {
};
AUTODATA(json_command, &check_command);
#if DEVELOPER
void jsonrpc_remove_memleak(struct htable *memtable,
const struct jsonrpc *jsonrpc)
{
memleak_remove_strmap(memtable, &jsonrpc->usagemap);
}
#endif /* DEVELOPER */