jsonrpc: Make an explicit jsonrpc struct

This wraps the listener, a separate log and the registered
commands. This is mainly needed once we dynamically add
sjson_command`s to the JSON-RPC.
This commit is contained in:
Christian Decker
2018-11-14 08:12:03 +01:00
committed by Rusty Russell
parent 0f72e0bce8
commit 01c7bc5884
5 changed files with 82 additions and 32 deletions

View File

@@ -77,6 +77,18 @@ struct json_connection {
struct json_stream **js_arr;
};
/**
* `jsonrpc` encapsulates the entire state of the JSON-RPC interface,
* including a list of methods that the interface supports (can be
* appended dynamically, e.g., for plugins, and logs. It also serves
* as a convenient `tal`-parent for all JSON-RPC related allocations.
*/
struct jsonrpc {
struct io_listener *rpc_listener;
struct json_command **commands;
struct log *log;
};
/* The command itself usually owns the stream, because jcon may get closed.
* The command transfers ownership once it's done though. */
static struct json_stream *jcon_new_json_stream(const tal_t *ctx,
@@ -292,10 +304,9 @@ static void json_add_help_command(struct command *cmd,
static void json_help(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
unsigned int i;
struct json_stream *response;
struct json_command **cmdlist = get_cmdlist();
const jsmntok_t *cmdtok;
struct json_command **commands = cmd->ld->jsonrpc->commands;
if (!param(cmd, buffer, params,
p_opt("command", json_tok_tok, &cmdtok),
@@ -303,10 +314,10 @@ static void json_help(struct command *cmd,
return;
if (cmdtok) {
for (i = 0; i < num_cmdlist; i++) {
if (json_tok_streq(buffer, cmdtok, cmdlist[i]->name)) {
for (size_t i = 0; i < tal_count(commands); i++) {
if (json_tok_streq(buffer, cmdtok, commands[i]->name)) {
response = json_stream_success(cmd);
json_add_help_command(cmd, response, cmdlist[i]);
json_add_help_command(cmd, response, commands[i]);
goto done;
}
}
@@ -320,8 +331,8 @@ static void json_help(struct command *cmd,
response = json_stream_success(cmd);
json_object_start(response, NULL);
json_array_start(response, "help");
for (i = 0; i < num_cmdlist; i++) {
json_add_help_command(cmd, response, cmdlist[i]);
for (size_t i=0; i<tal_count(commands); i++) {
json_add_help_command(cmd, response, commands[i]);
}
json_array_end(response);
json_object_end(response);
@@ -330,17 +341,17 @@ done:
command_success(cmd, response);
}
static const struct json_command *find_cmd(const char *buffer,
static const struct json_command *find_cmd(const struct jsonrpc *rpc,
const char *buffer,
const jsmntok_t *tok)
{
unsigned int i;
struct json_command **cmdlist = get_cmdlist();
struct json_command **commands = rpc->commands;
/* cmdlist[i]->name can be NULL in test code. */
for (i = 0; i < num_cmdlist; i++)
if (cmdlist[i]->name
&& json_tok_streq(buffer, tok, cmdlist[i]->name))
return cmdlist[i];
/* commands[i]->name can be NULL in test code. */
for (size_t i=0; i<tal_count(commands); i++)
if (commands[i]->name &&
json_tok_streq(buffer, tok, commands[i]->name))
return commands[i];
return NULL;
}
@@ -516,9 +527,9 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[])
return;
}
/* This is a convenient tal parent for duration of command
* (which may outlive the conn!). */
c = tal(jcon->ld->rpc_listener, struct command);
/* Allocate the command off of the `jsonrpc` object and not
* the connection since the command may outlive `conn`. */
c = tal(jcon->ld->jsonrpc, struct command);
c->jcon = jcon;
c->ld = jcon->ld;
c->pending = false;
@@ -543,8 +554,8 @@ static void parse_request(struct json_connection *jcon, const jsmntok_t tok[])
return;
}
c->json_cmd = find_cmd(jcon->buffer, method);
if (!c->json_cmd) {
c->json_cmd = find_cmd(jcon->ld->jsonrpc, jcon->buffer, method);
if (!c->json_cmd) {
command_fail(c, JSONRPC2_METHOD_NOT_FOUND,
"Unknown command '%.*s'",
method->end - method->start,
@@ -713,10 +724,41 @@ static struct io_plan *incoming_jcon_connected(struct io_conn *conn,
return jcon_connected(notleak(conn), ld);
}
void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename)
bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command)
{
size_t count = tal_count(rpc->commands);
/* Check that we don't clobber a method */
for (size_t i = 0; i < count; i++)
if (rpc->commands[i] != NULL &&
streq(rpc->commands[i]->name, command->name))
return false;
*tal_arr_expand(&rpc->commands) = command;
return true;
}
static struct jsonrpc *jsonrpc_new(const tal_t *ctx, struct lightningd *ld, int fd)
{
struct jsonrpc *jsonrpc = tal(ctx, struct jsonrpc);
struct json_command **commands = get_cmdlist();
jsonrpc->commands = tal_arr(jsonrpc, struct json_command *, 0);
jsonrpc->log = new_log(jsonrpc, ld->log_book, "jsonrpc");
for (size_t i=0; i<num_cmdlist; i++) {
jsonrpc_command_add(jsonrpc, commands[i]);
}
jsonrpc->rpc_listener = io_new_listener(
ld->rpc_filename, fd, incoming_jcon_connected, ld);
log_debug(jsonrpc->log, "Listening on '%s'", ld->rpc_filename);
return jsonrpc;
}
struct jsonrpc *setup_jsonrpc(struct lightningd *ld)
{
struct sockaddr_un addr;
int fd, old_umask;
const char *rpc_filename = ld->rpc_filename;
if (streq(rpc_filename, "/dev/tty")) {
fd = open(rpc_filename, O_RDWR);
@@ -724,7 +766,7 @@ void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename)
err(1, "Opening %s", rpc_filename);
/* Technically this is a leak, but there's only one */
notleak(io_new_conn(ld, fd, jcon_connected, ld));
return;
return jsonrpc_new(ld, ld, fd);
}
fd = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -750,8 +792,7 @@ void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename)
if (listen(fd, 1) != 0)
err(1, "Listening on '%s'", rpc_filename);
log_debug(ld->log, "Listening on '%s'", rpc_filename);
ld->rpc_listener = io_new_listener(ld->rpc_filename, fd, incoming_jcon_connected, ld);
return jsonrpc_new(ld, ld, fd);
}
/**

View File

@@ -93,7 +93,15 @@ void PRINTF_FMT(3, 4) command_fail(struct command *cmd, int code,
void command_still_pending(struct command *cmd);
/* For initialization */
void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename);
struct jsonrpc *setup_jsonrpc(struct lightningd *ld);
/**
* Add a new command/method to the JSON-RPC interface.
*
* Returns true if the command was added correctly, false if adding
* this would clobber a command name.
*/
bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command);
AUTODATA_TYPE(json_command, struct json_command);
#endif /* LIGHTNING_LIGHTNINGD_JSONRPC_H */

View File

@@ -695,7 +695,7 @@ int main(int argc, char *argv[])
/*~ Create RPC socket: now lightning-cli can send us JSON RPC commands
* over a UNIX domain socket specified by `ld->rpc_filename`. */
setup_jsonrpc(ld, ld->rpc_filename);
ld->jsonrpc = setup_jsonrpc(ld);
/*~ We defer --daemon until we've completed most initialization: that
* way we'll exit with an error rather than silently exiting 0, then
@@ -778,7 +778,7 @@ int main(int argc, char *argv[])
* it might actually be touching the DB in some destructors, e.g.,
* unreserving UTXOs (see #1737) */
db_begin_transaction(ld->wallet->db);
tal_free(ld->rpc_listener);
tal_free(ld->jsonrpc);
db_commit_transaction(ld->wallet->db);
remove(ld->pidfile);

View File

@@ -84,10 +84,11 @@ struct lightningd {
/* Location of the RPC socket. */
char *rpc_filename;
/* The listener for the RPC socket. Can be shut down separately from the
* rest of the daemon to allow a clean shutdown, which frees all pending
* cmds in a DB transaction. */
struct io_listener *rpc_listener;
/* The root of the jsonrpc interface. Can be shut down
* separately from the rest of the daemon to allow a clean
* shutdown, which frees all pending cmds in a DB
* transaction. */
struct jsonrpc *jsonrpc;
/* Configuration file name */
char *config_filename;

View File

@@ -138,7 +138,7 @@ void register_opts(struct lightningd *ld UNNEEDED)
void setup_color_and_alias(struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "setup_color_and_alias called!\n"); abort(); }
/* Generated stub for setup_jsonrpc */
void setup_jsonrpc(struct lightningd *ld UNNEEDED, const char *rpc_filename UNNEEDED)
struct jsonrpc *setup_jsonrpc(struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "setup_jsonrpc called!\n"); abort(); }
/* Generated stub for setup_topology */
void setup_topology(struct chain_topology *topology UNNEEDED, struct timers *timers UNNEEDED,