mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
lightningd/plugin.c: Add important plugins, which if they terminate, lightningd also terminates.
Changelog-Added: New option `--important-plugin` loads a plugin is so important that if it dies, `lightningd` will exit rather than continue. You can still `--disable-plugin` it, however, which trumps `--important-plugin` and it will not be started at all.
This commit is contained in:
committed by
neil saitug
parent
50600dce95
commit
a847487bbe
@@ -534,10 +534,11 @@ add multiple directories\.
|
|||||||
|
|
||||||
|
|
||||||
\fBclear-plugins\fR
|
\fBclear-plugins\fR
|
||||||
This option clears all \fIplugin\fR and \fIplugin-dir\fR options preceeding it,
|
This option clears all \fIplugin\fR, \fIimportant-plugin\fR, and \fIplugin-dir\fR options
|
||||||
|
preceeding it,
|
||||||
including the default built-in plugin directory\. You can still add
|
including the default built-in plugin directory\. You can still add
|
||||||
\fIplugin-dir\fR and \fIplugin\fR options following this and they will have the
|
\fIplugin-dir\fR, \fIplugin\fR, and \fIimportant-plugin\fR options following this
|
||||||
normal effect\.
|
and they will have the normal effect\.
|
||||||
|
|
||||||
|
|
||||||
\fBdisable-plugin\fR=\fIPLUGIN\fR
|
\fBdisable-plugin\fR=\fIPLUGIN\fR
|
||||||
@@ -547,6 +548,16 @@ be loaded at startup, whatever directory it is in\. This option is useful for
|
|||||||
disabling a single plugin inside a directory\. You can still explicitly
|
disabling a single plugin inside a directory\. You can still explicitly
|
||||||
load plugins which have been disabled, using \fBlightning-plugin\fR(7) \fBstart\fR\.
|
load plugins which have been disabled, using \fBlightning-plugin\fR(7) \fBstart\fR\.
|
||||||
|
|
||||||
|
|
||||||
|
\fBimportant-plugin\fR=\fIPLUGIN\fR
|
||||||
|
Speciy a plugin to run as part of C-lightning\.
|
||||||
|
This can be specified multiple times to add multiple plugins\.
|
||||||
|
Plugins specified via this option are considered so important, that if the
|
||||||
|
plugin stops for any reason (including via \fBlightning-plugin\fR(7) \fBstop\fR),
|
||||||
|
C-lightning will also stop running\.
|
||||||
|
This way, you can monitor crashes of important plugins by simply monitoring
|
||||||
|
if C-lightning terminates\.
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
|
|
||||||
You should report bugs on our github issues page, and maybe submit a fix
|
You should report bugs on our github issues page, and maybe submit a fix
|
||||||
|
|||||||
@@ -439,10 +439,11 @@ loaded. *DIRECTORY* must exist; this can be specified multiple times to
|
|||||||
add multiple directories.
|
add multiple directories.
|
||||||
|
|
||||||
**clear-plugins**
|
**clear-plugins**
|
||||||
This option clears all *plugin* and *plugin-dir* options preceeding it,
|
This option clears all *plugin*, *important-plugin*, and *plugin-dir* options
|
||||||
|
preceeding it,
|
||||||
including the default built-in plugin directory. You can still add
|
including the default built-in plugin directory. You can still add
|
||||||
*plugin-dir* and *plugin* options following this and they will have the
|
*plugin-dir*, *plugin*, and *important-plugin* options following this
|
||||||
normal effect.
|
and they will have the normal effect.
|
||||||
|
|
||||||
**disable-plugin**=*PLUGIN*
|
**disable-plugin**=*PLUGIN*
|
||||||
If *PLUGIN* contains a /, plugins with the same path as *PLUGIN* will
|
If *PLUGIN* contains a /, plugins with the same path as *PLUGIN* will
|
||||||
@@ -451,6 +452,15 @@ be loaded at startup, whatever directory it is in. This option is useful for
|
|||||||
disabling a single plugin inside a directory. You can still explicitly
|
disabling a single plugin inside a directory. You can still explicitly
|
||||||
load plugins which have been disabled, using lightning-plugin(7) `start`.
|
load plugins which have been disabled, using lightning-plugin(7) `start`.
|
||||||
|
|
||||||
|
**important-plugin**=*PLUGIN*
|
||||||
|
Speciy a plugin to run as part of C-lightning.
|
||||||
|
This can be specified multiple times to add multiple plugins.
|
||||||
|
Plugins specified via this option are considered so important, that if the
|
||||||
|
plugin stops for any reason (including via lightning-plugin(7) `stop`),
|
||||||
|
C-lightning will also stop running.
|
||||||
|
This way, you can monitor crashes of important plugins by simply monitoring
|
||||||
|
if C-lightning terminates.
|
||||||
|
|
||||||
BUGS
|
BUGS
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ static char *opt_add_plugin(const char *arg, struct lightningd *ld)
|
|||||||
log_info(ld->log, "%s: disabled via disable-plugin", arg);
|
log_info(ld->log, "%s: disabled via disable-plugin", arg);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
plugin_register(ld->plugins, arg, NULL);
|
plugin_register(ld->plugins, arg, NULL, false);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +369,16 @@ static char *opt_clear_plugins(struct lightningd *ld)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *opt_important_plugin(const char *arg, struct lightningd *ld)
|
||||||
|
{
|
||||||
|
if (plugin_blacklisted(ld->plugins, arg)) {
|
||||||
|
log_info(ld->log, "%s: disabled via disable-plugin", arg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
plugin_register(ld->plugins, arg, NULL, true);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prompt the user to enter a password, from which will be derived the key used
|
/* Prompt the user to enter a password, from which will be derived the key used
|
||||||
* for `hsm_secret` encryption.
|
* for `hsm_secret` encryption.
|
||||||
* The algorithm used to derive the key is Argon2(id), to which libsodium
|
* The algorithm used to derive the key is Argon2(id), to which libsodium
|
||||||
@@ -771,6 +781,10 @@ static void register_opts(struct lightningd *ld)
|
|||||||
NULL, ld,
|
NULL, ld,
|
||||||
"Disable a particular plugin by filename/name");
|
"Disable a particular plugin by filename/name");
|
||||||
|
|
||||||
|
opt_register_early_arg("--important-plugin", opt_important_plugin,
|
||||||
|
NULL, ld,
|
||||||
|
"Add an important plugin to be run (can be used multiple times). Die if the plugin dies.");
|
||||||
|
|
||||||
/* Early, as it suppresses DNS lookups from cmdline too. */
|
/* Early, as it suppresses DNS lookups from cmdline too. */
|
||||||
opt_register_early_arg("--always-use-proxy",
|
opt_register_early_arg("--always-use-proxy",
|
||||||
opt_set_bool_arg, opt_show_bool,
|
opt_set_bool_arg, opt_show_bool,
|
||||||
@@ -1278,6 +1292,9 @@ static void add_config(struct lightningd *ld,
|
|||||||
json_add_opt_log_levels(response, ld->log);
|
json_add_opt_log_levels(response, ld->log);
|
||||||
} else if (opt->cb_arg == (void *)opt_disable_plugin) {
|
} else if (opt->cb_arg == (void *)opt_disable_plugin) {
|
||||||
json_add_opt_disable_plugins(response, ld->plugins);
|
json_add_opt_disable_plugins(response, ld->plugins);
|
||||||
|
} else if (opt->cb_arg == (void *)opt_important_plugin) {
|
||||||
|
/* Do nothing, this is already handled by
|
||||||
|
* opt_add_plugin. */
|
||||||
} else if (opt->cb_arg == (void *)opt_add_plugin_dir
|
} else if (opt->cb_arg == (void *)opt_add_plugin_dir
|
||||||
|| opt->cb_arg == (void *)plugin_opt_set
|
|| opt->cb_arg == (void *)plugin_opt_set
|
||||||
|| opt->cb_arg == (void *)plugin_opt_flag_set) {
|
|| opt->cb_arg == (void *)plugin_opt_flag_set) {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book,
|
|||||||
p->startup = true;
|
p->startup = true;
|
||||||
p->json_cmds = tal_arr(p, struct command *, 0);
|
p->json_cmds = tal_arr(p, struct command *, 0);
|
||||||
p->blacklist = tal_arr(p, const char *, 0);
|
p->blacklist = tal_arr(p, const char *, 0);
|
||||||
|
p->shutdown = false;
|
||||||
uintmap_init(&p->pending_requests);
|
uintmap_init(&p->pending_requests);
|
||||||
memleak_add_helper(p, memleak_help_pending_requests);
|
memleak_add_helper(p, memleak_help_pending_requests);
|
||||||
|
|
||||||
@@ -62,6 +63,9 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book,
|
|||||||
void plugins_free(struct plugins *plugins)
|
void plugins_free(struct plugins *plugins)
|
||||||
{
|
{
|
||||||
struct plugin *p;
|
struct plugin *p;
|
||||||
|
|
||||||
|
plugins->shutdown = true;
|
||||||
|
|
||||||
/* Plugins are usually the unit of allocation, and they are internally
|
/* Plugins are usually the unit of allocation, and they are internally
|
||||||
* consistent, so let's free each plugin first. */
|
* consistent, so let's free each plugin first. */
|
||||||
while (!list_empty(&plugins->plugins)) {
|
while (!list_empty(&plugins->plugins)) {
|
||||||
@@ -105,6 +109,7 @@ struct command_result *plugin_register_all_complete(struct lightningd *ld,
|
|||||||
static void destroy_plugin(struct plugin *p)
|
static void destroy_plugin(struct plugin *p)
|
||||||
{
|
{
|
||||||
struct plugin_rpccall *call;
|
struct plugin_rpccall *call;
|
||||||
|
|
||||||
plugin_hook_unregister_all(p);
|
plugin_hook_unregister_all(p);
|
||||||
list_del(&p->list);
|
list_del(&p->list);
|
||||||
|
|
||||||
@@ -118,10 +123,23 @@ static void destroy_plugin(struct plugin *p)
|
|||||||
/* Don't call this if we're still parsing options! */
|
/* Don't call this if we're still parsing options! */
|
||||||
if (p->plugin_state != UNCONFIGURED)
|
if (p->plugin_state != UNCONFIGURED)
|
||||||
check_plugins_resolved(p->plugins);
|
check_plugins_resolved(p->plugins);
|
||||||
|
|
||||||
|
/* If we are shutting down, do not continue to checking if
|
||||||
|
* the dying plugin is important. */
|
||||||
|
if (p->plugins->shutdown)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Now check if the dying plugin is important. */
|
||||||
|
if (p->important) {
|
||||||
|
log_broken(p->log,
|
||||||
|
"Plugin marked as important, "
|
||||||
|
"shutting down lightningd!");
|
||||||
|
lightningd_exit(p->plugins->ld, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES,
|
struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES,
|
||||||
struct command *start_cmd)
|
struct command *start_cmd, bool important)
|
||||||
{
|
{
|
||||||
struct plugin *p, *p_temp;
|
struct plugin *p, *p_temp;
|
||||||
|
|
||||||
@@ -130,6 +148,9 @@ struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES,
|
|||||||
if (streq(path, p_temp->cmd)) {
|
if (streq(path, p_temp->cmd)) {
|
||||||
if (taken(path))
|
if (taken(path))
|
||||||
tal_free(path);
|
tal_free(path);
|
||||||
|
/* If added as "important", upgrade to "important". */
|
||||||
|
if (important)
|
||||||
|
p_temp->important = true;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +174,8 @@ struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES,
|
|||||||
list_add_tail(&plugins->plugins, &p->list);
|
list_add_tail(&plugins->plugins, &p->list);
|
||||||
tal_add_destructor(p, destroy_plugin);
|
tal_add_destructor(p, destroy_plugin);
|
||||||
list_head_init(&p->pending_rpccalls);
|
list_head_init(&p->pending_rpccalls);
|
||||||
|
|
||||||
|
p->important = important;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +209,8 @@ void plugin_blacklist(struct plugins *plugins, const char *name)
|
|||||||
log_info(plugins->log, "%s: disabled via disable-plugin",
|
log_info(plugins->log, "%s: disabled via disable-plugin",
|
||||||
p->cmd);
|
p->cmd);
|
||||||
list_del_from(&plugins->plugins, &p->list);
|
list_del_from(&plugins->plugins, &p->list);
|
||||||
|
/* disable-plugin overrides important-plugin. */
|
||||||
|
p->important = false;
|
||||||
tal_free(p);
|
tal_free(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1188,7 +1213,7 @@ char *add_plugin_dir(struct plugins *plugins, const char *dir, bool error_ok)
|
|||||||
log_info(plugins->log, "%s: disabled via disable-plugin",
|
log_info(plugins->log, "%s: disabled via disable-plugin",
|
||||||
fullpath);
|
fullpath);
|
||||||
} else {
|
} else {
|
||||||
p = plugin_register(plugins, fullpath, NULL);
|
p = plugin_register(plugins, fullpath, NULL, false);
|
||||||
if (!p && !error_ok)
|
if (!p && !error_ok)
|
||||||
return tal_fmt(NULL, "Failed to register %s: %s",
|
return tal_fmt(NULL, "Failed to register %s: %s",
|
||||||
fullpath, strerror(errno));
|
fullpath, strerror(errno));
|
||||||
@@ -1396,24 +1421,35 @@ void plugins_config(struct plugins *plugins)
|
|||||||
plugins->startup = false;
|
plugins->startup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void json_add_opt_plugins(struct json_stream *response,
|
/** json_add_opt_plugins_array
|
||||||
const struct plugins *plugins)
|
*
|
||||||
|
* @brief add a named array of plugins to the given response,
|
||||||
|
* depending on whether it is important or not important.
|
||||||
|
*
|
||||||
|
* @param response - the `json_stream` to write into.
|
||||||
|
* @param name - the field name of the array.
|
||||||
|
* @param plugins - the plugins object to query.
|
||||||
|
* @param important - match the `important` setting of the
|
||||||
|
* plugins to be added.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
void json_add_opt_plugins_array(struct json_stream *response,
|
||||||
|
const char *name,
|
||||||
|
const struct plugins *plugins,
|
||||||
|
bool important)
|
||||||
{
|
{
|
||||||
struct plugin *p;
|
struct plugin *p;
|
||||||
struct plugin_opt *opt;
|
|
||||||
const char *plugin_name;
|
const char *plugin_name;
|
||||||
|
struct plugin_opt *opt;
|
||||||
const char *opt_name;
|
const char *opt_name;
|
||||||
|
|
||||||
/* DEPRECATED: duplicated JSON "plugin" entries */
|
|
||||||
if (deprecated_apis) {
|
|
||||||
list_for_each(&plugins->plugins, p, list) {
|
|
||||||
json_add_string(response, "plugin", p->cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we output 'plugins' and their options as an array of substructures */
|
/* we output 'plugins' and their options as an array of substructures */
|
||||||
json_array_start(response, "plugins");
|
json_array_start(response, name);
|
||||||
list_for_each(&plugins->plugins, p, list) {
|
list_for_each(&plugins->plugins, p, list) {
|
||||||
|
/* Skip if not matching. */
|
||||||
|
if (p->important != important)
|
||||||
|
continue;
|
||||||
|
|
||||||
json_object_start(response, NULL);
|
json_object_start(response, NULL);
|
||||||
json_add_string(response, "path", p->cmd);
|
json_add_string(response, "path", p->cmd);
|
||||||
|
|
||||||
@@ -1444,6 +1480,23 @@ void json_add_opt_plugins(struct json_stream *response,
|
|||||||
json_array_end(response);
|
json_array_end(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void json_add_opt_plugins(struct json_stream *response,
|
||||||
|
const struct plugins *plugins)
|
||||||
|
{
|
||||||
|
struct plugin *p;
|
||||||
|
|
||||||
|
json_add_opt_plugins_array(response, "plugins", plugins, false);
|
||||||
|
json_add_opt_plugins_array(response, "important-plugins", plugins, true);
|
||||||
|
|
||||||
|
/* DEPRECATED: duplicated JSON "plugin" entries */
|
||||||
|
if (deprecated_apis) {
|
||||||
|
list_for_each(&plugins->plugins, p, list) {
|
||||||
|
json_add_string(response, p->important ? "important-plugin" : "plugin", p->cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void json_add_opt_disable_plugins(struct json_stream *response,
|
void json_add_opt_disable_plugins(struct json_stream *response,
|
||||||
const struct plugins *plugins)
|
const struct plugins *plugins)
|
||||||
{
|
{
|
||||||
@@ -1546,7 +1599,6 @@ static void mark_plugin_destroyed(const struct plugin *unused,
|
|||||||
pd->plugin = NULL;
|
pd->plugin = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct plugin_destroyed *plugin_detect_destruction(const struct plugin *plugin)
|
struct plugin_destroyed *plugin_detect_destruction(const struct plugin *plugin)
|
||||||
{
|
{
|
||||||
struct plugin_destroyed *pd = tal(NULL, struct plugin_destroyed);
|
struct plugin_destroyed *pd = tal(NULL, struct plugin_destroyed);
|
||||||
|
|||||||
@@ -79,6 +79,10 @@ struct plugin {
|
|||||||
/* An array of currently pending RPC method calls, to be killed if the
|
/* An array of currently pending RPC method calls, to be killed if the
|
||||||
* plugin exits. */
|
* plugin exits. */
|
||||||
struct list_head pending_rpccalls;
|
struct list_head pending_rpccalls;
|
||||||
|
|
||||||
|
/* If set, the plugin is so important that if it terminates early,
|
||||||
|
* C-lightning should terminate as well. */
|
||||||
|
bool important;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,6 +107,9 @@ struct plugins {
|
|||||||
|
|
||||||
/* Blacklist of plugins from --disable-plugin */
|
/* Blacklist of plugins from --disable-plugin */
|
||||||
const char **blacklist;
|
const char **blacklist;
|
||||||
|
|
||||||
|
/* Whether we are shutting down (`plugins_free` is called) */
|
||||||
|
bool shutdown;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The value of a plugin option, which can have different types.
|
/* The value of a plugin option, which can have different types.
|
||||||
@@ -175,13 +182,15 @@ void plugins_free(struct plugins *plugins);
|
|||||||
* @param plugins: Plugin context
|
* @param plugins: Plugin context
|
||||||
* @param path: The path of the executable for this plugin
|
* @param path: The path of the executable for this plugin
|
||||||
* @param start_cmd: The optional JSON command which caused this.
|
* @param start_cmd: The optional JSON command which caused this.
|
||||||
|
* @param important: The plugin is important.
|
||||||
*
|
*
|
||||||
* If @start_cmd, then plugin_cmd_killed or plugin_cmd_succeeded will be called
|
* If @start_cmd, then plugin_cmd_killed or plugin_cmd_succeeded will be called
|
||||||
* on it eventually.
|
* on it eventually.
|
||||||
*/
|
*/
|
||||||
struct plugin *plugin_register(struct plugins *plugins,
|
struct plugin *plugin_register(struct plugins *plugins,
|
||||||
const char* path TAKES,
|
const char* path TAKES,
|
||||||
struct command *start_cmd);
|
struct command *start_cmd,
|
||||||
|
bool important);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the provided name matches a plugin command
|
* Returns true if the provided name matches a plugin command
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ struct command_result *plugin_cmd_all_complete(struct plugins *plugins,
|
|||||||
static struct command_result *
|
static struct command_result *
|
||||||
plugin_dynamic_start(struct command *cmd, const char *plugin_path)
|
plugin_dynamic_start(struct command *cmd, const char *plugin_path)
|
||||||
{
|
{
|
||||||
struct plugin *p = plugin_register(cmd->ld->plugins, plugin_path, cmd);
|
struct plugin *p = plugin_register(cmd->ld->plugins, plugin_path, cmd, false);
|
||||||
const char *err;
|
const char *err;
|
||||||
|
|
||||||
if (!p)
|
if (!p)
|
||||||
|
|||||||
@@ -833,7 +833,7 @@ def test_listconfigs(node_factory, bitcoind, chainparams):
|
|||||||
|
|
||||||
# Test one at a time.
|
# Test one at a time.
|
||||||
for c in configs.keys():
|
for c in configs.keys():
|
||||||
if c.startswith('#') or c.startswith('plugins'):
|
if c.startswith('#') or c.startswith('plugins') or c == 'important-plugins':
|
||||||
continue
|
continue
|
||||||
oneconfig = l1.rpc.listconfigs(config=c)
|
oneconfig = l1.rpc.listconfigs(config=c)
|
||||||
assert(oneconfig[c] == configs[c])
|
assert(oneconfig[c] == configs[c])
|
||||||
|
|||||||
Reference in New Issue
Block a user