falco_plugin/listen/
mod.rs

1//! # Capture listening plugins
2//!
3//! Plugins with capture listening capability can receive notifications whenever a capture is
4//! started or stopped. Note that a capture may be stopped and restarted multiple times
5//! over the lifetime of a plugin.
6//!
7//! ## Background tasks
8//!
9//! Capture listening plugins receive a reference to a thread pool, which can be used to submit
10//! "routines" (tasks running in a separate thread, effectively).
11//!
12//! *Note* there is no built-in mechanism to stop a running routine, so you should avoid doing this
13//! in the routine:
14//! ```ignore
15//! loop {
16//!     do_something();
17//!     std::thread::sleep(some_time);
18//! }
19//! ```
20//!
21//! Instead, have your routine just do a single iteration and request a rerun from the scheduler:
22//! ```ignore
23//! do_something();
24//! std::thread::sleep(some_time)
25//! std::ops::ControlFlow::Continue(())
26//! ```
27//!
28//! If you insist on using an infinite loop inside a routine, consider using e.g.
29//! [`BackgroundTask`](crate::async_event::BackgroundTask) to manage the lifetime of the routine.
30//!
31//! For your plugin to support event parsing, you will need to implement the [`CaptureListenPlugin`]
32//! trait and invoke the [`capture_listen_plugin`](crate::capture_listen_plugin) macro, for example:
33//!
34//! ```
35//!# use std::ffi::CStr;
36//!# use std::time::Duration;
37//! use falco_plugin::anyhow::Error;
38//! use falco_plugin::base::Plugin;
39//! use falco_plugin::{capture_listen_plugin, plugin};
40//! use falco_plugin::listen::{CaptureListenInput, CaptureListenPlugin, Routine};
41//!# use falco_plugin::tables::TablesInput;
42//!# use log;
43//!
44//! struct MyListenPlugin {
45//!     tasks: Vec<Routine>,
46//! }
47//!
48//! impl Plugin for MyListenPlugin {
49//!     // ...
50//! #    const NAME: &'static CStr = c"sample-plugin-rs";
51//! #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
52//! #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
53//! #    const CONTACT: &'static CStr = c"you@example.com";
54//! #    type ConfigType = ();
55//! #
56//! #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
57//! #        -> Result<Self, Error> {
58//! #        Ok(MyListenPlugin {
59//! #             tasks: Vec::new(),
60//! #        })
61//! #    }
62//! }
63//!
64//! impl CaptureListenPlugin for MyListenPlugin {
65//!     fn capture_open(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> {
66//!         log::info!("Capture started");
67//!         self.tasks.push(listen_input.thread_pool.subscribe(|| {
68//!             log::info!("Doing stuff in the background");
69//!             std::thread::sleep(Duration::from_millis(500));
70//!             std::ops::ControlFlow::Continue(())
71//!         })?);
72//!
73//!         Ok(())
74//!     }
75//!
76//! fn capture_close(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> {
77//!         log::info!("Capture stopped");
78//!         for routine in self.tasks.drain(..) {
79//!             listen_input.thread_pool.unsubscribe(&routine)?;
80//!         }
81//!
82//!         Ok(())
83//!     }
84//! }
85//!
86//! plugin!(MyListenPlugin);
87//! capture_listen_plugin!(MyListenPlugin);
88//! ```
89
90use crate::base::Plugin;
91use crate::error::last_error::LastError;
92use crate::listen::wrappers::CaptureListenPluginExported;
93use crate::tables::LazyTableReader;
94use crate::tables::LazyTableWriter;
95use falco_plugin_api::ss_plugin_capture_listen_input;
96
97mod routine;
98#[doc(hidden)]
99pub mod wrappers;
100
101pub use routine::{Routine, ThreadPool};
102
103/// Support for capture listening plugins
104pub trait CaptureListenPlugin: Plugin + CaptureListenPluginExported {
105    /// # Capture open notification
106    ///
107    /// This method gets called whenever the capture is started
108    fn capture_open(&mut self, listen_input: &CaptureListenInput) -> Result<(), anyhow::Error>;
109
110    /// # Capture close notification
111    ///
112    /// This method gets called whenever the capture is stopped
113    fn capture_close(&mut self, listen_input: &CaptureListenInput) -> Result<(), anyhow::Error>;
114}
115
116/// # The input to a capture listening plugin
117///
118/// It has two fields containing the vtables needed to access tables imported through
119/// the [tables API](`crate::tables`), as well as a [`ThreadPool`] to run tasks
120/// in the background.
121#[derive(Debug)]
122pub struct CaptureListenInput<'t> {
123    /// Accessors to the thread pool for submitting routines to
124    pub thread_pool: ThreadPool,
125    /// Accessors to read table entries
126    pub reader: LazyTableReader<'t>,
127    /// Accessors to modify table entries
128    pub writer: LazyTableWriter<'t>,
129}
130
131impl CaptureListenInput<'_> {
132    unsafe fn try_from(
133        value: *const ss_plugin_capture_listen_input,
134        last_error: LastError,
135    ) -> Result<Self, anyhow::Error> {
136        let input = unsafe {
137            value
138                .as_ref()
139                .ok_or_else(|| anyhow::anyhow!("Got null event parse input"))?
140        };
141
142        let thread_pool = ThreadPool::try_from(input.owner, input.routine, last_error.clone())?;
143
144        let reader = unsafe {
145            input
146                .table_reader_ext
147                .as_ref()
148                .ok_or_else(|| anyhow::anyhow!("Got null reader vtable"))?
149        };
150        let writer = unsafe {
151            input
152                .table_writer_ext
153                .as_ref()
154                .ok_or_else(|| anyhow::anyhow!("Got null writer vtable"))?
155        };
156
157        let reader = LazyTableReader::new(reader, last_error.clone());
158        let writer = LazyTableWriter::try_from(writer, last_error)?;
159
160        Ok(Self {
161            thread_pool,
162            reader,
163            writer,
164        })
165    }
166}