Browse Source

lightningd: actually order the hooks.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-prep
Rusty Russell 4 years ago
committed by neil saitug
parent
commit
6a55b4367e
  1. 8
      lightningd/plugin.c
  2. 122
      lightningd/plugin_hook.c
  3. 3
      lightningd/plugin_hook.h

8
lightningd/plugin.c

@ -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;

122
lightningd/plugin_hook.c

@ -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;
}

3
lightningd/plugin_hook.h

@ -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…
Cancel
Save