Browse Source

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>
fix-test_pay_direct-flake
Rusty Russell 6 years ago
parent
commit
5770e0c700
  1. 72
      lightningd/jsonrpc.c
  2. 14
      lightningd/jsonrpc.h
  3. 1
      lightningd/memdump.c
  4. 9
      lightningd/plugin.c
  5. 3
      lightningd/test/run-jsonrpc.c
  6. 9
      wallet/walletrpc.c

72
lightningd/jsonrpc.c

@ -21,6 +21,7 @@
#include <ccan/err/err.h> #include <ccan/err/err.h>
#include <ccan/io/io.h> #include <ccan/io/io.h>
#include <ccan/str/hex/hex.h> #include <ccan/str/hex/hex.h>
#include <ccan/strmap/strmap.h>
#include <ccan/tal/str/str.h> #include <ccan/tal/str/str.h>
#include <common/bech32.h> #include <common/bech32.h>
#include <common/json_command.h> #include <common/json_command.h>
@ -38,6 +39,7 @@
#include <lightningd/json.h> #include <lightningd/json.h>
#include <lightningd/jsonrpc.h> #include <lightningd/jsonrpc.h>
#include <lightningd/log.h> #include <lightningd/log.h>
#include <lightningd/memdump.h>
#include <lightningd/options.h> #include <lightningd/options.h>
#include <stdio.h> #include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -106,6 +108,10 @@ struct jsonrpc {
struct io_listener *rpc_listener; struct io_listener *rpc_listener;
struct json_command **commands; struct json_command **commands;
struct log *log; 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. /* 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) struct json_command *json_command)
{ {
char *usage; 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_object_start(response, NULL);
json_add_string(response, "command", usage); json_add_string(response, "command", usage);
@ -347,7 +354,7 @@ static struct command_result *json_help(struct command *cmd,
{ {
struct json_stream *response; struct json_stream *response;
const jsmntok_t *cmdtok; const jsmntok_t *cmdtok;
struct json_command **commands = cmd->ld->jsonrpc->commands; struct json_command **commands;
const struct json_command *one_cmd; const struct json_command *one_cmd;
if (!param(cmd, buffer, params, if (!param(cmd, buffer, params,
@ -355,6 +362,7 @@ static struct command_result *json_help(struct command *cmd,
NULL)) NULL))
return command_param_failed(); return command_param_failed();
commands = cmd->ld->jsonrpc->commands;
if (cmdtok) { if (cmdtok) {
one_cmd = find_command(commands, buffer, cmdtok); one_cmd = find_command(commands, buffer, cmdtok);
if (!one_cmd) 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) 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++) { for (size_t i = 0; i < tal_count(rpc->commands); i++) {
if (rpc->commands[i] == command) { if (rpc->commands[i] == command) {
tal_arr_remove(&rpc->commands, i); tal_arr_remove(&rpc->commands, i);
@ -780,9 +789,7 @@ static void destroy_json_command(struct json_command *command, struct jsonrpc *r
abort(); abort();
} }
/* For built-in ones, they're not tal objects, so no destructor */ static bool command_add(struct jsonrpc *rpc, struct json_command *command)
static bool jsonrpc_command_add_perm(struct jsonrpc *rpc,
struct json_command *command)
{ {
size_t count = tal_count(rpc->commands); size_t count = tal_count(rpc->commands);
@ -795,23 +802,54 @@ static bool jsonrpc_command_add_perm(struct jsonrpc *rpc,
return true; 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)
{
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 (!jsonrpc_command_add_perm(rpc, command)) if (!command_add(rpc, command))
return false; return false;
usage = tal_strdup(command, usage);
strmap_add(&rpc->usagemap, command->name, usage);
tal_add_destructor2(command, destroy_json_command, rpc); tal_add_destructor2(command, destroy_json_command, rpc);
return true; 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) void jsonrpc_setup(struct lightningd *ld)
{ {
struct json_command **commands = get_cmdlist(); struct json_command **commands = get_cmdlist();
ld->jsonrpc = tal(ld, struct jsonrpc); ld->jsonrpc = tal(ld, struct jsonrpc);
strmap_init(&ld->jsonrpc->usagemap);
ld->jsonrpc->commands = tal_arr(ld->jsonrpc, struct json_command *, 0); ld->jsonrpc->commands = tal_arr(ld->jsonrpc, struct json_command *, 0);
ld->jsonrpc->log = new_log(ld->jsonrpc, ld->log_book, "jsonrpc"); ld->jsonrpc->log = new_log(ld->jsonrpc, ld->log_book, "jsonrpc");
for (size_t i=0; i<num_cmdlist; i++) { 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", fatal("Cannot add duplicate command %s",
commands[i]->name); commands[i]->name);
} }
@ -823,9 +861,11 @@ bool command_usage_only(const struct command *cmd)
return cmd->mode == CMD_USAGE; 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) bool command_check_only(const struct command *cmd)
@ -1141,3 +1181,11 @@ static const struct json_command check_command = {
}; };
AUTODATA(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 */

14
lightningd/jsonrpc.h

@ -37,8 +37,6 @@ struct command {
bool pending; bool pending;
/* Tell param() how to process the command */ /* Tell param() how to process the command */
enum command_mode mode; enum command_mode mode;
/* This is created if mode is CMD_USAGE */
const char *usage;
/* Have we started a json stream already? For debugging. */ /* Have we started a json stream already? For debugging. */
bool have_json_stream; bool have_json_stream;
}; };
@ -175,7 +173,8 @@ void jsonrpc_listen(struct jsonrpc *rpc, struct lightningd *ld);
* *
* Free @command to remove it. * Free @command to remove it.
*/ */
bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command); bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command,
const char *usage TAKES);
/** /**
* Begin a JSON-RPC notification with the specified topic. * Begin a JSON-RPC notification with the specified topic.
@ -208,4 +207,13 @@ struct jsonrpc_request *jsonrpc_request_start_(
void jsonrpc_request_end(struct jsonrpc_request *request); void jsonrpc_request_end(struct jsonrpc_request *request);
AUTODATA_TYPE(json_command, struct json_command); AUTODATA_TYPE(json_command, struct json_command);
#if DEVELOPER
struct htable;
struct jsonrpc;
void jsonrpc_remove_memleak(struct htable *memtable,
const struct jsonrpc *jsonrpc);
#endif /* DEVELOPER */
#endif /* LIGHTNING_LIGHTNINGD_JSONRPC_H */ #endif /* LIGHTNING_LIGHTNINGD_JSONRPC_H */

