lightningd: call "shutdown" notification on plugins at shutdown.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Plugins: `shutdown` notification for clean exits.
This commit is contained in:
Rusty Russell
2021-09-03 19:46:18 +09:30
committed by Christian Decker
parent 89c3541c83
commit 1d8aecb44f
6 changed files with 107 additions and 48 deletions

View File

@@ -89,22 +89,6 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book,
return p;
}
void plugins_free(struct plugins *plugins)
{
struct plugin *p;
plugins->shutdown = true;
/* Plugins are usually the unit of allocation, and they are internally
* consistent, so let's free each plugin first. */
while (!list_empty(&plugins->plugins)) {
p = list_pop(&plugins->plugins, struct plugin, list);
tal_free(p);
}
tal_free(plugins);
}
/* Check that all the plugin's subscriptions are actually for known
* notification topics. Emit a warning if that's not the case, but
* don't kill the plugin. */
@@ -205,8 +189,12 @@ static void destroy_plugin(struct plugin *p)
/* If we are shutting down, do not continue to checking if
* the dying plugin is important. */
if (p->plugins->shutdown)
if (p->plugins->shutdown) {
/* But return if this was the last plugin! */
if (list_empty(&p->plugins->plugins))
io_break(p->plugins);
return;
}
/* Now check if the dying plugin is important. */
if (p->important) {
@@ -1958,21 +1946,36 @@ static bool plugin_subscriptions_contains(struct plugin *plugin,
return false;
}
bool plugin_single_notify(struct plugin *p,
const struct jsonrpc_notification *n TAKES)
{
bool interested;
if (plugin_subscriptions_contains(p, n->method)) {
plugin_send(p, json_stream_dup(p, n->stream, p->log));
interested = true;
} else
interested = false;
if (taken(n))
tal_free(n);
return interested;
}
void plugins_notify(struct plugins *plugins,
const struct jsonrpc_notification *n TAKES)
{
struct plugin *p;
if (taken(n))
tal_steal(tmpctx, n);
/* If we're shutting down, ld->plugins will be NULL */
if (plugins) {
list_for_each(&plugins->plugins, p, list) {
if (plugin_subscriptions_contains(p, n->method))
plugin_send(p, json_stream_dup(p, n->stream,
p->log));
plugin_single_notify(p, n);
}
}
if (taken(n))
tal_free(n);
}
static void destroy_request(struct jsonrpc_request *req,
@@ -2067,3 +2070,49 @@ bool was_plugin_destroyed(struct plugin_destroyed *pd)
tal_free(pd);
return true;
}
static void plugin_shutdown_timeout(struct lightningd *ld)
{
io_break(ld->plugins);
}
void shutdown_plugins(struct lightningd *ld)
{
struct plugin *p, *next;
/* This makes sure we don't complain about important plugins
* vanishing! */
ld->plugins->shutdown = true;
/* Tell them all to shutdown; if they care. */
list_for_each_safe(&ld->plugins->plugins, p, next, list) {
/* Kill immediately, deletes self from list. */
if (!notify_plugin_shutdown(ld, p))
tal_free(p);
}
/* If anyone was interested in shutdown, give them time. */
if (!list_empty(&ld->plugins->plugins)) {
struct oneshot *t;
/* 30 seconds should do it. */
t = new_reltimer(ld->timers, ld,
time_from_sec(30),
plugin_shutdown_timeout, ld);
io_loop_with_timers(ld);
tal_free(t);
/* Report and free remaining plugins. */
while (!list_empty(&ld->plugins->plugins)) {
p = list_pop(&ld->plugins->plugins, struct plugin, list);
log_debug(ld->log,
"%s: failed to shutdown, killing.",
p->shortname);
tal_free(p);
}
}
/* NULL stops notifications trying to access plugins. */
ld->plugins = tal_free(ld->plugins);
}