From dc4fb650dc0d3a8dce0ace5c66306510f6515a1a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 28 Nov 2018 12:58:18 +0100 Subject: [PATCH] plugin: Add a timeout to the `getmanifest` call If the plugin fails to respond to we may end up hanging indefinitely, so we limit the time we're willing to wait to 10 seconds. Signed-off-by: Christian Decker --- lightningd/plugin.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 31c395650..6601eae79 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,14 @@ #include #include +/* How many seconds may the plugin take to reply to the `getmanifest + * call`? This is the maximum delay to `lightningd --help` and until + * we can start the main `io_loop` to communicate with peers. If this + * hangs we can't do much, so we put an upper bound on the time we're + * willing to wait. Plugins shouldn't do any initialization in the + * `getmanifest` call anyway, that's what `init `is for. */ +#define PLUGIN_MANIFEST_TIMEOUT 10 + struct plugin { struct list_node list; @@ -43,6 +52,10 @@ struct plugin { struct list_head plugin_opts; const char **methods; + + /* Timer to add a timeout to some plugin RPC calls. Used to + * guarantee that `getmanifest` doesn't block indefinitely. */ + const struct oneshot *timeout_timer; }; struct plugin_request { @@ -73,6 +86,8 @@ struct plugins { /* RPC interface to bind JSON-RPC methods to */ struct jsonrpc *rpc; + + struct timers timers; }; struct json_output { @@ -112,6 +127,7 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book, p->log_book = log_book; p->log = new_log(p, log_book, "plugin-manager"); p->rpc = rpc; + timers_init(&p->timers, time_mono()); return p; } @@ -618,6 +634,12 @@ static bool plugin_rpcmethods_add(const struct plugin_request *req) return true; } +static void plugin_manifest_timeout(struct plugin *plugin) +{ + log_broken(plugin->log, "The plugin failed to respond to \"getmanifest\" in time, terminating."); + fatal("Can't recover from plugin failure, terminating."); +} + /** * Callback for the plugin_manifest request. */ @@ -637,6 +659,8 @@ static void plugin_manifest_cb(const struct plugin_request *req, struct plugin * if (!plugin_opts_add(req) || !plugin_rpcmethods_add(req)) plugin_kill(plugin, "Failed to register options or methods"); + /* Reset timer, it'd kill us otherwise. */ + tal_free(plugin->timeout_timer); } /* If this is a valid plugin return full path name, otherwise NULL */ @@ -710,6 +734,7 @@ void plugins_init(struct plugins *plugins) struct plugin *p; char **cmd; int stdin, stdout; + struct timer *expired; plugins->pending_manifests = 0; uintmap_init(&plugins->pending_requests); @@ -735,10 +760,19 @@ void plugins_init(struct plugins *plugins) io_new_conn(p, stdin, plugin_stdin_conn_init, p); plugin_request_send(p, "getmanifest", "[]", plugin_manifest_cb, p); plugins->pending_manifests++; + p->timeout_timer = new_reltimer( + &plugins->timers, p, time_from_sec(PLUGIN_MANIFEST_TIMEOUT), + plugin_manifest_timeout, p); tal_free(cmd); } - if (plugins->pending_manifests > 0) - io_loop(NULL, NULL); + + while (plugins->pending_manifests > 0) { + void *v = io_loop(&plugins->timers, &expired); + if (v == plugins) + break; + if (expired) + timer_expired(plugins, expired); + } } static void plugin_config_cb(const struct plugin_request *req,