Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix plugin startup crash #7673

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions lightningd/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -875,14 +875,24 @@ static struct io_plan *plugin_write_json(struct io_conn *conn,
/* This catches the case where their stdout closes (usually they're dead). */
static void plugin_conn_finish(struct io_conn *conn, struct plugin *plugin)
{
struct db *db = plugin->plugins->ld->wallet->db;
db_begin_transaction(db);
struct db *db;

/* If they die during startup (plugins_init) wallet is NULL
* (but there are also no plugin commands to kill, so nothing
* would ever try to access db */
if (plugin->plugins->ld->wallet) {
db = plugin->plugins->ld->wallet->db;
db_begin_transaction(db);
} else
db = NULL;

/* This is expected at shutdown of course. */
plugin_kill(plugin,
plugin->plugins->ld->state == LD_STATE_SHUTDOWN
? LOG_DBG : LOG_INFORM,
"exited %s", state_desc(plugin));
db_commit_transaction(db);
if (db)
db_commit_transaction(db);
}

struct io_plan *plugin_stdin_conn_init(struct io_conn *conn,
Expand Down
21 changes: 15 additions & 6 deletions tests/plugins/broken.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@
"""Simple plugin to test that lightningd doesnt crash if it starts a
misbehaving plugin via RPC.
"""

from pyln.client import Plugin
import an_unexistent_module_that_will_make_me_crash

plugin = Plugin(dynamic=False)
import os
plugin = Plugin()
crash_at = os.environ.get("BROKEN_CRASH", "before_start")


@plugin.init()
def init(options, configuration, plugin):
plugin.log("broken.py initializing {}".format(configuration))
# We need to actually use the import to pass source checks..
an_unexistent_module_that_will_make_me_crash.hello()
assert crash_at == "during_init"
plugin.does_not_exist()


@plugin.method("test_broken")
def test_broken():
return {}


if crash_at == "before_start":
assert False
elif crash_at == "during_getmanifest":
del plugin.methods['getmanifest']

plugin.run()
12 changes: 12 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,18 @@ def test_plugin_command(node_factory):
n.rpc.stop()


def test_plugin_fail_on_startup(node_factory):
for crash in ('during_init', 'before_start', 'during_getmanifest'):
os.environ['BROKEN_CRASH'] = crash
n = node_factory.get_node(options={'plugin': os.path.join(os.getcwd(), "tests/plugins/broken.py")})
# This can happen before 'Server started with public key' msg
n.daemon.logsearch_start = 0
n.daemon.wait_for_log('plugin-broken.py: Traceback')

# Make sure they don't die *after* the message!
time.sleep(30)


def test_plugin_disable(node_factory):
"""--disable-plugin works"""
plugin_dir = os.path.join(os.getcwd(), 'contrib/plugins')
Expand Down
Loading