mirror of
https://github.com/aljazceru/lightning.git
synced 2026-01-11 10:04:28 +01:00
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:
committed by
Rusty Russell
parent
0f72e0bce8
commit
01c7bc5884
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user