From 20630495596c71c3764cf75ea475ec3dc9c728b4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Sep 2021 19:46:20 +0930 Subject: [PATCH] libplugin: plugin_exit helper which flushes stdout. We weren't actually getting the last log out; this does that. We have to fix test_bitcoin_failure which now notices the BROKEN log message. Signed-off-by: Rusty Russell Changelog-Fixed: libplugin: Fatal error messages from plugin_exit() now logged in lightningd. --- plugins/libplugin.c | 21 +++++++++++++++++++-- plugins/libplugin.h | 3 +++ tests/test_misc.py | 7 ++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 43216f335..63d56c21d 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,8 @@ struct plugin { bool manifested; /* Has init been received ? */ bool initialized; + /* Are we exiting? */ + bool exiting; /* Map from json command names to usage strings: we don't put this inside * struct json_command as it's good practice to have those const. */ @@ -1123,6 +1126,15 @@ void plugin_notify_progress(struct command *cmd, plugin_notify_end(cmd, js); } +void NORETURN plugin_exit(struct plugin *p, int exitcode) +{ + p->exiting = true; + io_conn_out_exclusive(p->stdout_conn, true); + io_wake(p); + io_loop(NULL, NULL); + exit(exitcode); +} + void NORETURN plugin_err(struct plugin *p, const char *fmt, ...) { va_list ap; @@ -1131,8 +1143,10 @@ void NORETURN plugin_err(struct plugin *p, const char *fmt, ...) plugin_logv(p, LOG_BROKEN, fmt, ap); va_end(ap); va_start(ap, fmt); - errx(1, "%s", tal_vfmt(NULL, fmt, ap)); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); va_end(ap); + plugin_exit(p, 1); } void plugin_log(struct plugin *p, enum log_level l, const char *fmt, ...) @@ -1315,6 +1329,9 @@ static struct io_plan *ld_write_json(struct io_conn *conn, return json_stream_output(plugin->js_arr[0], plugin->stdout_conn, ld_stream_complete, plugin); + /* If we were simply flushing final output, stop now. */ + if (plugin->exiting) + io_break(plugin); return io_out_wait(conn, plugin, ld_write_json, plugin); } @@ -1391,7 +1408,7 @@ static struct plugin *new_plugin(const tal_t *ctx, } p->init = init; - p->manifested = p->initialized = false; + p->manifested = p->initialized = p->exiting = false; p->restartability = restartability; strmap_init(&p->usagemap); p->in_timer = 0; diff --git a/plugins/libplugin.h b/plugins/libplugin.h index e480ed50d..e79b4a0a0 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -164,6 +164,9 @@ struct command_result *command_param_failed(void); /* Call this on fatal error. */ void NORETURN plugin_err(struct plugin *p, const char *fmt, ...); +/* Normal exit (makes sure to flush output!). */ +void NORETURN plugin_exit(struct plugin *p, int exitcode); + /* This command is finished, here's a detailed error; @cmd cannot be * NULL, data can be NULL; otherwise it must be a JSON object. */ struct command_result *WARN_UNUSED_RESULT diff --git a/tests/test_misc.py b/tests/test_misc.py index 65d810ad0..94282291e 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -134,11 +134,16 @@ def test_bitcoin_failure(node_factory, bitcoind): bitcoind.cmd_line += ["-blocksonly"] bitcoind.start() - l2 = node_factory.get_node(start=False, expect_fail=True) + # Ignore BROKEN log message about blocksonly mode. + l2 = node_factory.get_node(start=False, expect_fail=True, + allow_broken_log=True) with pytest.raises(ValueError): l2.start(stderr=subprocess.PIPE) assert l2.daemon.is_in_stderr(r".*deactivating transaction relay is not" " supported.") is not None + # wait_for_log gets upset since daemon is not running. + wait_for(lambda: l2.daemon.is_in_log('deactivating transaction' + ' relay is not supported')) def test_bitcoin_ibd(node_factory, bitcoind):