mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
lightningd: actually order the hooks.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
e2a31f42f2
commit
6a55b4367e
@ -1064,7 +1064,7 @@ static const char *plugin_hooks_add(struct plugin *plugin, const char *buffer,
|
||||
return NULL;
|
||||
|
||||
json_for_each_arr(i, t, hookstok) {
|
||||
char *name;
|
||||
char *name, *depfail;
|
||||
struct plugin_hook *hook;
|
||||
|
||||
if (t->type == JSMN_OBJECT) {
|
||||
@ -1093,6 +1093,12 @@ static const char *plugin_hooks_add(struct plugin *plugin, const char *buffer,
|
||||
}
|
||||
|
||||
plugin_hook_add_deps(hook, plugin, buffer, beforetok, aftertok);
|
||||
depfail = plugin_hook_make_ordered(tmpctx, hook);
|
||||
if (depfail)
|
||||
return tal_fmt(plugin,
|
||||
"Cannot correctly order hook %s:"
|
||||
"conflicts in %s",
|
||||
name, depfail);
|
||||
tal_free(name);
|
||||
}
|
||||
return NULL;
|
||||
|
@ -392,3 +392,125 @@ void plugin_hook_add_deps(struct plugin_hook *hook,
|
||||
add_deps(&h->before, buffer, before);
|
||||
add_deps(&h->after, buffer, after);
|
||||
}
|
||||
|
||||
/* From https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
|
||||
* (https://creativecommons.org/licenses/by-sa/3.0/):
|
||||
* L ← Empty list that will contain the sorted elements
|
||||
* S ← Set of all nodes with no incoming edge
|
||||
*
|
||||
* while S is not empty do
|
||||
* remove a node n from S
|
||||
* add n to L
|
||||
* for each node m with an edge e from n to m do
|
||||
* remove edge e from the graph
|
||||
* if m has no other incoming edges then
|
||||
* insert m into S
|
||||
*
|
||||
* if graph has edges then
|
||||
* return error (graph has at least one cycle)
|
||||
* else
|
||||
* return L (a topologically sorted order)
|
||||
*/
|
||||
struct hook_node {
|
||||
struct hook_instance *hook;
|
||||
size_t num_incoming;
|
||||
struct hook_node **outgoing;
|
||||
};
|
||||
|
||||
static struct hook_node *find_hook(struct hook_node *graph, const char *name)
|
||||
{
|
||||
for (size_t i = 0; i < tal_count(graph); i++) {
|
||||
if (plugin_paths_match(graph[i].hook->plugin->cmd, name))
|
||||
return graph + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *plugin_hook_make_ordered(const tal_t *ctx, struct plugin_hook *hook)
|
||||
{
|
||||
struct hook_node *graph;
|
||||
struct hook_node **l, **s;
|
||||
char *ret;
|
||||
|
||||
/* Populate graph nodes */
|
||||
graph = tal_arr(tmpctx, struct hook_node, tal_count(hook->hooks));
|
||||
for (size_t i = 0; i < tal_count(graph); i++) {
|
||||
graph[i].hook = hook->hooks[i];
|
||||
graph[i].num_incoming = 0;
|
||||
graph[i].outgoing = tal_arr(graph, struct hook_node *, 0);
|
||||
}
|
||||
|
||||
/* Add edges. */
|
||||
for (size_t i = 0; i < tal_count(graph); i++) {
|
||||
for (size_t j = 0; j < tal_count(graph[i].hook->before); j++) {
|
||||
struct hook_node *n = find_hook(graph,
|
||||
graph[i].hook->before[j]);
|
||||
if (!n) {
|
||||
/* This is useful for typos! */
|
||||
log_debug(graph[i].hook->plugin->log,
|
||||
"hook %s before unknown plugin %s",
|
||||
hook->name,
|
||||
graph[i].hook->before[j]);
|
||||
continue;
|
||||
}
|
||||
tal_arr_expand(&graph[i].outgoing, n);
|
||||
n->num_incoming++;
|
||||
}
|
||||
for (size_t j = 0; j < tal_count(graph[i].hook->after); j++) {
|
||||
struct hook_node *n = find_hook(graph,
|
||||
graph[i].hook->after[j]);
|
||||
if (!n) {
|
||||
/* This is useful for typos! */
|
||||
log_debug(graph[i].hook->plugin->log,
|
||||
"hook %s after unknown plugin %s",
|
||||
hook->name,
|
||||
graph[i].hook->after[j]);
|
||||
continue;
|
||||
}
|
||||
tal_arr_expand(&n->outgoing, &graph[i]);
|
||||
graph[i].num_incoming++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate array of ready-to-go nodes. */
|
||||
s = tal_arr(graph, struct hook_node *, 0);
|
||||
for (size_t i = 0; i < tal_count(graph); i++) {
|
||||
if (graph[i].num_incoming == 0)
|
||||
tal_arr_expand(&s, &graph[i]);
|
||||
}
|
||||
|
||||
l = tal_arr(graph, struct hook_node *, 0);
|
||||
while (tal_count(s)) {
|
||||
struct hook_node *n = s[0];
|
||||
tal_arr_expand(&l, n);
|
||||
tal_arr_remove(&s, 0);
|
||||
|
||||
/* for each node m with an edge e from n to m do
|
||||
* remove edge e from the graph
|
||||
* if m has no other incoming edges then
|
||||
* insert m into S
|
||||
*/
|
||||
for (size_t i = 0; i < tal_count(n->outgoing); i++) {
|
||||
if (--n->outgoing[i]->num_incoming == 0)
|
||||
tal_arr_expand(&s, n->outgoing[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for any left over */
|
||||
ret = tal_strdup(ctx, "");
|
||||
for (size_t i = 0; i < tal_count(graph); i++) {
|
||||
if (graph[i].num_incoming)
|
||||
tal_append_fmt(&ret, "%s ", graph[i].hook->plugin->cmd);
|
||||
}
|
||||
|
||||
if (strlen(ret) == 0) {
|
||||
/* Success! Write them back in order. */
|
||||
assert(tal_count(l) == tal_count(hook->hooks));
|
||||
for (size_t i = 0; i < tal_count(hook->hooks); i++)
|
||||
hook->hooks[i] = l[i]->hook;
|
||||
|
||||
return tal_free(ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -168,4 +168,7 @@ void plugin_hook_add_deps(struct plugin_hook *hook,
|
||||
const jsmntok_t *before,
|
||||
const jsmntok_t *after);
|
||||
|
||||
/* Returns NULL on success, error string allocated off ctx on failure. */
|
||||
char *plugin_hook_make_ordered(const tal_t *ctx, struct plugin_hook *hook);
|
||||
|
||||
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */
|
||||
|
Loading…
Reference in New Issue
Block a user