1
lightningd/memdump.c

@ -154,6 +154,7 @@ static void scan_mem(struct command *cmd,
memleak_remove_htable(memtable, &ld->topology->txowatches.raw); memleak_remove_htable(memtable, &ld->topology->txowatches.raw);
memleak_remove_htable(memtable, &ld->htlcs_in.raw); memleak_remove_htable(memtable, &ld->htlcs_in.raw);
memleak_remove_htable(memtable, &ld->htlcs_out.raw); memleak_remove_htable(memtable, &ld->htlcs_out.raw);
jsonrpc_remove_memleak(memtable, ld->jsonrpc);
/* Now delete ld and those which it has pointers to. */ /* Now delete ld and those which it has pointers to. */
memleak_remove_referenced(memtable, ld); memleak_remove_referenced(memtable, ld);

9
lightningd/plugin.c

@ -610,11 +610,8 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd,
struct jsonrpc_request *req; struct jsonrpc_request *req;
char id[STR_MAX_CHARS(u64)]; char id[STR_MAX_CHARS(u64)];
if (cmd->mode == CMD_USAGE || cmd->mode == CMD_CHECK) { if (cmd->mode == CMD_CHECK)
/* FIXME! */
cmd->usage = "[params]";
return command_param_failed(); return command_param_failed();
}
plugin = find_plugin_for_command(cmd); plugin = find_plugin_for_command(cmd);
@ -675,7 +672,9 @@ static bool plugin_rpcmethod_add(struct plugin *plugin,
cmd->deprecated = false; cmd->deprecated = false;
cmd->dispatch = plugin_rpcmethod_dispatch; cmd->dispatch = plugin_rpcmethod_dispatch;
if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd)) { if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd,
/* FIXME */
"[params]")) {
log_broken(plugin->log, log_broken(plugin->log,
"Could not register method \"%s\", a method with " "Could not register method \"%s\", a method with "
"that name is already registered", "that name is already registered",

3
lightningd/test/run-jsonrpc.c

@ -40,6 +40,9 @@ void log_io(struct log *log UNNEEDED, enum log_level dir UNNEEDED, const char *c
/* Generated stub for log_prefix */ /* Generated stub for log_prefix */
const char *log_prefix(const struct log *log UNNEEDED) const char *log_prefix(const struct log *log UNNEEDED)
{ fprintf(stderr, "log_prefix called!\n"); abort(); } { fprintf(stderr, "log_prefix called!\n"); abort(); }
/* Generated stub for memleak_remove_strmap_ */
void memleak_remove_strmap_(struct htable *memtable UNNEEDED, const struct strmap *m UNNEEDED)
{ fprintf(stderr, "memleak_remove_strmap_ called!\n"); abort(); }
/* Generated stub for new_log */ /* Generated stub for new_log */
struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "new_log called!\n"); abort(); } { fprintf(stderr, "new_log called!\n"); abort(); }

9
wallet/walletrpc.c

@ -332,12 +332,15 @@ static struct command_result *json_listaddrs(struct command *cmd,
u64 *bip32_max_index; u64 *bip32_max_index;
if (!param(cmd, buffer, params, if (!param(cmd, buffer, params,
p_opt_def("bip32_max_index", param_u64, &bip32_max_index, p_opt("bip32_max_index", param_u64, &bip32_max_index),
db_get_intvar(cmd->ld->wallet->db,
"bip32_max_index", 0)),
NULL)) NULL))
return command_param_failed(); return command_param_failed();
if (!bip32_max_index) {
bip32_max_index = tal(cmd, u64);
*bip32_max_index = db_get_intvar(cmd->ld->wallet->db,
"bip32_max_index", 0);
}
response = json_stream_success(cmd); response = json_stream_success(cmd);
json_object_start(response, NULL); json_object_start(response, NULL);
json_array_start(response, "addresses"); json_array_start(response, "addresses");

Loading…
Cancel
Save