falco_plugin/async_event/
mod.rs

1//! # Asynchronous event support
2//!
3//! Plugins with async events capability can enrich an event stream from a given source (not
4//! necessarily implemented by itself) by injecting events asynchronously in the stream. Such
5//! a feature can be used for implementing notification systems or recording state transitions
6//! in the event-driven model of the Falcosecurity libraries, so that they can be available to other
7//! components at runtime or when the event stream is replayed through a capture file.
8//!
9//! For example, the Falcosecurity libraries leverage this feature internally to implement metadata
10//! enrichment systems such as the one related to container runtimes. In that case, the libraries
11//! implement asynchronous jobs responsible for retrieving such information externally outside
12//! the main event processing loop so that it's non-blocking. The worker jobs produce a notification
13//! event every time a new container is detected and inject it asynchronously in the system event
14//! stream to be later processed for state updates and for evaluating Falco rules.
15//!
16//! For your plugin to support asynchronous events, you will need to implement the [`AsyncEventPlugin`]
17//! trait and invoke the [`async_event_plugin`](crate::async_event_plugin) macro, for example:
18//!
19//! ```
20//! use std::ffi::{CStr, CString};
21//! use std::sync::Arc;
22//! use std::thread::JoinHandle;
23//! use falco_plugin::anyhow::Error;
24//! use falco_plugin::event::events::Event;
25//! use falco_plugin::event::events::EventMetadata;
26//! use falco_plugin::base::Plugin;
27//! use falco_plugin::{async_event_plugin, plugin};
28//! use falco_plugin::async_event::{
29//!     AsyncEventPlugin,
30//!     AsyncHandler,
31//!     BackgroundTask};
32//! use falco_plugin::tables::TablesInput;
33//!
34//! struct MyAsyncPlugin {
35//!     task: Arc<BackgroundTask>,
36//!     thread: Option<JoinHandle<Result<(), Error>>>,
37//! }
38//!
39//! impl Plugin for MyAsyncPlugin {
40//!     // ...
41//! #    const NAME: &'static CStr = c"sample-plugin-rs";
42//! #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
43//! #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
44//! #    const CONTACT: &'static CStr = c"you@example.com";
45//! #    type ConfigType = ();
46//! #
47//! #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
48//! #        -> Result<Self, Error> {
49//! #        Ok(MyAsyncPlugin {
50//! #            task: Arc::new(Default::default()),
51//! #            thread: None,
52//! #        })
53//! #    }
54//! }
55//!
56//! impl AsyncEventPlugin for MyAsyncPlugin {
57//!     const ASYNC_EVENTS: &'static [&'static str] = &[]; // generate any async events
58//!     const EVENT_SOURCES: &'static [&'static str] = &[]; // attach to all event sources
59//!
60//!     fn start_async(&mut self, handler: AsyncHandler) -> Result<(), Error> {
61//!         // stop the thread if it was already running
62//!         if self.thread.is_some() {
63//!            self.stop_async()?;
64//!         }
65//!
66//!         // start a new thread
67//!         // waiting up to 100ms between events for the stop request
68//!         self.thread = Some(self.task.spawn(std::time::Duration::from_millis(100), move || {
69//!             // submit an async event to the main event loop
70//!             handler.emit(Self::async_event(c"sample_async", b"hello"))?;
71//!             Ok(())
72//!         })?);
73//!         Ok(())
74//!     }
75//!
76//!     fn stop_async(&mut self) -> Result<(), Error> {
77//!         self.task.request_stop_and_notify()?;
78//!         let Some(handle) = self.thread.take() else {
79//!             return Ok(());
80//!         };
81//!
82//!         match handle.join() {
83//!             Ok(res) => res,
84//!             Err(e) => std::panic::resume_unwind(e),
85//!         }
86//!     }
87//! }
88//!
89//! plugin!(MyAsyncPlugin);
90//! async_event_plugin!(MyAsyncPlugin);
91//! ```
92
93use crate::async_event::wrappers::AsyncPluginExported;
94use crate::base::Plugin;
95use falco_event::events::Event;
96
97mod async_handler;
98mod background_task;
99#[doc(hidden)]
100pub mod wrappers;
101
102pub use crate::event::AsyncEvent;
103pub use async_handler::AsyncHandler;
104pub use background_task::BackgroundTask;
105
106/// Support for asynchronous event plugins
107pub trait AsyncEventPlugin: Plugin + AsyncPluginExported {
108    /// # Event names coming from this plugin
109    ///
110    /// This constant contains a list describing the name list of all asynchronous events
111    /// that this plugin is capable of pushing into a live event stream. The framework rejects
112    /// async events produced by a plugin if their name is not on the name list returned by this
113    /// function.
114    const ASYNC_EVENTS: &'static [&'static str];
115    /// # Event sources to attach asynchronous events to
116    ///
117    /// This constant contains a list describing the event sources for which this plugin
118    /// is capable of injecting async events in the event stream of a capture.
119    ///
120    /// This is optional--if NULL or an empty array, then async events produced by this plugin will
121    /// be injected in the event stream of any data source.
122    ///
123    /// **Note**: one notable event source is called `syscall`
124    const EVENT_SOURCES: &'static [&'static str];
125
126    /// # Start asynchronous event generation
127    ///
128    /// When this method is called, your plugin should start whatever background mechanism
129    /// is necessary (e.g. spawn a separate thread) and use the [`AsyncHandler::emit`] method
130    /// to inject events to the main event loop.
131    ///
132    /// **Note**: you must provide a mechanism to shut down the thread upon a call to [`AsyncEventPlugin::stop_async`].
133    /// This may involve e.g. a [`std::sync::Condvar`] that's checked via [`std::sync::Condvar::wait_timeout`]
134    /// by the thread.
135    ///
136    /// **Note**: one notable event source is called `syscall`
137    fn start_async(&mut self, handler: AsyncHandler) -> Result<(), anyhow::Error>;
138
139    /// # Stop asynchronous event generation
140    ///
141    /// When this method is called, your plugin must stop the background mechanism started by
142    /// [`AsyncEventPlugin::start_async`] and wait for it to finish (no calls to [`AsyncHandler::emit`]
143    /// are permitted after this method returns).
144    ///
145    /// **Note**: [`AsyncEventPlugin::start_async`] can be called again, with a different [`AsyncHandler`].
146    fn stop_async(&mut self) -> Result<(), anyhow::Error>;
147
148    /// # Dump the plugin state as a series of async events
149    ///
150    /// When this method is called, your plugin may save its state via a series of async events
151    /// that will be replayed when a capture file is loaded.
152    ///
153    /// The default implementation does nothing.
154    fn dump_state(&mut self, _handler: AsyncHandler) -> Result<(), anyhow::Error> {
155        Ok(())
156    }
157
158    /// # A helper method to create an asynchronous event
159    fn async_event<'a>(
160        name: &'a std::ffi::CStr,
161        data: &'a [u8],
162    ) -> Event<AsyncEvent<'a, &'a [u8]>> {
163        let event = AsyncEvent {
164            plugin_id: 0, // gets populated by the framework, shall be None
165            name,
166            data,
167        };
168
169        let metadata = falco_event::events::EventMetadata::default();
170
171        Event {
172            metadata,
173            params: event,
174        }
175    }
176}