mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-26 20:30:59 +01:00
lightningd: call shutdown plugin when we dynamic shutdown a single one.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
2063049559
commit
516c38a750
3 changed files with 95 additions and 9 deletions
|
@ -1,4 +1,5 @@
|
||||||
#include <ccan/opt/opt.h>
|
#include <ccan/opt/opt.h>
|
||||||
|
#include <lightningd/notification.h>
|
||||||
#include <lightningd/options.h>
|
#include <lightningd/options.h>
|
||||||
#include <lightningd/plugin_control.h>
|
#include <lightningd/plugin_control.h>
|
||||||
#include <lightningd/plugin_hook.h>
|
#include <lightningd/plugin_hook.h>
|
||||||
|
@ -101,11 +102,44 @@ plugin_dynamic_startdir(struct plugin_command *pcmd, const char *dir_path)
|
||||||
return command_still_pending(pcmd->cmd);
|
return command_still_pending(pcmd->cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct command_result *plugin_stop(struct command *cmd, struct plugin *p,
|
||||||
|
bool kill)
|
||||||
|
{
|
||||||
|
struct json_stream *response;
|
||||||
|
const char *stopmsg = tal_fmt(NULL, "Successfully stopped %s.",
|
||||||
|
p->shortname);
|
||||||
|
|
||||||
|
if (kill)
|
||||||
|
plugin_kill(p, LOG_INFORM, "stopped by lightningd via RPC");
|
||||||
|
|
||||||
|
response = json_stream_success(cmd);
|
||||||
|
json_add_string(response, "command", "stop");
|
||||||
|
json_add_string(response, "result", take(stopmsg));
|
||||||
|
return command_success(cmd, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If plugin stops itself, we end up here. */
|
||||||
|
static void plugin_stopped(struct plugin *p, struct command *cmd)
|
||||||
|
{
|
||||||
|
plugin_stop(cmd, p, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct plugin_stop_timeout {
|
||||||
|
struct command *cmd;
|
||||||
|
struct plugin *p;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void plugin_stop_timeout(struct plugin_stop_timeout *pst)
|
||||||
|
{
|
||||||
|
log_unusual(pst->p->log, "Timeout on shutdown: killing anyway");
|
||||||
|
tal_del_destructor2(pst->p, plugin_stopped, pst->cmd);
|
||||||
|
plugin_stop(pst->cmd, pst->p, true);
|
||||||
|
}
|
||||||
|
|
||||||
static struct command_result *
|
static struct command_result *
|
||||||
plugin_dynamic_stop(struct command *cmd, const char *plugin_name)
|
plugin_dynamic_stop(struct command *cmd, const char *plugin_name)
|
||||||
{
|
{
|
||||||
struct plugin *p;
|
struct plugin *p;
|
||||||
struct json_stream *response;
|
|
||||||
|
|
||||||
list_for_each(&cmd->ld->plugins->plugins, p, list) {
|
list_for_each(&cmd->ld->plugins->plugins, p, list) {
|
||||||
if (plugin_paths_match(p->cmd, plugin_name)) {
|
if (plugin_paths_match(p->cmd, plugin_name)) {
|
||||||
|
@ -114,14 +148,24 @@ plugin_dynamic_stop(struct command *cmd, const char *plugin_name)
|
||||||
"%s cannot be managed when "
|
"%s cannot be managed when "
|
||||||
"lightningd is up",
|
"lightningd is up",
|
||||||
plugin_name);
|
plugin_name);
|
||||||
plugin_kill(p, LOG_INFORM,
|
|
||||||
"stopped by lightningd via RPC");
|
/* If it's interested in clean shutdown, tell it. */
|
||||||
response = json_stream_success(cmd);
|
if (notify_plugin_shutdown(cmd->ld, p)) {
|
||||||
json_add_string(response, "command", "stop");
|
struct plugin_stop_timeout *pst;
|
||||||
json_add_string(response, "result",
|
|
||||||
take(tal_fmt(NULL, "Successfully stopped %s.",
|
/* Kill in 30 seconds if it doesn't exit. */
|
||||||
plugin_name)));
|
pst = tal(p, struct plugin_stop_timeout);
|
||||||
return command_success(cmd, response);
|
pst->p = p;
|
||||||
|
pst->cmd = cmd;
|
||||||
|
notleak(new_reltimer(cmd->ld->timers, pst,
|
||||||
|
time_from_sec(30),
|
||||||
|
plugin_stop_timeout,
|
||||||
|
pst));
|
||||||
|
|
||||||
|
tal_add_destructor2(p, plugin_stopped, cmd);
|
||||||
|
return command_still_pending(cmd);
|
||||||
|
}
|
||||||
|
return plugin_stop(cmd, p, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
const char *name_option;
|
const char *name_option;
|
||||||
static bool self_disable = false;
|
static bool self_disable = false;
|
||||||
|
static bool dont_shutdown = false;
|
||||||
|
|
||||||
static struct command_result *json_helloworld(struct command *cmd,
|
static struct command_result *json_helloworld(struct command *cmd,
|
||||||
const char *buf,
|
const char *buf,
|
||||||
|
@ -56,6 +57,18 @@ static void json_connected(struct command *cmd,
|
||||||
json_strdup(tmpctx, buf, idtok));
|
json_strdup(tmpctx, buf, idtok));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void json_shutdown(struct command *cmd,
|
||||||
|
const char *buf,
|
||||||
|
const jsmntok_t *params)
|
||||||
|
{
|
||||||
|
plugin_log(cmd->plugin, LOG_DBG, "shutdown called");
|
||||||
|
|
||||||
|
if (dont_shutdown)
|
||||||
|
return;
|
||||||
|
|
||||||
|
plugin_exit(cmd->plugin, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static struct command_result *testrpc_cb(struct command *cmd,
|
static struct command_result *testrpc_cb(struct command *cmd,
|
||||||
const char *buf,
|
const char *buf,
|
||||||
const jsmntok_t *params,
|
const jsmntok_t *params,
|
||||||
|
@ -137,6 +150,9 @@ static const struct plugin_hook hooks[] = { {
|
||||||
static const struct plugin_notification notifs[] = { {
|
static const struct plugin_notification notifs[] = { {
|
||||||
"connect",
|
"connect",
|
||||||
json_connected,
|
json_connected,
|
||||||
|
}, {
|
||||||
|
"shutdown",
|
||||||
|
json_shutdown
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,5 +175,9 @@ int main(int argc, char *argv[])
|
||||||
"flag",
|
"flag",
|
||||||
"Whether to disable.",
|
"Whether to disable.",
|
||||||
flag_option, &self_disable),
|
flag_option, &self_disable),
|
||||||
|
plugin_option("dont_shutdown",
|
||||||
|
"flag",
|
||||||
|
"Whether to timeout when asked to shutdown.",
|
||||||
|
flag_option, &dont_shutdown),
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2550,3 +2550,25 @@ plugin.run()
|
||||||
n.daemon.wait_for_log(r"Plugin changed, needs restart.")
|
n.daemon.wait_for_log(r"Plugin changed, needs restart.")
|
||||||
n.daemon.wait_for_log(r"test_restart_on_update 2")
|
n.daemon.wait_for_log(r"test_restart_on_update 2")
|
||||||
n.stop()
|
n.stop()
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugin_shutdown(node_factory):
|
||||||
|
"""test 'shutdown' notification"""
|
||||||
|
p = os.path.join(os.getcwd(), "tests/plugins/test_libplugin")
|
||||||
|
l1 = node_factory.get_node(options={'plugin': p})
|
||||||
|
|
||||||
|
l1.rpc.plugin_stop(p)
|
||||||
|
l1.daemon.wait_for_log(r"test_libplugin: shutdown called")
|
||||||
|
# FIXME: clean this up!
|
||||||
|
l1.daemon.wait_for_log(r"test_libplugin: Killing plugin: exited during normal operation")
|
||||||
|
|
||||||
|
# Now try timeout.
|
||||||
|
l1.rpc.plugin_start(p, dont_shutdown=True)
|
||||||
|
l1.rpc.plugin_stop(p)
|
||||||
|
l1.daemon.wait_for_log(r"test_libplugin: shutdown called")
|
||||||
|
l1.daemon.wait_for_log(r"test_libplugin: Timeout on shutdown: killing anyway")
|
||||||
|
|
||||||
|
# Now, should also shutdown on finish.
|
||||||
|
l1.rpc.plugin_start(p)
|
||||||
|
l1.rpc.stop()
|
||||||
|
l1.daemon.wait_for_log(r"test_libplugin: shutdown called")
|
||||||
|
|
Loading…
Add table
Reference in a new issue