From b359a24772f3b2d6f75145495c2d064602883b47 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 10 Apr 2022 16:56:13 +0200 Subject: [PATCH] cln-plugin: Handle --help invocations better We now have ternary outcomes for `Builder.configure()` and `Builder.start()`: - Ok(Some(p)) means we were configured correctly, and can continue with our work normally - Ok(None) means that `lightningd` was invoked with `--help`, we weren't configured (which is not an error since the `lightningd` just implicitly told us to shut down) and user code should clean up and exit as well - Err(e) something went wrong, user code may report an error and exit. --- plugins/examples/cln-plugin-startup.rs | 15 ++++++++++---- plugins/grpc-plugin/src/main.rs | 12 +++++++++--- plugins/src/lib.rs | 27 ++++++++++++++++++++------ 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index 9bf8cc979..ff152235f 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -6,7 +6,7 @@ use cln_plugin::{options, Builder, Error, Plugin}; use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let plugin = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + if let Some(plugin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "test-option", options::Value::Integer(42), @@ -16,8 +16,12 @@ async fn main() -> Result<(), anyhow::Error> { .subscribe("connect", connect_handler) .hook("peer_connected", peer_connected_handler) .start() - .await?; - plugin.join().await + .await? + { + plugin.join().await + } else { + Ok(()) + } } async fn testmethod(_p: Plugin<()>, _v: serde_json::Value) -> Result { @@ -29,7 +33,10 @@ async fn connect_handler(_p: Plugin<()>, v: serde_json::Value) -> Result<(), Err Ok(()) } -async fn peer_connected_handler(_p: Plugin<()>, v: serde_json::Value) -> Result { +async fn peer_connected_handler( + _p: Plugin<()>, + v: serde_json::Value, +) -> Result { log::info!("Got a connect hook call: {}", v); Ok(json!({"result": "continue"})) } diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 58bd2751c..a83f0131d 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -28,19 +28,25 @@ async fn main() -> Result<()> { ca_cert, }; - let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) + let plugin = match Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", options::Value::Integer(-1), "Which port should the grpc plugin listen for incoming connections?", )) .configure() - .await?; + .await? + { + Some(p) => p, + None => return Ok(()), + }; let bind_port = match plugin.option("grpc-port") { Some(options::Value::Integer(-1)) => { log::info!("`grpc-port` option is not configured, exiting."); - plugin.disable("`grpc-port` option is not configured.").await?; + plugin + .disable("`grpc-port` option is not configured.") + .await?; return Ok(()); } Some(options::Value::Integer(i)) => i, diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index a3798adf7..e91bb69f6 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -139,7 +139,13 @@ where self } - pub async fn configure(mut self) -> Result, anyhow::Error> { + /// Communicate with `lightningd` to tell it about our options, + /// RPC methods and subscribe to hooks, and then process the + /// initialization, configuring the plugin. + /// + /// Returns `None` if we were invoked with `--help` and thus + /// should exit after this handshake + pub async fn configure(mut self) -> Result>, anyhow::Error> { let mut input = FramedRead::new(self.input.take().unwrap(), JsonRpcCodec::default()); // Sadly we need to wrap the output in a mutex in order to @@ -189,7 +195,7 @@ where // If we are being called with --help we will get // disconnected here. That's expected, so don't // complain about it. - 0 + return Ok(None); } }; @@ -213,7 +219,7 @@ where // Leave the `init` reply pending, so we can disable based on // the options if required. - Ok(ConfiguredPlugin { + Ok(Some(ConfiguredPlugin { // The JSON-RPC `id` field so we can reply correctly. init_id, input, @@ -228,7 +234,7 @@ where ), }, plugin, - }) + })) } /// Build and start the plugin loop. This performs the handshake @@ -236,8 +242,17 @@ where /// Core Lightning and dispatches them to the handlers. It only /// returns after completing the handshake to ensure that the /// configuration and initialization was successfull. - pub async fn start(self) -> Result, anyhow::Error> { - self.configure().await?.start().await + /// + /// If `lightningd` was called with `--help` we won't get a + /// `Plugin` instance and return `None` instead. This signals that + /// we should exit, and not continue running. `start()` returns in + /// order to allow user code to perform cleanup if necessary. + pub async fn start(self) -> Result>, anyhow::Error> { + if let Some(cp) = self.configure().await? { + Ok(Some(cp.start().await?)) + } else { + Ok(None) + } } fn handle_get_manifest(