plugins: allow 'before' and 'after' arrays for hooks.

The next patch will use these to order the hooks.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: plugins: hooks can now specify that they must be called 'before' or 'after' other plugins.
This commit is contained in:
Rusty Russell
2020-10-30 11:43:37 +10:30
committed by neil saitug
parent 7c3c0b1013
commit e2a31f42f2
4 changed files with 131 additions and 65 deletions

View File

@@ -117,7 +117,6 @@ static void destroy_plugin(struct plugin *p)
{
struct plugin_rpccall *call;
plugin_hook_unregister_all(p);
list_del(&p->list);
/* Terminate all pending RPC calls with an error. */
@@ -1057,20 +1056,43 @@ static const char *plugin_subscriptions_add(struct plugin *plugin,
static const char *plugin_hooks_add(struct plugin *plugin, const char *buffer,
const jsmntok_t *resulttok)
{
const jsmntok_t *hookstok = json_get_member(buffer, resulttok, "hooks");
const jsmntok_t *t, *hookstok, *beforetok, *aftertok;
size_t i;
hookstok = json_get_member(buffer, resulttok, "hooks");
if (!hookstok)
return NULL;
for (int i = 0; i < hookstok->size; i++) {
char *name = json_strdup(tmpctx, plugin->buffer,
json_get_arr(hookstok, i));
if (!plugin_hook_register(plugin, name)) {
json_for_each_arr(i, t, hookstok) {
char *name;
struct plugin_hook *hook;
if (t->type == JSMN_OBJECT) {
const jsmntok_t *nametok;
nametok = json_get_member(buffer, t, "name");
if (!nametok)
return tal_fmt(plugin, "no name in hook obj %.*s",
json_tok_full_len(t),
json_tok_full(buffer, t));
name = json_strdup(tmpctx, buffer, nametok);
beforetok = json_get_member(buffer, t, "before");
aftertok = json_get_member(buffer, t, "after");
} else {
name = json_strdup(tmpctx, plugin->buffer, t);
beforetok = aftertok = NULL;
}
hook = plugin_hook_register(plugin, name);
if (!hook) {
return tal_fmt(plugin,
"could not register hook '%s', either the "
"name doesn't exist or another plugin "
"already registered it.",
name);
}
plugin_hook_add_deps(hook, plugin, buffer, beforetok, aftertok);
tal_free(name);
}
return NULL;

View File

@@ -18,6 +18,14 @@ struct plugin_hook_request {
struct lightningd *ld;
};
struct hook_instance {
/* What plugin registered */
struct plugin *plugin;
/* Dependencies it asked for. */
const char **before, **after;
};
/* A link in the plugin_hook call chain (there's a joke in there about
* computer scientists and naming...). The purpose is to act both as a list
* from which elements can be popped off as we progress along the chain as
@@ -43,61 +51,52 @@ static struct plugin_hook *plugin_hook_by_name(const char *name)
return NULL;
}
bool plugin_hook_register(struct plugin *plugin, const char *method)
/* When we destroy a plugin, we remove its hooks */
static void destroy_hook_instance(struct hook_instance *h,
struct plugin_hook *hook)
{
for (size_t i = 0; i < tal_count(hook->hooks); i++) {
if (h == hook->hooks[i]) {
tal_arr_remove(&hook->hooks, i);
return;
}
}
abort();
}
struct plugin_hook *plugin_hook_register(struct plugin *plugin, const char *method)
{
struct hook_instance *h;
struct plugin_hook *hook = plugin_hook_by_name(method);
if (!hook) {
/* No such hook name registered */
return false;
return NULL;
}
/* Make sure the plugins array is initialized. */
if (hook->plugins == NULL)
hook->plugins = notleak(tal_arr(NULL, struct plugin *, 0));
/* Make sure the hook_elements array is initialized. */
if (hook->hooks == NULL)
hook->hooks = notleak(tal_arr(NULL, struct hook_instance *, 0));
/* If this is a single type hook and we have a plugin registered we
* must fail this attempt to add the plugin to the hook. */
if (hook->type == PLUGIN_HOOK_SINGLE && tal_count(hook->plugins) > 0)
return false;
if (hook->type == PLUGIN_HOOK_SINGLE && tal_count(hook->hooks) > 0)
return NULL;
/* Ensure we don't register the same plugin multple times. */
for (size_t i=0; i<tal_count(hook->plugins); i++)
if (hook->plugins[i] == plugin)
return true;
for (size_t i=0; i<tal_count(hook->hooks); i++)
if (hook->hooks[i]->plugin == plugin)
return NULL;
/* Ok, we're sure they can register and they aren't yet registered, so
* register them. */
tal_arr_expand(&hook->plugins, plugin);
return true;
}
h = tal(plugin, struct hook_instance);
h->plugin = plugin;
h->before = tal_arr(h, const char *, 0);
h->after = tal_arr(h, const char *, 0);
tal_add_destructor2(h, destroy_hook_instance, hook);
bool plugin_hook_unregister(struct plugin *plugin, const char *method)
{
struct plugin_hook *hook = plugin_hook_by_name(method);
if (!hook || !hook->plugins) {
/* No such hook name registered */
return false;
}
for (size_t i = 0; i < tal_count(hook->plugins); i++) {
if (hook->plugins[i] == plugin) {
tal_arr_remove(&hook->plugins, i);
return true;
}
}
return false;
}
void plugin_hook_unregister_all(struct plugin *plugin)
{
static struct plugin_hook **hooks = NULL;
static size_t num_hooks;
if (!hooks)
hooks = autodata_get(hooks, &num_hooks);
for (size_t i = 0; i < num_hooks; i++)
plugin_hook_unregister(plugin, hooks[i]->name);
tal_arr_expand(&hook->hooks, h);
return hook;
}
/* Mutual recursion */
@@ -244,23 +243,24 @@ bool plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook,
{
struct plugin_hook_request *ph_req;
struct plugin_hook_call_link *link;
if (tal_count(hook->plugins)) {
if (tal_count(hook->hooks)) {
/* If we have a plugin that has registered for this
* hook, serialize and call it */
/* FIXME: technically this is a leak, but we don't
* currently have a list to store these. We might want
* to eventually to inspect in-flight requests. */
ph_req = notleak(tal(hook->plugins, struct plugin_hook_request));
ph_req = notleak(tal(hook->hooks, struct plugin_hook_request));
ph_req->hook = hook;
ph_req->cb_arg = tal_steal(ph_req, cb_arg);
ph_req->db = ld->wallet->db;
ph_req->ld = ld;
list_head_init(&ph_req->call_chain);
for (size_t i=0; i<tal_count(hook->plugins); i++) {
for (size_t i=0; i<tal_count(hook->hooks); i++) {
/* We allocate this off of the plugin so we get notified if the plugin dies. */
link = tal(hook->plugins[i], struct plugin_hook_call_link);
link->plugin = hook->plugins[i];
link = tal(hook->hooks[i]->plugin,
struct plugin_hook_call_link);
link->plugin = hook->hooks[i]->plugin;
link->req = ph_req;
tal_add_destructor(link, plugin_hook_killed);
list_add_tail(&ph_req->call_chain, &link->list);
@@ -324,10 +324,10 @@ void plugin_hook_db_sync(struct db *db)
struct plugin *plugin;
const char **changes = db_changes(db);
if (tal_count(hook->plugins) == 0)
if (tal_count(hook->hooks) == 0)
return;
ph_req = notleak(tal(hook->plugins, struct plugin_hook_request));
ph_req = notleak(tal(hook->hooks, struct plugin_hook_request));
/* FIXME: do IO logging for this! */
req = jsonrpc_request_start(NULL, hook->name, NULL, NULL,
db_hook_response,
@@ -335,7 +335,7 @@ void plugin_hook_db_sync(struct db *db)
ph_req->hook = hook;
ph_req->db = db;
plugin = ph_req->plugin = hook->plugins[0];
plugin = ph_req->plugin = hook->hooks[0]->plugin;
json_add_num(req->stream, "data_version", db_data_version_get(db));
@@ -357,3 +357,38 @@ void plugin_hook_db_sync(struct db *db)
io_break(ret);
}
}
static void add_deps(const char ***arr,
const char *buffer,
const jsmntok_t *arrtok)
{
const jsmntok_t *t;
size_t i;
if (!arrtok)
return;
json_for_each_arr(i, t, arrtok)
tal_arr_expand(arr, json_strdup(*arr, buffer, t));
}
void plugin_hook_add_deps(struct plugin_hook *hook,
struct plugin *plugin,
const char *buffer,
const jsmntok_t *before,
const jsmntok_t *after)
{
struct hook_instance *h = NULL;
/* We just added this, it must exist */
for (size_t i = 0; i < tal_count(hook->hooks); i++) {
if (hook->hooks[i]->plugin == plugin) {
h = hook->hooks[i];
break;
}
}
assert(h);
add_deps(&h->before, buffer, before);
add_deps(&h->after, buffer, after);
}

View File

@@ -77,7 +77,7 @@ struct plugin_hook {
/* Which plugins have registered this hook? This is a `tal_arr`
* initialized at creation. */
struct plugin **plugins;
struct hook_instance **hooks;
};
AUTODATA_TYPE(hooks, struct plugin_hook);
@@ -155,15 +155,17 @@ bool plugin_hook_continue(void *arg, const char *buffer, const jsmntok_t *toks);
AUTODATA(hooks, &name##_hook_gen); \
PLUGIN_HOOK_CALL_DEF(name, cb_arg_type)
bool plugin_hook_register(struct plugin *plugin, const char *method);
/* Unregister a hook a plugin has registered for */
bool plugin_hook_unregister(struct plugin *plugin, const char *method);
/* Unregister all hooks a plugin has registered for */
void plugin_hook_unregister_all(struct plugin *plugin);
struct plugin_hook *plugin_hook_register(struct plugin *plugin,
const char *method);
/* Special sync plugin hook for db. */
void plugin_hook_db_sync(struct db *db);
/* Add dependencies for this hook. */
void plugin_hook_add_deps(struct plugin_hook *hook,
struct plugin *plugin,
const char *buffer,
const jsmntok_t *before,
const jsmntok_t *after);
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */