Browse Source

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.
plugin-6
Christian Decker 6 years ago
committed by Rusty Russell
parent
commit
01c7bc5884
  1. 89
      lightningd/jsonrpc.c
  2. 10
      lightningd/jsonrpc.h
  3. 4
      lightningd/lightningd.c
  4. 9
      lightningd/lightningd.h
  5. 2
      lightningd/test/run-find_my_abspath.c

89
lightningd/jsonrpc.c

@ -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);
}
/**

10
lightningd/jsonrpc.h

@ -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 */

4
lightningd/lightningd.c

@ -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);

9
lightningd/lightningd.h

@ -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;

2
lightningd/test/run-find_my_abspath.c

@ -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,

Loading…
Cancel
Save