lightningd: log when we have to defer hook registration.
What changed, and why it matters
This change only adds log messages when Core Lightning has to delay updating a plugin hook because the hook is currently in use. It does not fix or change any behavior; it is purely diagnostic observability to help detect a potential future 'livelock' problem. There is no security vulnerability being patched here.
No security action required. Treat as normal diagnostic logging improvement. If reviewing for reliability, verify the new log messages are not noisy in production, but this is an operational rather than security concern.
Security signals we found
No security-relevant code change: only logging added or promoted from debug to unusual
No bounds, input validation, cryptographic, or authorization changes
Commit message frames change as livelock observability, not a fix
Evidence from the diff
The commit threads a logger into plugin_hooks_make_ordered() and plugin_hook_make_ordered(), changes an existing hook-update log line from log_debug() to log_unusual(), and adds a new log_unusual() message when hook registration is deferred because the hook is still in use. The test is updated to match the new log strings. No logic, ordering, or synchronization behavior is altered.
Changed components
lightningd/plugin.clightningd/plugin_hook.clightningd/plugin_hook.htests/test_plugin.pyInspect captured patch +19 / −11
diff --git a/lightningd/plugin.c b/lightningd/plugin.c
index 9ce2fc44..17e4a192 100644
--- a/lightningd/plugin.c
+++ b/lightningd/plugin.c
@@ -124,7 +124,8 @@ static bool plugins_all_in_state(const struct plugins *plugins,
}
/* Once they've all replied with their manifests, we can order them. */
-static void check_plugins_manifests(struct plugins *plugins)
+static void check_plugins_manifests(struct plugins *plugins,
+ struct logger *log)
{
struct plugin *plugin;
struct plugin **depfail;
@@ -133,7 +134,7 @@ static void check_plugins_manifests(struct plugins *plugins)
return;
/* Now things are settled, try to order hooks. */
- depfail = plugin_hooks_make_ordered(tmpctx);
+ depfail = plugin_hooks_make_ordered(tmpctx, log);
for (size_t i = 0; i < tal_count(depfail); i++) {
/* Only complain and free plugins! */
if (depfail[i]->plugin_state != NEEDS_INIT)
@@ -284,7 +285,7 @@ static void destroy_plugin(struct plugin *p)
/* If this was last one manifests were waiting for, handle deps */
if (p->plugin_state == AWAITING_GETMANIFEST_RESPONSE)
- check_plugins_manifests(p->plugins);
+ check_plugins_manifests(p->plugins, p->plugins->ld->log);
/* Daemon shutdown overrules plugin's importance; aborts init checks */
if (p->plugins->ld->state == LD_STATE_SHUTDOWN) {
@@ -1843,7 +1844,7 @@ static void plugin_manifest_cb(const char *buffer,
plugin_kill(plugin, LOG_INFORM,
"Not a dynamic plugin");
else
- check_plugins_manifests(plugin->plugins);
+ check_plugins_manifests(plugin->plugins, plugin->log);
}
/* If this is a valid plugin return full path name, otherwise NULL */
diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c
index 34d21075..b80040ea 100644
--- a/lightningd/plugin_hook.c
+++ b/lightningd/plugin_hook.c
@@ -132,8 +132,8 @@ static void hook_done(struct lightningd *ld,
/* If we're the last one out, we can update hooks */
if (--hook->num_users == 0) {
if (hook->new_hooks) {
- log_debug(ld->log, "Updating hooks for %s now usage is done.",
- hook->name);
+ log_unusual(ld->log, "Updating hooks for %s now usage is done.",
+ hook->name);
/* Free this later (after final_cb) if not already done */
tal_steal(tmpctx, hook->hooks);
hook->hooks = hook->new_hooks;
@@ -472,6 +472,7 @@ static struct hook_node *get_best_candidate(struct hook_node *graph)
}
static struct plugin **plugin_hook_make_ordered(const tal_t *ctx,
+ struct logger *log,
struct plugin_hook *hook)
{
struct hook_node *graph, *n;
@@ -551,7 +552,10 @@ static struct plugin **plugin_hook_make_ordered(const tal_t *ctx,
tal_free(hook->hooks);
hook->hooks = hook->new_hooks;
hook->new_hooks = NULL;
- }
+ } else
+ /* If this ever live locks, we will see this in the log! */
+ log_unusual(log, "Deferring registration of hook %s until it's not in use.",
+ hook->name);
return NULL;
}
@@ -565,14 +569,15 @@ static void append_plugin_once(struct plugin ***ret, struct plugin *p)
tal_arr_expand(ret, p);
}
-struct plugin **plugin_hooks_make_ordered(const tal_t *ctx)
+struct plugin **plugin_hooks_make_ordered(const tal_t *ctx,
+ struct logger *log)
{
size_t num_hooks;
struct plugin_hook **hooks = get_hooks(&num_hooks);
struct plugin **ret = tal_arr(ctx, struct plugin *, 0);
for (size_t i=0; i<num_hooks; i++) {
- struct plugin **these = plugin_hook_make_ordered(ctx, hooks[i]);
+ struct plugin **these = plugin_hook_make_ordered(ctx, log, hooks[i]);
for (size_t j = 0; j < tal_count(these); j++)
append_plugin_once(&ret, these[j]);
}
diff --git a/lightningd/plugin_hook.h b/lightningd/plugin_hook.h
index 51ddaf43..b1f108a3 100644
--- a/lightningd/plugin_hook.h
+++ b/lightningd/plugin_hook.h
@@ -128,6 +128,7 @@ void plugin_hook_add_deps(struct plugin_hook *hook,
const jsmntok_t *after);
/* Returns array of plugins which cannot be ordered (empty on success) */
-struct plugin **plugin_hooks_make_ordered(const tal_t *ctx);
+struct plugin **plugin_hooks_make_ordered(const tal_t *ctx,
+ struct logger *log);
#endif /* LIGHTNING_LIGHTNINGD_PLUGIN_HOOK_H */
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index e2df084a..a036ecc2 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -2639,7 +2639,8 @@ def test_hook_in_use(node_factory):
l2.rpc.plugin_stop(plugin=dep_b)
# We should have deferred hook update at least once!
- l2.daemon.wait_for_log("Updating hooks for htlc_accepted now usage is done.")
+ l2.daemon.wait_for_log("UNUSUAL plugin-dep_b.py: Deferring registration of hook htlc_accepted until it's not in use.")
+ l2.daemon.wait_for_log("UNUSUAL lightningd: Updating hooks for htlc_accepted now usage is done.")
def test_htlc_accepted_hook_fwdto(node_factory):
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.