mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
plugins: make rpc_command hook chainable
Changelog-Changed: The `rpc_command` hook is now chainable.
This commit is contained in:
parent
48e91da829
commit
afaaeb3c7d
@ -665,6 +665,12 @@ struct rpc_command_hook_payload {
|
|||||||
struct command *cmd;
|
struct command *cmd;
|
||||||
const char *buffer;
|
const char *buffer;
|
||||||
const jsmntok_t *request;
|
const jsmntok_t *request;
|
||||||
|
|
||||||
|
/* custom response/replace/error options plugins can have */
|
||||||
|
const char *custom_result;
|
||||||
|
const char *custom_error;
|
||||||
|
const jsmntok_t *custom_replace;
|
||||||
|
const char *custom_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void rpc_command_hook_serialize(struct rpc_command_hook_payload *p,
|
static void rpc_command_hook_serialize(struct rpc_command_hook_payload *p,
|
||||||
@ -734,50 +740,89 @@ fail:
|
|||||||
"Bad response to 'rpc_command' hook: %s", bad));
|
"Bad response to 'rpc_command' hook: %s", bad));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void rpc_command_hook_final(struct rpc_command_hook_payload *p STEALS)
|
||||||
rpc_command_hook_callback(struct rpc_command_hook_payload *p STEALS,
|
|
||||||
const char *buffer, const jsmntok_t *resulttok)
|
|
||||||
{
|
{
|
||||||
const jsmntok_t *tok, *params, *custom_return;
|
const jsmntok_t *params;
|
||||||
const jsmntok_t *innerresulttok;
|
|
||||||
struct json_stream *response;
|
|
||||||
|
|
||||||
/* Free payload with cmd */
|
/* Free payload with cmd */
|
||||||
tal_steal(p->cmd, p);
|
tal_steal(p->cmd, p);
|
||||||
|
|
||||||
|
if (p->custom_result != NULL) {
|
||||||
|
struct json_stream *s = json_start(p->cmd);
|
||||||
|
json_add_jsonstr(s, "result", p->custom_result);
|
||||||
|
json_object_compat_end(s);
|
||||||
|
return was_pending(command_raw_complete(p->cmd, s));
|
||||||
|
}
|
||||||
|
if (p->custom_error != NULL) {
|
||||||
|
struct json_stream *s = json_start(p->cmd);
|
||||||
|
json_add_jsonstr(s, "error", p->custom_error);
|
||||||
|
json_object_compat_end(s);
|
||||||
|
return was_pending(command_raw_complete(p->cmd, s));
|
||||||
|
}
|
||||||
|
if (p->custom_replace != NULL)
|
||||||
|
return replace_command(p, p->custom_buffer, p->custom_replace);
|
||||||
|
|
||||||
|
/* If no plugin requested a change, just continue command execution. */
|
||||||
params = json_get_member(p->buffer, p->request, "params");
|
params = json_get_member(p->buffer, p->request, "params");
|
||||||
|
return was_pending(command_exec(p->cmd->jcon,
|
||||||
|
p->cmd,
|
||||||
|
p->buffer,
|
||||||
|
p->request,
|
||||||
|
params));
|
||||||
|
}
|
||||||
|
|
||||||
/* If no plugin registered, just continue command execution. Same if
|
static bool
|
||||||
* the registered plugin tells us to do so. */
|
rpc_command_hook_callback(struct rpc_command_hook_payload *p,
|
||||||
if (buffer == NULL)
|
const char *buffer, const jsmntok_t *resulttok)
|
||||||
return was_pending(command_exec(p->cmd->jcon, p->cmd, p->buffer,
|
{
|
||||||
p->request, params));
|
const struct lightningd *ld = p->cmd->ld;
|
||||||
|
const jsmntok_t *tok, *custom_return;
|
||||||
|
static char *error = "";
|
||||||
|
char *method;
|
||||||
|
|
||||||
innerresulttok = json_get_member(buffer, resulttok, "result");
|
if (!resulttok || !buffer)
|
||||||
if (innerresulttok) {
|
return true;
|
||||||
if (json_tok_streq(buffer, innerresulttok, "continue")) {
|
|
||||||
return was_pending(command_exec(p->cmd->jcon, p->cmd, p->buffer,
|
tok = json_get_member(buffer, resulttok, "result");
|
||||||
p->request, params));
|
if (tok) {
|
||||||
|
if (!json_tok_streq(buffer, tok, "continue")) {
|
||||||
|
error = "'result' should only be 'continue'.";
|
||||||
|
goto log_error_and_skip;
|
||||||
}
|
}
|
||||||
return was_pending(command_fail(p->cmd, JSONRPC2_INVALID_REQUEST,
|
/* plugin tells us to do nothing. just pass. */
|
||||||
"Bad 'result' to 'rpc_command' hook."));
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* didn't just continue but hook was already modified by prior plugin */
|
||||||
|
if (p->custom_result != NULL ||
|
||||||
|
p->custom_error != NULL ||
|
||||||
|
p->custom_replace != NULL) {
|
||||||
|
/* get method name and log error (only the first time). */
|
||||||
|
tok = json_get_member(p->buffer, p->request, "method");
|
||||||
|
method = tal_strndup(p, p->buffer + tok->start, tok->end - tok->start);
|
||||||
|
log_unusual(ld->log, "rpc_command hook '%s' already modified, ignoring.", method );
|
||||||
|
rpc_command_hook_final(p);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the registered plugin did not respond with continue,
|
/* If the registered plugin did not respond with continue,
|
||||||
* it wants either to replace the request... */
|
* it wants either to replace the request... */
|
||||||
tok = json_get_member(buffer, resulttok, "replace");
|
tok = json_get_member(buffer, resulttok, "replace");
|
||||||
if (tok)
|
if (tok) {
|
||||||
return replace_command(p, buffer, tok);
|
/* We need to make copies here, as buffer and tokens
|
||||||
|
* can be reused. */
|
||||||
|
p->custom_replace = json_tok_copy(p, tok);
|
||||||
|
p->custom_buffer = tal_dup_talarr(p, char, buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ...or return a custom JSONRPC response. */
|
/* ...or return a custom JSONRPC response. */
|
||||||
tok = json_get_member(buffer, resulttok, "return");
|
tok = json_get_member(buffer, resulttok, "return");
|
||||||
if (tok) {
|
if (tok) {
|
||||||
custom_return = json_get_member(buffer, tok, "result");
|
custom_return = json_get_member(buffer, tok, "result");
|
||||||
if (custom_return) {
|
if (custom_return) {
|
||||||
response = json_start(p->cmd);
|
p->custom_result = json_strdup(p, buffer, custom_return);
|
||||||
json_add_tok(response, "result", custom_return, buffer);
|
return true;
|
||||||
json_object_compat_end(response);
|
|
||||||
return was_pending(command_raw_complete(p->cmd, response));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_return = json_get_member(buffer, tok, "error");
|
custom_return = json_get_member(buffer, tok, "error");
|
||||||
@ -786,29 +831,32 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p STEALS,
|
|||||||
const char *errmsg;
|
const char *errmsg;
|
||||||
if (!json_to_errcode(buffer,
|
if (!json_to_errcode(buffer,
|
||||||
json_get_member(buffer, custom_return, "code"),
|
json_get_member(buffer, custom_return, "code"),
|
||||||
&code))
|
&code)) {
|
||||||
return was_pending(command_fail(p->cmd, JSONRPC2_INVALID_REQUEST,
|
error = "'error' object does not contain a code.";
|
||||||
"Bad response to 'rpc_command' hook: "
|
goto log_error_and_skip;
|
||||||
"'error' object does not contain a code."));
|
}
|
||||||
errmsg = json_strdup(tmpctx, buffer,
|
errmsg = json_strdup(tmpctx, buffer,
|
||||||
json_get_member(buffer, custom_return, "message"));
|
json_get_member(buffer, custom_return, "message"));
|
||||||
if (!errmsg)
|
if (!errmsg) {
|
||||||
return was_pending(command_fail(p->cmd, JSONRPC2_INVALID_REQUEST,
|
error = "'error' object does not contain a message.";
|
||||||
"Bad response to 'rpc_command' hook: "
|
goto log_error_and_skip;
|
||||||
"'error' object does not contain a message."));
|
}
|
||||||
response = json_stream_fail_nodata(p->cmd, code, errmsg);
|
p->custom_error = json_strdup(p, buffer, custom_return);
|
||||||
return was_pending(command_failed(p->cmd, response));
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
was_pending(command_fail(p->cmd, JSONRPC2_INVALID_REQUEST,
|
log_error_and_skip:
|
||||||
"Bad response to 'rpc_command' hook."));
|
/* Just log BROKEN errors. Give other plugins a chance. */
|
||||||
|
log_broken(ld->log, "Bad response to 'rpc_command' hook. %s", error);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_SINGLE_PLUGIN_HOOK(rpc_command,
|
REGISTER_PLUGIN_HOOK(rpc_command,
|
||||||
rpc_command_hook_callback,
|
rpc_command_hook_callback,
|
||||||
rpc_command_hook_serialize,
|
rpc_command_hook_final,
|
||||||
struct rpc_command_hook_payload *);
|
rpc_command_hook_serialize,
|
||||||
|
struct rpc_command_hook_payload *);
|
||||||
|
|
||||||
/* We return struct command_result so command_fail return value has a natural
|
/* We return struct command_result so command_fail return value has a natural
|
||||||
* sink; we don't actually use the result. */
|
* sink; we don't actually use the result. */
|
||||||
@ -884,6 +932,12 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[])
|
|||||||
rpc_hook->buffer = tal_dup_talarr(rpc_hook, char, jcon->buffer);
|
rpc_hook->buffer = tal_dup_talarr(rpc_hook, char, jcon->buffer);
|
||||||
rpc_hook->request = tal_dup_talarr(rpc_hook, jsmntok_t, tok);
|
rpc_hook->request = tal_dup_talarr(rpc_hook, jsmntok_t, tok);
|
||||||
|
|
||||||
|
/* NULL the custom_ values for the hooks */
|
||||||
|
rpc_hook->custom_result = NULL;
|
||||||
|
rpc_hook->custom_error = NULL;
|
||||||
|
rpc_hook->custom_replace = NULL;
|
||||||
|
rpc_hook->custom_buffer = NULL;
|
||||||
|
|
||||||
db_begin_transaction(jcon->ld->wallet->db);
|
db_begin_transaction(jcon->ld->wallet->db);
|
||||||
completed = plugin_hook_call_rpc_command(jcon->ld, rpc_hook);
|
completed = plugin_hook_call_rpc_command(jcon->ld, rpc_hook);
|
||||||
db_commit_transaction(jcon->ld->wallet->db);
|
db_commit_transaction(jcon->ld->wallet->db);
|
||||||
|
Loading…
Reference in New Issue
Block a user