mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
lightningd: actually order the hooks.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
committed by
neil saitug
parent
e2a31f42f2
commit
6a55b4367e
@@ -1064,7 +1064,7 @@ static const char *plugin_hooks_add(struct plugin *plugin, const char *buffer,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
json_for_each_arr(i, t, hookstok) {
|
json_for_each_arr(i, t, hookstok) {
|
||||||
char *name;
|
char *name, *depfail;
|
||||||
struct plugin_hook *hook;
|
struct plugin_hook *hook;
|
||||||
|
|
||||||
if (t->type == JSMN_OBJECT) {
|
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);
|
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);
|
tal_free(name);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -392,3 +392,125 @@ void plugin_hook_add_deps(struct plugin_hook *hook,
|
|||||||
add_deps(&h->before, buffer, before);
|
add_deps(&h->before, buffer, before);
|
||||||
add_deps(&h->after, buffer, after);
|
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 *before,
|
||||||
const jsmntok_t *after);
|
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 */
|
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user