mirror of
https://github.com/aljazceru/lightning.git
synced 2026-01-07 16:14:26 +01:00
plugins: add and install built-in plugin dir, add clear and disable options.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
Christian Decker
parent
ac5002a79e
commit
eb03b33655
8
Makefile
8
Makefile
@@ -401,6 +401,7 @@ exec_prefix = $(prefix)
|
||||
bindir = $(exec_prefix)/bin
|
||||
libexecdir = $(exec_prefix)/libexec
|
||||
pkglibexecdir = $(libexecdir)/$(PKGNAME)
|
||||
plugindir = $(pkglibexecdir)/plugins
|
||||
datadir = $(prefix)/share
|
||||
docdir = $(datadir)/doc/$(PKGNAME)
|
||||
mandir = $(datadir)/man
|
||||
@@ -427,6 +428,7 @@ installdirs:
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MKDIR_P) $(DESTDIR)$(bindir)
|
||||
$(MKDIR_P) $(DESTDIR)$(pkglibexecdir)
|
||||
$(MKDIR_P) $(DESTDIR)$(plugindir)
|
||||
$(MKDIR_P) $(DESTDIR)$(man1dir)
|
||||
$(MKDIR_P) $(DESTDIR)$(man5dir)
|
||||
$(MKDIR_P) $(DESTDIR)$(man7dir)
|
||||
@@ -446,11 +448,13 @@ PKGLIBEXEC_PROGRAMS = \
|
||||
lightningd/lightning_hsmd \
|
||||
lightningd/lightning_onchaind \
|
||||
lightningd/lightning_openingd
|
||||
PLUGINS=
|
||||
|
||||
install-program: installdirs $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS)
|
||||
@$(NORMAL_INSTALL)
|
||||
$(INSTALL_PROGRAM) $(BIN_PROGRAMS) $(DESTDIR)$(bindir)
|
||||
$(INSTALL_PROGRAM) $(PKGLIBEXEC_PROGRAMS) $(DESTDIR)$(pkglibexecdir)
|
||||
[ -z "$(PLUGINS)" ] || $(INSTALL_PROGRAM) $(PLUGINS) $(DESTDIR)$(plugindir)
|
||||
|
||||
MAN1PAGES = $(filter %.1,$(MANPAGES))
|
||||
MAN5PAGES = $(filter %.5,$(MANPAGES))
|
||||
@@ -472,6 +476,10 @@ uninstall:
|
||||
echo rm -f $(DESTDIR)$(bindir)/`basename $$f`; \
|
||||
rm -f $(DESTDIR)$(bindir)/`basename $$f`; \
|
||||
done
|
||||
@for f in $(PLUGINS); do \
|
||||
echo rm -f $(DESTDIR)$(plugindir)/`basename $$f`; \
|
||||
rm -f $(DESTDIR)$(plugindir)/`basename $$f`; \
|
||||
done
|
||||
@for f in $(PKGLIBEXEC_PROGRAMS); do \
|
||||
echo rm -f $(DESTDIR)$(pkglibexecdir)/`basename $$f`; \
|
||||
rm -f $(DESTDIR)$(pkglibexecdir)/`basename $$f`; \
|
||||
|
||||
@@ -339,7 +339,7 @@ static const char *find_my_directory(const tal_t *ctx, const char *argv0)
|
||||
*
|
||||
* TAKES is only a convention unfortunately, and ignored by the compiler.
|
||||
*/
|
||||
static const char *find_my_pkglibexec_path(const tal_t *ctx,
|
||||
static const char *find_my_pkglibexec_path(struct lightningd *ld,
|
||||
const char *my_path TAKES)
|
||||
{
|
||||
const char *pkglibexecdir;
|
||||
@@ -352,23 +352,34 @@ static const char *find_my_pkglibexec_path(const tal_t *ctx,
|
||||
* So, as we promised with 'TAKES' in our own declaration, if the
|
||||
* caller has called `take()` the `my_path` parameter, path_join()
|
||||
* will free it. */
|
||||
pkglibexecdir = path_join(ctx, my_path, BINTOPKGLIBEXECDIR);
|
||||
pkglibexecdir = path_join(NULL, my_path, BINTOPKGLIBEXECDIR);
|
||||
|
||||
/*~ The plugin dir is in ../libexec/c-lightning/plugins, which (unlike
|
||||
* those given on the command line) does not need to exist. */
|
||||
add_plugin_dir(ld->plugins,
|
||||
path_join(tmpctx, pkglibexecdir, "plugins"),
|
||||
true);
|
||||
|
||||
/*~ Sometimes take() can be more efficient, since the routine can
|
||||
* manipulate the string in place. This is the case here. */
|
||||
return path_simplify(ctx, take(pkglibexecdir));
|
||||
return path_simplify(ld, take(pkglibexecdir));
|
||||
}
|
||||
|
||||
/* Determine the correct daemon dir. */
|
||||
static const char *find_daemon_dir(const tal_t *ctx, const char *argv0)
|
||||
static const char *find_daemon_dir(struct lightningd *ld, const char *argv0)
|
||||
{
|
||||
const char *my_path = find_my_directory(ctx, argv0);
|
||||
const char *my_path = find_my_directory(ld, argv0);
|
||||
/* If we're running in-tree, all the subdaemons are with lightningd. */
|
||||
if (has_all_subdaemons(my_path))
|
||||
if (has_all_subdaemons(my_path)) {
|
||||
/* In this case, look in ../plugins */
|
||||
add_plugin_dir(ld->plugins,
|
||||
path_join(tmpctx, my_path, "../plugins"),
|
||||
true);
|
||||
return my_path;
|
||||
}
|
||||
|
||||
/* Otherwise we assume they're in the installed dir. */
|
||||
return find_my_pkglibexec_path(ctx, take(my_path));
|
||||
return find_my_pkglibexec_path(ld, take(my_path));
|
||||
}
|
||||
|
||||
/*~ We like to free everything on exit, so valgrind doesn't complain (valgrind
|
||||
|
||||
@@ -293,9 +293,22 @@ static char *opt_add_plugin(const char *arg, struct lightningd *ld)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *opt_disable_plugin(const char *arg, struct lightningd *ld)
|
||||
{
|
||||
if (plugin_remove(ld->plugins, arg))
|
||||
return NULL;
|
||||
return tal_fmt(NULL, "Could not find plugin %s", arg);
|
||||
}
|
||||
|
||||
static char *opt_add_plugin_dir(const char *arg, struct lightningd *ld)
|
||||
{
|
||||
return add_plugin_dir(ld->plugins, arg);
|
||||
return add_plugin_dir(ld->plugins, arg, false);
|
||||
}
|
||||
|
||||
static char *opt_clear_plugins(struct lightningd *ld)
|
||||
{
|
||||
clear_plugins(ld->plugins);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void config_register_opts(struct lightningd *ld)
|
||||
@@ -304,13 +317,19 @@ static void config_register_opts(struct lightningd *ld)
|
||||
&ld->config_filename,
|
||||
"Specify configuration file. Relative paths will be prefixed by lightning-dir location. (default: config)");
|
||||
|
||||
/* Register plugins as an early argc, so we can initialize them and have
|
||||
/* Register plugins as an early args, so we can initialize them and have
|
||||
* them register more command line options */
|
||||
opt_register_early_arg("--plugin", opt_add_plugin, NULL, ld,
|
||||
"Add a plugin to be run (can be used multiple times)");
|
||||
opt_register_early_arg("--plugin-dir", opt_add_plugin_dir,
|
||||
NULL, ld,
|
||||
"Add a directory to load plugins from (can be used multiple times)");
|
||||
opt_register_early_noarg("--clear-plugins", opt_clear_plugins,
|
||||
ld,
|
||||
"Remove all plugins added before this option");
|
||||
opt_register_early_arg("--disable-plugin", opt_disable_plugin,
|
||||
NULL, ld,
|
||||
"Disable a particular plugin by filename/name");
|
||||
|
||||
opt_register_noarg("--daemon", opt_set_bool, &ld->daemon,
|
||||
"Run in the background, suppress stdout/stderr");
|
||||
@@ -924,7 +943,9 @@ static void add_config(struct lightningd *ld,
|
||||
|| opt->cb == (void *)opt_set_testnet
|
||||
|| opt->cb == (void *)opt_set_mainnet
|
||||
|| opt->cb == (void *)opt_lightningd_usage
|
||||
|| opt->cb == (void *)test_subdaemons_and_exit) {
|
||||
|| opt->cb == (void *)test_subdaemons_and_exit
|
||||
/* FIXME: we can't recover this. */
|
||||
|| opt->cb == (void *)opt_clear_plugins) {
|
||||
/* These are not important */
|
||||
} else if (opt->cb == (void *)opt_set_bool) {
|
||||
const bool *b = opt->u.carg;
|
||||
@@ -997,9 +1018,10 @@ static void add_config(struct lightningd *ld,
|
||||
answer = fmt_wireaddr(name0, ld->proxyaddr);
|
||||
} else if (opt->cb_arg == (void *)opt_add_plugin) {
|
||||
json_add_opt_plugins(response, ld->plugins);
|
||||
} else if (opt->cb_arg == (void *)opt_add_plugin_dir) {
|
||||
} else if (opt->cb_arg == (void *)opt_add_plugin_dir
|
||||
|| opt->cb_arg == (void *)opt_disable_plugin) {
|
||||
/* FIXME: We actually treat it as if they specified
|
||||
* --plugin for each one, so ignore this */
|
||||
* --plugin for each one, so ignore these */
|
||||
#if DEVELOPER
|
||||
} else if (strstarts(name, "dev-")) {
|
||||
/* Ignore dev settings */
|
||||
|
||||
@@ -134,6 +134,41 @@ void plugin_register(struct plugins *plugins, const char* path TAKES)
|
||||
list_head_init(&p->plugin_opts);
|
||||
}
|
||||
|
||||
static bool paths_match(const char *cmd, const char *name)
|
||||
{
|
||||
if (strchr(name, PATH_SEP)) {
|
||||
const char *cmd_canon, *name_canon;
|
||||
|
||||
if (streq(cmd, name))
|
||||
return true;
|
||||
|
||||
/* These return NULL path doesn't exist */
|
||||
cmd_canon = path_canon(tmpctx, cmd);
|
||||
name_canon = path_canon(tmpctx, name);
|
||||
return cmd_canon && name_canon && streq(name_canon, cmd_canon);
|
||||
} else {
|
||||
/* No path separator means a basename match. */
|
||||
const char *base = path_basename(tmpctx, cmd);
|
||||
|
||||
return streq(base, name);
|
||||
}
|
||||
}
|
||||
|
||||
bool plugin_remove(struct plugins *plugins, const char *name)
|
||||
{
|
||||
struct plugin *p, *next;
|
||||
bool removed = false;
|
||||
|
||||
list_for_each_safe(&plugins->plugins, p, next, list) {
|
||||
if (paths_match(p->cmd, name)) {
|
||||
list_del_from(&plugins->plugins, &p->list);
|
||||
tal_free(p);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill a plugin process, with an error message.
|
||||
*/
|
||||
@@ -638,13 +673,16 @@ static const char *plugin_fullpath(const tal_t *ctx, const char *dir,
|
||||
return fullname;
|
||||
}
|
||||
|
||||
char *add_plugin_dir(struct plugins *plugins, const char *dir)
|
||||
char *add_plugin_dir(struct plugins *plugins, const char *dir, bool nonexist_ok)
|
||||
{
|
||||
struct dirent *di;
|
||||
DIR *d = opendir(dir);
|
||||
if (!d)
|
||||
if (!d) {
|
||||
if (nonexist_ok && errno == ENOENT)
|
||||
return NULL;
|
||||
return tal_fmt("Failed to open plugin-dir %s: %s",
|
||||
dir, strerror(errno));
|
||||
}
|
||||
|
||||
while ((di = readdir(d)) != NULL) {
|
||||
const char *fullpath;
|
||||
@@ -658,6 +696,15 @@ char *add_plugin_dir(struct plugins *plugins, const char *dir)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void clear_plugins(struct plugins *plugins)
|
||||
{
|
||||
struct plugin *p;
|
||||
|
||||
log_info(plugins->log, "clear-plugins removing all plugins");
|
||||
while ((p = list_pop(&plugins->plugins, struct plugin, list)) != NULL)
|
||||
tal_free(p);
|
||||
}
|
||||
|
||||
void plugins_init(struct plugins *plugins)
|
||||
{
|
||||
struct plugin *p;
|
||||
|
||||
@@ -38,6 +38,15 @@ void plugins_init(struct plugins *plugins);
|
||||
*/
|
||||
void plugin_register(struct plugins *plugins, const char* path TAKES);
|
||||
|
||||
|
||||
/**
|
||||
* Remove a plugin registered for initialization.
|
||||
*
|
||||
* @param plugins: Plugin context
|
||||
* @param arg: The basename or fullname of the executable for this plugin
|
||||
*/
|
||||
bool plugin_remove(struct plugins *plugins, const char *name);
|
||||
|
||||
/**
|
||||
* Send the configure message to all plugins.
|
||||
*
|
||||
@@ -60,6 +69,12 @@ void json_add_opt_plugins(struct json_stream *response,
|
||||
/**
|
||||
* Add a directory to the plugin path to automatically load plugins.
|
||||
*/
|
||||
char *add_plugin_dir(struct plugins *plugins, const char *dir);
|
||||
char *add_plugin_dir(struct plugins *plugins, const char *dir,
|
||||
bool nonexist_ok);
|
||||
|
||||
/**
|
||||
* Clear all plugins registered so far.
|
||||
*/
|
||||
void clear_plugins(struct plugins *plugins);
|
||||
|
||||
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_H */
|
||||
|
||||
@@ -9,6 +9,10 @@ int unused_main(int argc, char *argv[]);
|
||||
/* Generated stub for activate_peers */
|
||||
void activate_peers(struct lightningd *ld UNNEEDED)
|
||||
{ fprintf(stderr, "activate_peers called!\n"); abort(); }
|
||||
/* Generated stub for add_plugin_dir */
|
||||
char *add_plugin_dir(struct plugins *plugins UNNEEDED, const char *dir UNNEEDED,
|
||||
bool nonexist_ok UNNEEDED)
|
||||
{ fprintf(stderr, "add_plugin_dir called!\n"); abort(); }
|
||||
/* Generated stub for begin_topology */
|
||||
void begin_topology(struct chain_topology *topo UNNEEDED)
|
||||
{ fprintf(stderr, "begin_topology called!\n"); abort(); }
|
||||
|
||||
6
plugins/README.md
Normal file
6
plugins/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Plugin Directory
|
||||
|
||||
Anything file in this directory which is executable and doesn't start
|
||||
and doesn't contain ASCII symbols other than '.', '-' or '_' will be
|
||||
automatically loaded when lightningd starts (unless suppressed with
|
||||
commandline options).
|
||||
@@ -1,3 +1,4 @@
|
||||
from collections import OrderedDict
|
||||
from fixtures import * # noqa: F401,F403
|
||||
from lightning import RpcError
|
||||
|
||||
@@ -57,3 +58,22 @@ def test_plugin_dir(node_factory):
|
||||
"""--plugin-dir works"""
|
||||
plugin_dir = 'contrib/plugins'
|
||||
node_factory.get_node(options={'plugin-dir': plugin_dir, 'greeting': 'Mars'})
|
||||
|
||||
|
||||
def test_plugin_disable(node_factory):
|
||||
"""--disable-plugin works"""
|
||||
plugin_dir = 'contrib/plugins'
|
||||
# We need plugin-dir before disable-plugin!
|
||||
n = node_factory.get_node(options=OrderedDict([('plugin-dir', plugin_dir),
|
||||
('disable-plugin',
|
||||
'{}/helloworld.py'
|
||||
.format(plugin_dir))]))
|
||||
with pytest.raises(RpcError):
|
||||
n.rpc.hello(name='Sun')
|
||||
|
||||
# Also works by basename.
|
||||
n = node_factory.get_node(options=OrderedDict([('plugin-dir', plugin_dir),
|
||||
('disable-plugin',
|
||||
'helloworld.py')]))
|
||||
with pytest.raises(RpcError):
|
||||
n.rpc.hello(name='Sun')
|
||||
|
||||
Reference in New Issue
Block a user