-
Notifications
You must be signed in to change notification settings - Fork 912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: chain hook rpc command #4384
feat: chain hook rpc command #4384
Conversation
0bcc8ac
to
913b122
Compare
Looking into the CI run with valgrind it seems like the field is not freed, otherwise valgrind would have complained about us asserting on a value that has been previously freed, so we'll need to look into this a bit further. |
Diff of partial fix. Basically, json_start() is linked only to the specific command, you need to be more primitive. diff --git a/common/json_stream.c b/common/json_stream.c
index 45ac30ebd..20af11a29 100644
--- a/common/json_stream.c
+++ b/common/json_stream.c
@@ -194,8 +194,9 @@ void json_add_jsonstr(struct json_stream *js,
size_t len = strlen(jsonstr);
p = json_member_direct(js, fieldname, len);
-
- memcpy(p, jsonstr, len);
+ /* Could be OOM! */
+ if (p)
+ memcpy(p, jsonstr, len);
}
/* This is where we read the json_stream and write it to conn */
diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c
index 2bd96294f..75d2c4463 100644
--- a/lightningd/jsonrpc.c
+++ b/lightningd/jsonrpc.c
@@ -666,9 +666,9 @@ struct rpc_command_hook_payload {
const char *buffer;
const jsmntok_t *request;
- /* custom repsonse/replace/error options plugins can have */
- struct json_stream *custom_result;
- struct json_stream *custom_error;
+ /* 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;
};
@@ -747,10 +747,18 @@ static void rpc_command_hook_final(struct rpc_command_hook_payload *p STEALS)
/* Free payload with cmd */
tal_steal(p->cmd, p);
- if (p->custom_result != NULL)
- return was_pending(command_raw_complete(p->cmd, p->custom_result));
- if (p->custom_error != NULL)
- return was_pending(command_failed(p->cmd, p->custom_error));
+ 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);
@@ -769,7 +777,6 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
{
const struct lightningd *ld = p->cmd->ld;
const jsmntok_t *tok, *custom_return;
- struct json_stream *response;
static char *error = "";
char *method;
@@ -812,10 +819,7 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
if (tok) {
custom_return = json_get_member(buffer, tok, "result");
if (custom_return) {
- response = json_start(p->cmd);
- json_add_tok(response, "result", custom_return, buffer);
- json_object_compat_end(response);
- p->custom_result = response;
+ p->custom_result = json_strdup(p, buffer, custom_return);
return true;
}
@@ -835,8 +839,7 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
error = "'error' object does not contain a message.";
goto log_error_and_skip;
}
- response = json_stream_fail_nodata(p->cmd, code, errmsg);
- p->custom_error = response;
+ p->custom_error = json_strdup(p, buffer, custom_return);
return true;
}
} |
OK, this fixes it For Real: diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c
index 2bd96294f..c445e7b80 100644
--- a/lightningd/jsonrpc.c
+++ b/lightningd/jsonrpc.c
@@ -666,9 +666,9 @@ struct rpc_command_hook_payload {
const char *buffer;
const jsmntok_t *request;
- /* custom repsonse/replace/error options plugins can have */
- struct json_stream *custom_result;
- struct json_stream *custom_error;
+ /* 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;
};
@@ -747,10 +747,18 @@ static void rpc_command_hook_final(struct rpc_command_hook_payload *p STEALS)
/* Free payload with cmd */
tal_steal(p->cmd, p);
- if (p->custom_result != NULL)
- return was_pending(command_raw_complete(p->cmd, p->custom_result));
- if (p->custom_error != NULL)
- return was_pending(command_failed(p->cmd, p->custom_error));
+ 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);
@@ -769,7 +777,6 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
{
const struct lightningd *ld = p->cmd->ld;
const jsmntok_t *tok, *custom_return;
- struct json_stream *response;
static char *error = "";
char *method;
@@ -802,8 +809,10 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
* it wants either to replace the request... */
tok = json_get_member(buffer, resulttok, "replace");
if (tok) {
- p->custom_replace = tok;
- p->custom_buffer = buffer;
+ /* 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_strdup(p, buffer);
return true;
}
@@ -812,10 +821,7 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
if (tok) {
custom_return = json_get_member(buffer, tok, "result");
if (custom_return) {
- response = json_start(p->cmd);
- json_add_tok(response, "result", custom_return, buffer);
- json_object_compat_end(response);
- p->custom_result = response;
+ p->custom_result = json_strdup(p, buffer, custom_return);
return true;
}
@@ -835,8 +841,7 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p,
error = "'error' object does not contain a message.";
goto log_error_and_skip;
}
- response = json_stream_fail_nodata(p->cmd, code, errmsg);
- p->custom_error = response;
+ p->custom_error = json_strdup(p, buffer, custom_return);
return true;
}
} |
913b122
to
66de2aa
Compare
Thanks @rustyrussell for the investigation! I applied it and everything seems to work. Lets hope CI tests will be green... I merged in your patch as mine but added your OOM as your extra commit ;) |
We have got a valgrind finding that complains about the
|
Ah, buffer is not a nul-terminated string, ofc! Fix is to use tal_dup_talarr() instead... |
Changelog-Changed: The `rpc_command` hook is now chainable.
bfc5917
to
fec9aa1
Compare
Thanks, I squashed that in... |
Ack fec9aa1 |
This chains the last remaining single hook
rpc_command
Adds: feature, doc and modified testcase.
This is in draft as there is an open issue that will crash the test that I don't fully understand yet.
It seems like
rpc_command_hook_payload *p
or at leastp->custom_result
is freed withtal_free()
somewhere,so that we cant read the JSON
p->custom_result
(viacommand_raw_complete
) anymore and give it to the daemon.If we modify the code so that
rpc_command_hook_final
is explicitly called in therpc_command_hook_callback
, we have a similar situation where it then wants total_free()
therpc_command_hook_payload
which is already freed.Ideally I want to call
rpc_command_hook_final
just implicitly (just like the current code does) in order to detect if two or multiple plugins try to handle the samerpc_command
.@cdecker @rustyrussell some advice could be helpful.