summaryrefslogtreecommitdiff
path: root/rustlibd/rustlib_lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rustlibd/rustlib_lib.rs')
-rw-r--r--rustlibd/rustlib_lib.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/rustlibd/rustlib_lib.rs b/rustlibd/rustlib_lib.rs
new file mode 100644
index 0000000000..363d801c20
--- /dev/null
+++ b/rustlibd/rustlib_lib.rs
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// September 9 2024, Christian Hopps <chopps@labn.net>
+//
+// Copyright (C) 2024 LabN Consulting, L.L.C.
+//
+
+pub mod c_shim;
+#[macro_use]
+pub mod frrutil;
+
+use std::ffi::c_void;
+use std::io::Result;
+use std::sync::OnceLock;
+
+use tokio::sync::mpsc::{channel, Receiver, Sender};
+use tracing::{debug, error};
+
+// -------
+// Globals
+// -------
+
+enum Command {
+ Quit,
+}
+
+static TX: OnceLock<Sender<Command>> = OnceLock::new();
+static RX: OnceLock<std::sync::Mutex<Receiver<Result<()>>>> = OnceLock::new();
+
+////
+/// Get an tokio runtime for async execution
+///
+/// This function should create a runtime for the daemon. The generic rust infra
+/// code will call it to create the runtime for the daemon. The runtime will be
+/// passed to `rust_run()` so that a main task (or whatever) can be started.
+fn get_runtime() -> tokio::runtime::Runtime {
+ tokio::runtime::Builder::new_multi_thread()
+ .on_thread_start(|| {
+ frrutil::frr_register_thread("tokio-worker-thread");
+ debug!("tokio thread started")
+ })
+ .on_thread_stop(|| debug!("tokio thread stop"))
+ .enable_all()
+ .build()
+ .unwrap()
+}
+
+// ===================================================
+// Initialization/Teardown Callbacks (from xxx_main.c)
+// ===================================================
+
+///
+/// Pre-init (called after frr_preinit()), returned value is passed to rust_init()
+fn rust_preinit(_daemon: *mut c_shim::frr_daemon_info) -> *mut c_void {
+ debug!("in rust_preinit");
+ std::ptr::null_mut()
+}
+
+///
+/// Pre-daemonize (called after frr_init()), returned value is passed to rust_run()
+fn rust_init(_preinit_val: *mut c_void) -> *mut c_void {
+ debug!("in rust_init");
+ std::ptr::null_mut()
+}
+
+///
+/// Called prior to entering the FRR event loop (frr_run())
+///
+/// This function should spawn a task into the tokio runtime to run the daemon
+fn rust_run(runtime: &mut tokio::runtime::Runtime, _init_val: *mut c_void) -> *mut c_void {
+ debug!("in rust_run");
+
+ //
+ // Setup command channel with async_main thread
+ //
+
+ let (tx, rx) = channel::<Command>(10);
+ let (result_tx, result_rx) = channel::<Result<()>>(10);
+ TX.set(tx.clone()).expect("only set once");
+ RX.set(std::sync::Mutex::new(result_rx))
+ .expect("only set once");
+
+ //
+ // Start running our main routine
+ //
+ runtime.spawn(async { async_main(rx, result_tx).await });
+ std::ptr::null_mut()
+}
+
+///
+/// FRR exiting callback
+///
+/// Do any cleanup prior to the rust runtime being dropped and all spawned tasks
+/// being joined/canceled.
+pub fn rust_fini(_run_val: *mut c_void) {
+ debug!("rust_fini: sending quit command to async_main");
+ if TX.get().unwrap().blocking_send(Command::Quit).is_err() {
+ return;
+ }
+
+ debug!("rust_fini: waiting on exit result from async_main");
+ if let Some(result) = RX.get().unwrap().lock().unwrap().blocking_recv() {
+ match result {
+ Ok(()) => debug!("Runtime quit successfully"),
+ Err(x) => error!("Failed to quit runtime: {}", x),
+ }
+ }
+}
+
+// ============================
+// Main Rust Execution Function
+// ============================
+
+///
+/// Main loop -- process commands from FRR
+async fn async_main(mut cmd_rx: Receiver<Command>, result_tx: Sender<Result<()>>) {
+ loop {
+ debug!("Waiting on cmd channel");
+ let cmd = match cmd_rx.recv().await {
+ None => continue,
+ Some(cmd) => cmd,
+ };
+ match cmd {
+ Command::Quit => {
+ debug!("Quit command");
+ let _ = result_tx.send(Ok(())).await;
+ return;
+ }
+ }
+ }
+}