lightningd: add --plugin-dir option to load directory full of plugins.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
Christian Decker
parent
31830ea6bb
commit
a4287f99fd
@@ -293,6 +293,11 @@ static char *opt_add_plugin(const char *arg, struct lightningd *ld)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *opt_add_plugin_dir(const char *arg, struct lightningd *ld)
|
||||
{
|
||||
return add_plugin_dir(ld->plugins, arg);
|
||||
}
|
||||
|
||||
static void config_register_opts(struct lightningd *ld)
|
||||
{
|
||||
opt_register_early_arg("--conf=<file>", opt_set_talstr, NULL,
|
||||
@@ -302,7 +307,10 @@ static void config_register_opts(struct lightningd *ld)
|
||||
/* Register plugins as an early argc, 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.");
|
||||
"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_noarg("--daemon", opt_set_bool, &ld->daemon,
|
||||
"Run in the background, suppress stdout/stderr");
|
||||
@@ -989,6 +997,9 @@ 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) {
|
||||
/* FIXME: We actually treat it as if they specified
|
||||
* --plugin for each one, so ignore this */
|
||||
#if DEVELOPER
|
||||
} else if (strstarts(name, "dev-")) {
|
||||
/* Ignore dev settings */
|
||||
|
||||
@@ -5,13 +5,18 @@
|
||||
#include <ccan/list/list.h>
|
||||
#include <ccan/opt/opt.h>
|
||||
#include <ccan/pipecmd/pipecmd.h>
|
||||
#include <ccan/tal/path/path.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <ccan/utf8/utf8.h>
|
||||
#include <common/memleak.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <lightningd/json.h>
|
||||
#include <lightningd/jsonrpc_errors.h>
|
||||
#include <lightningd/lightningd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct plugin {
|
||||
@@ -22,6 +27,7 @@ struct plugin {
|
||||
struct io_conn *stdin_conn, *stdout_conn;
|
||||
bool stop;
|
||||
struct plugins *plugins;
|
||||
const char **plugin_path;
|
||||
|
||||
/* Stuff we read */
|
||||
char *buffer;
|
||||
@@ -602,6 +608,56 @@ static void plugin_manifest_cb(const struct plugin_request *req, struct plugin *
|
||||
plugin_kill(plugin, "Failed to register options or methods");
|
||||
}
|
||||
|
||||
/* If this is a valid plugin return full path name, otherwise NULL */
|
||||
static const char *plugin_fullpath(const tal_t *ctx, const char *dir,
|
||||
const char *basename)
|
||||
{
|
||||
struct stat st;
|
||||
const char *fullname;
|
||||
struct utf8_state utf8 = UTF8_STATE_INIT;
|
||||
|
||||
for (size_t i = 0; basename[i]; i++) {
|
||||
if (!utf8_decode(&utf8, basename[i]))
|
||||
continue;
|
||||
/* Not valid UTF8? Let's not go there... */
|
||||
if (errno != 0)
|
||||
return NULL;
|
||||
if (utf8.used_len != 1)
|
||||
continue;
|
||||
if (!cispunct(utf8.c))
|
||||
continue;
|
||||
if (utf8.c != '-' && utf8.c != '_' && utf8.c != '.')
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fullname = path_join(ctx, dir, basename);
|
||||
if (stat(fullname, &st) != 0)
|
||||
return tal_free(fullname);
|
||||
if (!(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
|
||||
return tal_free(fullname);
|
||||
return fullname;
|
||||
}
|
||||
|
||||
char *add_plugin_dir(struct plugins *plugins, const char *dir)
|
||||
{
|
||||
struct dirent *di;
|
||||
DIR *d = opendir(dir);
|
||||
if (!d)
|
||||
return tal_fmt("Failed to open plugin-dir %s: %s",
|
||||
dir, strerror(errno));
|
||||
|
||||
while ((di = readdir(d)) != NULL) {
|
||||
const char *fullpath;
|
||||
|
||||
if (streq(di->d_name, ".") || streq(di->d_name, ".."))
|
||||
continue;
|
||||
fullpath = plugin_fullpath(NULL, dir, di->d_name);
|
||||
if (fullpath)
|
||||
plugin_register(plugins, take(fullpath));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void plugins_init(struct plugins *plugins)
|
||||
{
|
||||
struct plugin *p;
|
||||
|
||||
@@ -56,4 +56,10 @@ void plugins_config(struct plugins *plugins);
|
||||
void json_add_opt_plugins(struct json_stream *response,
|
||||
const struct plugins *plugins);
|
||||
|
||||
|
||||
/**
|
||||
* Add a directory to the plugin path to automatically load plugins.
|
||||
*/
|
||||
char *add_plugin_dir(struct plugins *plugins, const char *dir);
|
||||
|
||||
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_H */
|
||||
|
||||
@@ -51,3 +51,9 @@ def test_rpc_passthrough(node_factory):
|
||||
assert(greet == "Hello Sun")
|
||||
with pytest.raises(RpcError):
|
||||
n.rpc.fail()
|
||||
|
||||
|
||||
def test_plugin_dir(node_factory):
|
||||
"""--plugin-dir works"""
|
||||
plugin_dir = 'contrib/plugins'
|
||||
node_factory.get_node(options={'plugin-dir': plugin_dir, 'greeting': 'Mars'})
|
||||
|
||||
Reference in New Issue
Block a user