Browse Source

lightningd: make plugin response functions return the error.

Instead of calling plugin_kill() and returning, have them
uniformly return an error string or NULL, and have the top
level (plugin_read_json) do the plugin_kill() call.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
nifty/pset-pre
Rusty Russell 5 years ago
parent
commit
852d785afb
  1. 122
      lightningd/plugin.c

122
lightningd/plugin.c

@ -228,7 +228,12 @@ static void plugin_send(struct plugin *plugin, struct json_stream *stream)
io_wake(plugin); io_wake(plugin);
} }
static void plugin_log_handle(struct plugin *plugin, const jsmntok_t *paramstok) /* Returns the error string, or NULL */
static const char *plugin_log_handle(struct plugin *plugin,
const jsmntok_t *paramstok)
WARN_UNUSED_RESULT;
static const char *plugin_log_handle(struct plugin *plugin,
const jsmntok_t *paramstok)
{ {
const jsmntok_t *msgtok, *leveltok; const jsmntok_t *msgtok, *leveltok;
enum log_level level; enum log_level level;
@ -237,9 +242,8 @@ static void plugin_log_handle(struct plugin *plugin, const jsmntok_t *paramstok)
leveltok = json_get_member(plugin->buffer, paramstok, "level"); leveltok = json_get_member(plugin->buffer, paramstok, "level");
if (!msgtok || msgtok->type != JSMN_STRING) { if (!msgtok || msgtok->type != JSMN_STRING) {
plugin_kill(plugin, "Log notification from plugin doesn't have " return tal_fmt(plugin, "Log notification from plugin doesn't have "
"a string \"message\" field"); "a string \"message\" field");
return;
} }
if (!leveltok || json_tok_streq(plugin->buffer, leveltok, "info")) if (!leveltok || json_tok_streq(plugin->buffer, leveltok, "info"))
@ -251,22 +255,27 @@ static void plugin_log_handle(struct plugin *plugin, const jsmntok_t *paramstok)
else if (json_tok_streq(plugin->buffer, leveltok, "error")) else if (json_tok_streq(plugin->buffer, leveltok, "error"))
level = LOG_BROKEN; level = LOG_BROKEN;
else { else {
plugin_kill(plugin, return tal_fmt(plugin,
"Unknown log-level %.*s, valid values are " "Unknown log-level %.*s, valid values are "
"\"debug\", \"info\", \"warn\", or \"error\".", "\"debug\", \"info\", \"warn\", or \"error\".",
json_tok_full_len(leveltok), json_tok_full_len(leveltok),
json_tok_full(plugin->buffer, leveltok)); json_tok_full(plugin->buffer, leveltok));
return;
} }
call_notifier = (level == LOG_BROKEN || level == LOG_UNUSUAL)? true : false; call_notifier = (level == LOG_BROKEN || level == LOG_UNUSUAL)? true : false;
/* FIXME: Let plugin specify node_id? */ /* FIXME: Let plugin specify node_id? */
log_(plugin->log, level, NULL, call_notifier, "%.*s", msgtok->end - msgtok->start, log_(plugin->log, level, NULL, call_notifier, "%.*s", msgtok->end - msgtok->start,
plugin->buffer + msgtok->start); plugin->buffer + msgtok->start);
return NULL;
} }
static void plugin_notification_handle(struct plugin *plugin, /* Returns the error string, or NULL */
const jsmntok_t *toks) static const char *plugin_notification_handle(struct plugin *plugin,
const jsmntok_t *toks)
WARN_UNUSED_RESULT;
static const char *plugin_notification_handle(struct plugin *plugin,
const jsmntok_t *toks)
{ {
const jsmntok_t *methtok, *paramstok; const jsmntok_t *methtok, *paramstok;
@ -274,12 +283,11 @@ static void plugin_notification_handle(struct plugin *plugin,
paramstok = json_get_member(plugin->buffer, toks, "params"); paramstok = json_get_member(plugin->buffer, toks, "params");
if (!methtok || !paramstok) { if (!methtok || !paramstok) {
plugin_kill(plugin, return tal_fmt(plugin,
"Malformed JSON-RPC notification missing " "Malformed JSON-RPC notification missing "
"\"method\" or \"params\": %.*s", "\"method\" or \"params\": %.*s",
toks->end - toks->start, toks->end - toks->start,
plugin->buffer + toks->start); plugin->buffer + toks->start);
return;
} }
/* Dispatch incoming notifications. This is currently limited /* Dispatch incoming notifications. This is currently limited
@ -287,17 +295,23 @@ static void plugin_notification_handle(struct plugin *plugin,
* unwieldy we can switch to the AUTODATA construction to * unwieldy we can switch to the AUTODATA construction to
* register notification handlers in a variety of places. */ * register notification handlers in a variety of places. */
if (json_tok_streq(plugin->buffer, methtok, "log")) { if (json_tok_streq(plugin->buffer, methtok, "log")) {
plugin_log_handle(plugin, paramstok); return plugin_log_handle(plugin, paramstok);
} else { } else {
plugin_kill(plugin, "Unknown notification method %.*s", return tal_fmt(plugin, "Unknown notification method %.*s",
json_tok_full_len(methtok), json_tok_full_len(methtok),
json_tok_full(plugin->buffer, methtok)); json_tok_full(plugin->buffer, methtok));
} }
} }
static void plugin_response_handle(struct plugin *plugin, /* Returns the error string, or NULL */
const jsmntok_t *toks, static const char *plugin_response_handle(struct plugin *plugin,
const jsmntok_t *idtok) const jsmntok_t *toks,
const jsmntok_t *idtok)
WARN_UNUSED_RESULT;
static const char *plugin_response_handle(struct plugin *plugin,
const jsmntok_t *toks,
const jsmntok_t *idtok)
{ {
struct plugin_destroyed *pd; struct plugin_destroyed *pd;
struct jsonrpc_request *request; struct jsonrpc_request *request;
@ -305,18 +319,16 @@ static void plugin_response_handle(struct plugin *plugin,
/* We only send u64 ids, so if this fails it's a critical error (note /* We only send u64 ids, so if this fails it's a critical error (note
* that this also works if id is inside a JSON string!). */ * that this also works if id is inside a JSON string!). */
if (!json_to_u64(plugin->buffer, idtok, &id)) { if (!json_to_u64(plugin->buffer, idtok, &id)) {
plugin_kill(plugin, return tal_fmt(plugin,
"JSON-RPC response \"id\"-field is not a u64"); "JSON-RPC response \"id\"-field is not a u64");
return;
} }
request = uintmap_get(&plugin->plugins->pending_requests, id); request = uintmap_get(&plugin->plugins->pending_requests, id);
if (!request) { if (!request) {
plugin_kill( return tal_fmt(
plugin, plugin,
"Received a JSON-RPC response for non-existent request"); "Received a JSON-RPC response for non-existent request");
return;
} }
/* We expect the request->cb to copy if needed */ /* We expect the request->cb to copy if needed */
@ -327,19 +339,27 @@ static void plugin_response_handle(struct plugin *plugin,
* plugin is parent), so detect that case */ * plugin is parent), so detect that case */
if (!was_plugin_destroyed(pd)) if (!was_plugin_destroyed(pd))
tal_free(request); tal_free(request);
return NULL;
} }
/** /**
* Try to parse a complete message from the plugin's buffer. * Try to parse a complete message from the plugin's buffer.
* *
* Internally calls the handler if it was able to fully parse a JSON message, * Returns NULL if there was no error.
* and returns true in that case. * If it can parse a JSON message, sets *@complete, and returns any error
* from the callback.
*
* If @destroyed was set, it means the plugin called plugin stop on itself.
*/ */
static bool plugin_read_json_one(struct plugin *plugin, bool *destroyed) static const char *plugin_read_json_one(struct plugin *plugin,
bool *complete,
bool *destroyed)
{ {
bool valid; bool valid;
const jsmntok_t *toks, *jrtok, *idtok; const jsmntok_t *toks, *jrtok, *idtok;
struct plugin_destroyed *pd; struct plugin_destroyed *pd;
const char *err;
*destroyed = false; *destroyed = false;
/* Note that in the case of 'plugin stop' this can free request (since /* Note that in the case of 'plugin stop' this can free request (since
@ -352,28 +372,31 @@ static bool plugin_read_json_one(struct plugin *plugin, bool *destroyed)
&valid); &valid);
if (!toks) { if (!toks) {
if (!valid) { if (!valid) {
plugin_kill(plugin, "Failed to parse JSON response '%.*s'", return tal_fmt(plugin,
(int)plugin->used, plugin->buffer); "Failed to parse JSON response '%.*s'",
return false; (int)plugin->used, plugin->buffer);
} }
/* We need more. */ /* We need more. */
return false; *complete = false;
return NULL;
} }
/* Empty buffer? (eg. just whitespace). */ /* Empty buffer? (eg. just whitespace). */
if (tal_count(toks) == 1) { if (tal_count(toks) == 1) {
plugin->used = 0; plugin->used = 0;
return false; /* We need more. */
*complete = false;
return NULL;
} }
*complete = true;
jrtok = json_get_member(plugin->buffer, toks, "jsonrpc"); jrtok = json_get_member(plugin->buffer, toks, "jsonrpc");
idtok = json_get_member(plugin->buffer, toks, "id"); idtok = json_get_member(plugin->buffer, toks, "id");
if (!jrtok) { if (!jrtok) {
plugin_kill( return tal_fmt(
plugin, plugin,
"JSON-RPC message does not contain \"jsonrpc\" field"); "JSON-RPC message does not contain \"jsonrpc\" field");
return false;
} }
pd = plugin_detect_destruction(plugin); pd = plugin_detect_destruction(plugin);
@ -389,7 +412,7 @@ static bool plugin_read_json_one(struct plugin *plugin, bool *destroyed)
* *
* https://www.jsonrpc.org/specification#notification * https://www.jsonrpc.org/specification#notification
*/ */
plugin_notification_handle(plugin, toks); err = plugin_notification_handle(plugin, toks);
} else { } else {
/* When a rpc call is made, the Server MUST reply with /* When a rpc call is made, the Server MUST reply with
@ -419,7 +442,7 @@ static bool plugin_read_json_one(struct plugin *plugin, bool *destroyed)
* *
* https://www.jsonrpc.org/specification#response_object * https://www.jsonrpc.org/specification#response_object
*/ */
plugin_response_handle(plugin, toks, idtok); err = plugin_response_handle(plugin, toks, idtok);
} }
/* Corner case: rpc_command hook can destroy plugin for 'plugin /* Corner case: rpc_command hook can destroy plugin for 'plugin
@ -433,7 +456,7 @@ static bool plugin_read_json_one(struct plugin *plugin, bool *destroyed)
plugin->used -= toks[0].end; plugin->used -= toks[0].end;
tal_free(toks); tal_free(toks);
} }
return true; return err;
} }
static struct io_plan *plugin_read_json(struct io_conn *conn, static struct io_plan *plugin_read_json(struct io_conn *conn,
@ -451,16 +474,17 @@ static struct io_plan *plugin_read_json(struct io_conn *conn,
/* Read and process all messages from the connection */ /* Read and process all messages from the connection */
do { do {
bool destroyed; bool destroyed;
success = plugin_read_json_one(plugin, &destroyed); const char *err;
err = plugin_read_json_one(plugin, &success, &destroyed);
/* If it's destroyed, conn is already freed! */ /* If it's destroyed, conn is already freed! */
if (destroyed) if (destroyed)
return io_close(NULL); return io_close(NULL);
/* Processing the message from the plugin might have if (err) {
* resulted in it stopping, so let's check. */ plugin_kill(plugin, "%s", err);
if (plugin->stop)
return io_close(plugin->stdout_conn); return io_close(plugin->stdout_conn);
}
} while (success); } while (success);
/* Now read more from the connection */ /* Now read more from the connection */

Loading…
Cancel
Save