Browse Source

lightningd: plugin init routines return error string or NULL.

Once again, this unifies plugin_kill() into the caller.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
nifty/pset-pre
Rusty Russell 5 years ago
parent
commit
69b07cf5a6
  1. 173
      lightningd/plugin.c
  2. 8
      lightningd/plugin.h

173
lightningd/plugin.c

@ -606,8 +606,8 @@ char *plugin_opt_set(const char *arg, struct plugin_opt *popt)
/* Add a single plugin option to the plugin as well as registering it with the /* Add a single plugin option to the plugin as well as registering it with the
* command line options. */ * command line options. */
static bool plugin_opt_add(struct plugin *plugin, const char *buffer, static const char *plugin_opt_add(struct plugin *plugin, const char *buffer,
const jsmntok_t *opt) const jsmntok_t *opt)
{ {
const jsmntok_t *nametok, *typetok, *defaulttok, *desctok; const jsmntok_t *nametok, *typetok, *defaulttok, *desctok;
struct plugin_opt *popt; struct plugin_opt *popt;
@ -617,9 +617,8 @@ static bool plugin_opt_add(struct plugin *plugin, const char *buffer,
defaulttok = json_get_member(buffer, opt, "default"); defaulttok = json_get_member(buffer, opt, "default");
if (!typetok || !nametok || !desctok) { if (!typetok || !nametok || !desctok) {
plugin_kill(plugin, return tal_fmt(plugin,
"An option is missing either \"name\", \"description\" or \"type\""); "An option is missing either \"name\", \"description\" or \"type\"");
return false;
} }
popt = tal(plugin, struct plugin_opt); popt = tal(plugin, struct plugin_opt);
@ -663,8 +662,8 @@ static bool plugin_opt_add(struct plugin *plugin, const char *buffer,
*popt->value->as_bool = false; *popt->value->as_bool = false;
} else { } else {
plugin_kill(plugin, "Only \"string\", \"int\", \"bool\", and \"flag\" options are supported"); return tal_fmt(plugin,
return false; "Only \"string\", \"int\", \"bool\", and \"flag\" options are supported");
} }
if (!defaulttok) if (!defaulttok)
popt->description = json_strdup(popt, buffer, desctok); popt->description = json_strdup(popt, buffer, desctok);
@ -678,33 +677,34 @@ static bool plugin_opt_add(struct plugin *plugin, const char *buffer,
opt_register_arg(popt->name, plugin_opt_set, NULL, popt, opt_register_arg(popt->name, plugin_opt_set, NULL, popt,
popt->description); popt->description);
return true; return NULL;
} }
/* Iterate through the options in the manifest response, and add them /* Iterate through the options in the manifest response, and add them
* to the plugin and the command line options */ * to the plugin and the command line options */
static bool plugin_opts_add(struct plugin *plugin, static const char *plugin_opts_add(struct plugin *plugin,
const char *buffer, const char *buffer,
const jsmntok_t *resulttok) const jsmntok_t *resulttok)
{ {
const jsmntok_t *options = json_get_member(buffer, resulttok, "options"); const jsmntok_t *options = json_get_member(buffer, resulttok, "options");
if (!options) { if (!options) {
plugin_kill(plugin, return tal_fmt(plugin,
"\"result.options\" was not found in the manifest"); "\"result.options\" was not found in the manifest");
return false;
} }
if (options->type != JSMN_ARRAY) { if (options->type != JSMN_ARRAY) {
plugin_kill(plugin, "\"result.options\" is not an array"); return tal_fmt(plugin, "\"result.options\" is not an array");
return false;
} }
for (size_t i = 0; i < options->size; i++) for (size_t i = 0; i < options->size; i++) {
if (!plugin_opt_add(plugin, buffer, json_get_arr(options, i))) const char *err;
return false; err = plugin_opt_add(plugin, buffer, json_get_arr(options, i));
if (err)
return err;
}
return true; return NULL;
} }
static void json_stream_forward_change_id(struct json_stream *stream, static void json_stream_forward_change_id(struct json_stream *stream,
@ -808,9 +808,9 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd,
return command_still_pending(cmd); return command_still_pending(cmd);
} }
static bool plugin_rpcmethod_add(struct plugin *plugin, static const char *plugin_rpcmethod_add(struct plugin *plugin,
const char *buffer, const char *buffer,
const jsmntok_t *meth) const jsmntok_t *meth)
{ {
const jsmntok_t *nametok, *categorytok, *desctok, *longdesctok, *usagetok; const jsmntok_t *nametok, *categorytok, *desctok, *longdesctok, *usagetok;
struct json_command *cmd; struct json_command *cmd;
@ -823,38 +823,34 @@ static bool plugin_rpcmethod_add(struct plugin *plugin,
usagetok = json_get_member(buffer, meth, "usage"); usagetok = json_get_member(buffer, meth, "usage");
if (!nametok || nametok->type != JSMN_STRING) { if (!nametok || nametok->type != JSMN_STRING) {
plugin_kill(plugin, return tal_fmt(plugin,
"rpcmethod does not have a string \"name\": %.*s", "rpcmethod does not have a string \"name\": %.*s",
meth->end - meth->start, buffer + meth->start); meth->end - meth->start, buffer + meth->start);
return false;
} }
if (!desctok || desctok->type != JSMN_STRING) { if (!desctok || desctok->type != JSMN_STRING) {
plugin_kill(plugin, return tal_fmt(plugin,
"rpcmethod does not have a string " "rpcmethod does not have a string "
"\"description\": %.*s", "\"description\": %.*s",
meth->end - meth->start, buffer + meth->start); meth->end - meth->start, buffer + meth->start);
return false;
} }
if (longdesctok && longdesctok->type != JSMN_STRING) { if (longdesctok && longdesctok->type != JSMN_STRING) {
plugin_kill(plugin, return tal_fmt(plugin,
"\"long_description\" is not a string: %.*s", "\"long_description\" is not a string: %.*s",
meth->end - meth->start, buffer + meth->start); meth->end - meth->start, buffer + meth->start);
return false;
} }
if (usagetok && usagetok->type != JSMN_STRING) { if (usagetok && usagetok->type != JSMN_STRING) {
plugin_kill(plugin, return tal_fmt(plugin,
"\"usage\" is not a string: %.*s", "\"usage\" is not a string: %.*s",
meth->end - meth->start, buffer + meth->start); meth->end - meth->start, buffer + meth->start);
return false;
} }
cmd = notleak(tal(plugin, struct json_command)); cmd = notleak(tal(plugin, struct json_command));
cmd->name = json_strdup(cmd, buffer, nametok); cmd->name = json_strdup(cmd, buffer, nametok);
if (categorytok) if (categorytok)
cmd->category = json_strdup(cmd, buffer, categorytok); cmd->category = json_strdup(cmd, buffer, categorytok);
else else
cmd->category = "plugin"; cmd->category = "plugin";
cmd->description = json_strdup(cmd, buffer, desctok); cmd->description = json_strdup(cmd, buffer, desctok);
@ -865,110 +861,107 @@ static bool plugin_rpcmethod_add(struct plugin *plugin,
if (usagetok) if (usagetok)
usage = json_strdup(tmpctx, buffer, usagetok); usage = json_strdup(tmpctx, buffer, usagetok);
else if (!deprecated_apis) { else if (!deprecated_apis) {
plugin_kill(plugin, return tal_fmt(plugin,
"\"usage\" not provided by plugin"); "\"usage\" not provided by plugin");
return false;
} else } else
usage = "[params]"; usage = "[params]";
cmd->deprecated = false; cmd->deprecated = false;
cmd->dispatch = plugin_rpcmethod_dispatch; cmd->dispatch = plugin_rpcmethod_dispatch;
if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, usage)) { if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, usage)) {
log_broken(plugin->log, return tal_fmt(plugin,
"Could not register method \"%s\", a method with " "Could not register method \"%s\", a method with "
"that name is already registered", "that name is already registered",
cmd->name); cmd->name);
return false;
} }
tal_arr_expand(&plugin->methods, cmd->name); tal_arr_expand(&plugin->methods, cmd->name);
return true; return NULL;
} }
static bool plugin_rpcmethods_add(struct plugin *plugin, static const char *plugin_rpcmethods_add(struct plugin *plugin,
const char *buffer, const char *buffer,
const jsmntok_t *resulttok) const jsmntok_t *resulttok)
{ {
const jsmntok_t *methods = const jsmntok_t *methods =
json_get_member(buffer, resulttok, "rpcmethods"); json_get_member(buffer, resulttok, "rpcmethods");
if (!methods) if (!methods)
return false; return tal_fmt(plugin, "\"result.rpcmethods\" missing");
if (methods->type != JSMN_ARRAY) { if (methods->type != JSMN_ARRAY) {
plugin_kill(plugin, return tal_fmt(plugin,
"\"result.rpcmethods\" is not an array"); "\"result.rpcmethods\" is not an array");
return false;
} }
for (size_t i = 0; i < methods->size; i++) for (size_t i = 0; i < methods->size; i++) {
if (!plugin_rpcmethod_add(plugin, buffer, const char *err;
json_get_arr(methods, i))) err = plugin_rpcmethod_add(plugin, buffer,
return false; json_get_arr(methods, i));
return true; if (err)
return err;
}
return NULL;
} }
static bool plugin_subscriptions_add(struct plugin *plugin, const char *buffer, static const char *plugin_subscriptions_add(struct plugin *plugin,
const jsmntok_t *resulttok) const char *buffer,
const jsmntok_t *resulttok)
{ {
const jsmntok_t *subscriptions = const jsmntok_t *subscriptions =
json_get_member(buffer, resulttok, "subscriptions"); json_get_member(buffer, resulttok, "subscriptions");
if (!subscriptions) { if (!subscriptions) {
plugin->subscriptions = NULL; plugin->subscriptions = NULL;
return true; return NULL;
} }
plugin->subscriptions = tal_arr(plugin, char *, 0); plugin->subscriptions = tal_arr(plugin, char *, 0);
if (subscriptions->type != JSMN_ARRAY) { if (subscriptions->type != JSMN_ARRAY) {
plugin_kill(plugin, "\"result.subscriptions\" is not an array"); return tal_fmt(plugin, "\"result.subscriptions\" is not an array");
return false;
} }
for (int i = 0; i < subscriptions->size; i++) { for (int i = 0; i < subscriptions->size; i++) {
char *topic; char *topic;
const jsmntok_t *s = json_get_arr(subscriptions, i); const jsmntok_t *s = json_get_arr(subscriptions, i);
if (s->type != JSMN_STRING) { if (s->type != JSMN_STRING) {
plugin_kill( return tal_fmt(plugin,
plugin, "result.subscriptions[%d] is not a string: '%.*s'", i,
"result.subscriptions[%d] is not a string: %s", i, json_tok_full_len(s),
plugin->buffer); json_tok_full(buffer, s));
return false;
} }
topic = json_strdup(plugin, plugin->buffer, s); topic = json_strdup(plugin, plugin->buffer, s);
if (!notifications_have_topic(topic)) { if (!notifications_have_topic(topic)) {
plugin_kill( return tal_fmt(
plugin, plugin,
"topic '%s' is not a known notification topic", topic); "topic '%s' is not a known notification topic", topic);
return false;
} }
tal_arr_expand(&plugin->subscriptions, topic); tal_arr_expand(&plugin->subscriptions, topic);
} }
return true; return NULL;
} }
static bool plugin_hooks_add(struct plugin *plugin, const char *buffer, static const char *plugin_hooks_add(struct plugin *plugin, const char *buffer,
const jsmntok_t *resulttok) const jsmntok_t *resulttok)
{ {
const jsmntok_t *hookstok = json_get_member(buffer, resulttok, "hooks"); const jsmntok_t *hookstok = json_get_member(buffer, resulttok, "hooks");
if (!hookstok) if (!hookstok)
return true; return NULL;
for (int i = 0; i < hookstok->size; i++) { for (int i = 0; i < hookstok->size; i++) {
char *name = json_strdup(NULL, plugin->buffer, char *name = json_strdup(tmpctx, plugin->buffer,
json_get_arr(hookstok, i)); json_get_arr(hookstok, i));
if (!plugin_hook_register(plugin, name)) { if (!plugin_hook_register(plugin, name)) {
plugin_kill(plugin, return tal_fmt(plugin,
"could not register hook '%s', either the " "could not register hook '%s', either the "
"name doesn't exist or another plugin " "name doesn't exist or another plugin "
"already registered it.", "already registered it.",
name); name);
tal_free(name);
return false;
} }
tal_free(name); tal_free(name);
} }
return true; return NULL;
} }
static void plugin_manifest_timeout(struct plugin *plugin) static void plugin_manifest_timeout(struct plugin *plugin)
@ -981,23 +974,25 @@ static void plugin_manifest_timeout(struct plugin *plugin)
fatal("Can't recover from plugin failure, terminating."); fatal("Can't recover from plugin failure, terminating.");
} }
bool plugin_parse_getmanifest_response(const char *buffer, static const char *plugin_parse_getmanifest_response(const char *buffer,
const jsmntok_t *toks, const jsmntok_t *toks,
const jsmntok_t *idtok, const jsmntok_t *idtok,
struct plugin *plugin) struct plugin *plugin)
{ {
const jsmntok_t *resulttok, *dynamictok, *featurestok, *tok; const jsmntok_t *resulttok, *dynamictok, *featurestok, *tok;
const char *err;
resulttok = json_get_member(buffer, toks, "result"); resulttok = json_get_member(buffer, toks, "result");
if (!resulttok || resulttok->type != JSMN_OBJECT) if (!resulttok || resulttok->type != JSMN_OBJECT)
return false; return tal_fmt(plugin, "Invalid/missing result tok in '%.*s'",
json_tok_full_len(toks),
json_tok_full(buffer, toks));
dynamictok = json_get_member(buffer, resulttok, "dynamic"); dynamictok = json_get_member(buffer, resulttok, "dynamic");
if (dynamictok && !json_to_bool(buffer, dynamictok, &plugin->dynamic)) { if (dynamictok && !json_to_bool(buffer, dynamictok, &plugin->dynamic)) {
plugin_kill(plugin, "Bad 'dynamic' field ('%.*s')", return tal_fmt(plugin, "Bad 'dynamic' field ('%.*s')",
json_tok_full_len(dynamictok), json_tok_full_len(dynamictok),
json_tok_full(buffer, dynamictok)); json_tok_full(buffer, dynamictok));
return false;
} }
featurestok = json_get_member(buffer, resulttok, "featurebits"); featurestok = json_get_member(buffer, resulttok, "featurebits");
@ -1024,40 +1019,39 @@ bool plugin_parse_getmanifest_response(const char *buffer,
have_featurebits |= tal_bytelen(fset->bits[i]) > 0; have_featurebits |= tal_bytelen(fset->bits[i]) > 0;
if (!fset->bits[i]) { if (!fset->bits[i]) {
plugin_kill( return tal_fmt(
plugin, plugin,
"Featurebits returned by plugin is not a " "Featurebits returned by plugin is not a "
"valid hexadecimal string: %.*s", "valid hexadecimal string: %.*s",
tok->end - tok->start, buffer + tok->start); tok->end - tok->start, buffer + tok->start);
return true;
} }
} }
if (plugin->dynamic && have_featurebits) { if (plugin->dynamic && have_featurebits) {
plugin_kill(plugin, return tal_fmt(plugin,
"Custom featurebits only allows for non-dynamic " "Custom featurebits only allows for non-dynamic "
"plugins: dynamic=%d, featurebits=%.*s", "plugins: dynamic=%d, featurebits=%.*s",
plugin->dynamic, plugin->dynamic,
featurestok->end - featurestok->start, featurestok->end - featurestok->start,
buffer + featurestok->start); buffer + featurestok->start);
return true;
} }
if (!feature_set_or(plugin->plugins->ld->our_features, fset)) { if (!feature_set_or(plugin->plugins->ld->our_features, fset)) {
plugin_kill(plugin, return tal_fmt(plugin,
"Custom featurebits already present"); "Custom featurebits already present");
return true;
} }
} }
if (!plugin_opts_add(plugin, buffer, resulttok) || err = plugin_opts_add(plugin, buffer, resulttok);
!plugin_rpcmethods_add(plugin, buffer, resulttok) || if (!err)
!plugin_subscriptions_add(plugin, buffer, resulttok) || err = plugin_rpcmethods_add(plugin, buffer, resulttok);
!plugin_hooks_add(plugin, buffer, resulttok)) if (!err)
return false; err = plugin_subscriptions_add(plugin, buffer, resulttok);
if (!err)
err = plugin_hooks_add(plugin, buffer, resulttok);
plugin->plugin_state = NEEDS_INIT; plugin->plugin_state = NEEDS_INIT;
return true; return err;
} }
bool plugins_any_in_state(const struct plugins *plugins, enum plugin_state state) bool plugins_any_in_state(const struct plugins *plugins, enum plugin_state state)
@ -1093,8 +1087,11 @@ static void plugin_manifest_cb(const char *buffer,
const jsmntok_t *idtok, const jsmntok_t *idtok,
struct plugin *plugin) struct plugin *plugin)
{ {
if (!plugin_parse_getmanifest_response(buffer, toks, idtok, plugin)) { const char *err;
plugin_kill(plugin, "%s: Bad response to getmanifest.", plugin->cmd); err = plugin_parse_getmanifest_response(buffer, toks, idtok, plugin);
if (err) {
plugin_kill(plugin, "%s", err);
return; return;
} }

8
lightningd/plugin.h

@ -247,14 +247,6 @@ bool plugins_any_in_state(const struct plugins *plugins, enum plugin_state state
*/ */
bool plugins_all_in_state(const struct plugins *plugins, enum plugin_state state); bool plugins_all_in_state(const struct plugins *plugins, enum plugin_state state);
/**
* Read and treat (populate options, methods, ...) the `getmanifest` response.
*/
bool plugin_parse_getmanifest_response(const char *buffer,
const jsmntok_t *toks,
const jsmntok_t *idtok,
struct plugin *plugin);
/** /**
* This populates the jsonrpc request with the plugin/lightningd specifications * This populates the jsonrpc request with the plugin/lightningd specifications
*/ */

Loading…
Cancel
Save