diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 72df06d88..3cce8a3c4 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -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 == ¶m_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; ijsonrpc, 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 */ diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 28141baac..55d3dbf66 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -37,8 +37,6 @@ struct command { bool pending; /* Tell param() how to process the command */ enum command_mode mode; - /* This is created if mode is CMD_USAGE */ - const char *usage; /* Have we started a json stream already? For debugging. */ bool have_json_stream; }; @@ -175,7 +173,8 @@ void jsonrpc_listen(struct jsonrpc *rpc, struct lightningd *ld); * * 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. @@ -208,4 +207,13 @@ struct jsonrpc_request *jsonrpc_request_start_( void jsonrpc_request_end(struct jsonrpc_request *request); 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 */ diff --git a/lightningd/memdump.c b/lightningd/memdump.c index a7901d251..9c6642ae4 100644 --- a/lightningd/memdump.c +++ b/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->htlcs_in.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. */ memleak_remove_referenced(memtable, ld); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index e3a1a9f5b..30343abbc 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -610,11 +610,8 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, struct jsonrpc_request *req; char id[STR_MAX_CHARS(u64)]; - if (cmd->mode == CMD_USAGE || cmd->mode == CMD_CHECK) { - /* FIXME! */ - cmd->usage = "[params]"; + if (cmd->mode == CMD_CHECK) return command_param_failed(); - } plugin = find_plugin_for_command(cmd); @@ -675,7 +672,9 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, cmd->deprecated = false; 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, "Could not register method \"%s\", a method with " "that name is already registered", diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 1d35b022b..876774c3b 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/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 */ const char *log_prefix(const struct log *log UNNEEDED) { 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 */ 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(); } diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 06441d0c0..9f1209181 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -332,12 +332,15 @@ static struct command_result *json_listaddrs(struct command *cmd, u64 *bip32_max_index; if (!param(cmd, buffer, params, - p_opt_def("bip32_max_index", param_u64, &bip32_max_index, - db_get_intvar(cmd->ld->wallet->db, - "bip32_max_index", 0)), + p_opt("bip32_max_index", param_u64, &bip32_max_index), NULL)) 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); json_object_start(response, NULL); json_array_start(response, "addresses");