falco_plugin/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3#![warn(missing_debug_implementations)]
4#![deny(rustdoc::broken_intra_doc_links)]
5
6// reexport dependencies
7pub use anyhow;
8pub use falco_event as event;
9pub use falco_plugin_api as api;
10pub use phf;
11pub use schemars;
12pub use serde;
13
14pub use crate::plugin::error::FailureReason;
15
16/// # The common foundation for all Falco plugins
17///
18/// All plugins must implement the [`base::Plugin`] trait which specifies some basic metadata
19/// about the plugin.
20///
21/// See the [`base::Plugin`] trait documentation for details.
22pub mod base {
23    pub use crate::plugin::base::metrics::{Metric, MetricLabel, MetricType, MetricValue};
24    pub use crate::plugin::base::Plugin;
25    pub use crate::plugin::schema::Json;
26}
27
28/// # Field extraction plugin support
29///
30/// Plugins with field extraction capability can extract information from events
31/// based on fields. For example, a field (e.g. `proc.name`) extracts a value (e.g. process name
32/// like `nginx`) from a syscall event. The plugin returns a set of supported fields, and there are
33/// functions to extract a value given an event and field. The plugin framework can then build
34/// filtering expressions (e.g. rule conditions) based on these fields combined with relational
35/// and/or logical operators.
36///
37/// For example, given the expression `ct.name=root and ct.region=us-east-1`,
38/// the plugin framework handles parsing the expression, calling the plugin to extract values for
39/// fields `ct.name`/`ct.region` for a given event, and determining the result of the expression.
40/// In a Falco output string like `An EC2 Node was created (name=%ct.name region=%ct.region)`,
41/// the plugin framework handles parsing the output string, calling the plugin to extract values
42/// for fields, and building the resolved string, replacing the template field names
43/// (e.g. `%ct.region`) with values (e.g. `us-east-1`).
44///
45/// Plugins with this capability only focus on field extraction from events generated by other
46/// plugins or by the core libraries. They do not provide an event source but can extract fields
47/// from other event sources. The supported field extraction can be generic or be tied to a specific
48/// event source. An example is JSON field extraction, where a plugin might be able to extract
49/// fields from generic JSON payloads.
50///
51/// For your plugin to support field extraction, you will need to implement the [`extract::ExtractPlugin`]
52/// trait and invoke the [`extract_plugin`] macro, for example:
53///
54/// ```
55/// use std::ffi::{CStr, CString};
56/// use anyhow::Error;
57/// use falco_event::events::types::EventType;
58/// use falco_plugin::base::{Metric, Plugin};
59/// use falco_plugin::{extract_plugin, plugin};
60/// use falco_plugin::extract::{
61///     EventInput,
62///     ExtractFieldInfo,
63///     ExtractPlugin,
64///     ExtractRequest,
65///     field};
66/// use falco_plugin::tables::TablesInput;
67///
68/// struct MyExtractPlugin;
69/// impl Plugin for MyExtractPlugin {
70///     // ...
71/// #    const NAME: &'static CStr = c"sample-plugin-rs";
72/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
73/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
74/// #    const CONTACT: &'static CStr = c"you@example.com";
75/// #    type ConfigType = ();
76/// #
77/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
78/// #        -> Result<Self, anyhow::Error> {
79/// #        Ok(MyExtractPlugin)
80/// #    }
81/// #
82/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
83/// #        Ok(())
84/// #    }
85/// #
86/// #    fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
87/// #        []
88/// #    }
89/// }
90///
91/// impl MyExtractPlugin { // note this is not the trait implementation
92///     fn extract_sample(
93///         &mut self,
94///         _req: ExtractRequest<Self>,
95///     ) -> Result<CString, Error> {
96///         Ok(c"hello".to_owned())
97///     }
98/// }
99///
100/// impl ExtractPlugin for MyExtractPlugin {
101///     const EVENT_TYPES: &'static [EventType] = &[]; // all event types
102///     const EVENT_SOURCES: &'static [&'static str] = &[]; // all event sources
103///     type ExtractContext = ();
104///
105///     const EXTRACT_FIELDS: &'static [ExtractFieldInfo<Self>] = &[
106///         field("my_extract.sample", &Self::extract_sample),
107///     ];
108/// }
109///
110/// plugin!(MyExtractPlugin);
111/// extract_plugin!(MyExtractPlugin);
112/// ```
113///
114/// See the [`extract::ExtractPlugin`] trait documentation for details.
115pub mod extract {
116    pub use crate::plugin::event::EventInput;
117    pub use crate::plugin::extract::schema::field;
118    pub use crate::plugin::extract::schema::ExtractFieldInfo;
119    pub use crate::plugin::extract::ExtractByteRange;
120    pub use crate::plugin::extract::ExtractPlugin;
121    pub use crate::plugin::extract::ExtractRequest;
122    pub use crate::plugin::extract::INVALID_RANGE;
123    pub use crate::plugin::extract::UNSPECIFIED_RANGE;
124}
125
126/// # Event parsing support
127///
128/// Plugins with event parsing capability can hook into an event stream and receive all of its events
129/// sequentially. The parsing phase is the stage in the event processing loop in which
130/// the Falcosecurity libraries inspect the content of the events' payload and use it to apply
131/// internal state updates or implement additional logic. This phase happens before any field
132/// extraction for a given event. Each event in a given stream is guaranteed to be received at most once.
133///
134/// For your plugin to support event parsing, you will need to implement the [`parse::ParsePlugin`]
135/// trait and invoke the [`parse_plugin`] macro, for example:
136///
137/// ```
138///# use std::ffi::CStr;
139/// use falco_plugin::anyhow::Error;
140/// use falco_plugin::event::events::types::EventType;
141/// use falco_plugin::base::Plugin;
142/// use falco_plugin::{parse_plugin, plugin};
143/// use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin};
144///# use falco_plugin::tables::TablesInput;
145///
146/// struct MyParsePlugin;
147///
148/// impl Plugin for MyParsePlugin {
149///     // ...
150/// #    const NAME: &'static CStr = c"sample-plugin-rs";
151/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
152/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
153/// #    const CONTACT: &'static CStr = c"you@example.com";
154/// #    type ConfigType = ();
155/// #
156/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
157/// #        -> Result<Self, Error> {
158/// #        Ok(MyParsePlugin)
159/// #    }
160/// }
161///
162/// impl ParsePlugin for MyParsePlugin {
163///     const EVENT_TYPES: &'static [EventType] = &[]; // inspect all events...
164///     const EVENT_SOURCES: &'static [&'static str] = &[]; // ... from all event sources
165///
166///     fn parse_event(&mut self, event: &EventInput, parse_input: &ParseInput)
167///         -> Result<(), Error> {
168///         let event = event.event()?;
169///         let event = event.load_any()?;
170///
171///         // any processing you want here, e.g. involving tables
172///
173///         Ok(())
174///     }
175/// }
176///
177/// plugin!(MyParsePlugin);
178/// parse_plugin!(MyParsePlugin);
179/// ```
180pub mod parse {
181    pub use crate::plugin::event::EventInput;
182    pub use crate::plugin::parse::ParseInput;
183    pub use crate::plugin::parse::ParsePlugin;
184}
185
186/// # Asynchronous event support
187///
188/// Plugins with async events capability can enrich an event stream from a given source (not
189/// necessarily implemented by itself) by injecting events asynchronously in the stream. Such
190/// a feature can be used for implementing notification systems or recording state transitions
191/// in the event-driven model of the Falcosecurity libraries, so that they can be available to other
192/// components at runtime or when the event stream is replayed through a capture file.
193///
194/// For example, the Falcosecurity libraries leverage this feature internally to implement metadata
195/// enrichment systems such as the one related to container runtimes. In that case, the libraries
196/// implement asynchronous jobs responsible for retrieving such information externally outside
197/// the main event processing loop so that it's non-blocking. The worker jobs produce a notification
198/// event every time a new container is detected and inject it asynchronously in the system event
199/// stream to be later processed for state updates and for evaluating Falco rules.
200///
201/// For your plugin to support asynchronous events, you will need to implement the [`async_event::AsyncEventPlugin`]
202/// trait and invoke the [`async_event`] macro, for example:
203///
204/// ```
205/// use std::ffi::{CStr, CString};
206/// use std::sync::Arc;
207/// use std::thread::JoinHandle;
208/// use falco_plugin::anyhow::Error;
209/// use falco_plugin::event::events::Event;
210/// use falco_plugin::event::events::EventMetadata;
211/// use falco_plugin::base::Plugin;
212/// use falco_plugin::{async_event_plugin, plugin};
213/// use falco_plugin::async_event::{
214///     AsyncEventPlugin,
215///     AsyncHandler,
216///     BackgroundTask};
217/// use falco_plugin::tables::TablesInput;
218///
219/// struct MyAsyncPlugin {
220///     task: Arc<BackgroundTask>,
221///     thread: Option<JoinHandle<Result<(), Error>>>,
222/// }
223///
224/// impl Plugin for MyAsyncPlugin {
225///     // ...
226/// #    const NAME: &'static CStr = c"sample-plugin-rs";
227/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
228/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
229/// #    const CONTACT: &'static CStr = c"you@example.com";
230/// #    type ConfigType = ();
231/// #
232/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
233/// #        -> Result<Self, Error> {
234/// #        Ok(MyAsyncPlugin {
235/// #            task: Arc::new(Default::default()),
236/// #            thread: None,
237/// #        })
238/// #    }
239/// }
240///
241/// impl AsyncEventPlugin for MyAsyncPlugin {
242///     const ASYNC_EVENTS: &'static [&'static str] = &[]; // generate any async events
243///     const EVENT_SOURCES: &'static [&'static str] = &[]; // attach to all event sources
244///
245///     fn start_async(&mut self, handler: AsyncHandler) -> Result<(), Error> {
246///         // stop the thread if it was already running
247///         if self.thread.is_some() {
248///            self.stop_async()?;
249///         }
250///
251///         // start a new thread
252///         // waiting up to 100ms between events for the stop request
253///         self.thread = Some(self.task.spawn(std::time::Duration::from_millis(100), move || {
254///             // submit an async event to the main event loop
255///             handler.emit(Self::async_event(c"sample_async", b"hello"))?;
256///             Ok(())
257///         })?);
258///         Ok(())
259///     }
260///
261///     fn stop_async(&mut self) -> Result<(), Error> {
262///         self.task.request_stop_and_notify()?;
263///         let Some(handle) = self.thread.take() else {
264///             return Ok(());
265///         };
266///
267///         match handle.join() {
268///             Ok(res) => res,
269///             Err(e) => std::panic::resume_unwind(e),
270///         }
271///     }
272/// }
273///
274/// plugin!(MyAsyncPlugin);
275/// async_event_plugin!(MyAsyncPlugin);
276/// ```
277pub mod async_event {
278    /// The event type that can be emitted from async event plugins
279    pub use falco_event::events::types::PPME_ASYNCEVENT_E as AsyncEvent;
280
281    pub use crate::plugin::async_event::async_handler::AsyncHandler;
282    pub use crate::plugin::async_event::AsyncEventPlugin;
283
284    pub use crate::plugin::async_event::background_task::BackgroundTask;
285}
286
287/// # Event sourcing support
288///
289/// Plugins with event sourcing capability provide a new event source and make it available to
290/// libscap and libsinsp. They have the ability to "open" and "close" a stream of events and return
291/// those events to the plugin framework. They also provide a plugin ID, which is globally unique
292/// and is used in capture files. Event sources provided by plugins with this capability are tied
293/// to the events they generate and can be used by [plugins with field extraction](crate::source)
294/// capabilities and within Falco rules.
295/// For your plugin to support event sourcing, you will need to implement the [`source::SourcePlugin`]
296/// trait and invoke the [`source_plugin`] macro, for example:
297///
298/// ```
299/// use std::ffi::{CStr, CString};
300/// use anyhow::Error;
301/// use falco_event::events::Event;
302/// use falco_plugin::base::{Metric, Plugin};
303/// use falco_plugin::{plugin, source_plugin};
304/// use falco_plugin::source::{
305///     EventBatch,
306///     EventInput,
307///     PluginEvent,
308///     SourcePlugin,
309///     SourcePluginInstance};
310/// use falco_plugin::tables::TablesInput;
311/// use falco_plugin_api::ss_plugin_event_input;
312///
313/// struct MySourcePlugin;
314///
315/// impl Plugin for MySourcePlugin {
316///     // ...
317/// #    const NAME: &'static CStr = c"sample-plugin-rs";
318/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
319/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
320/// #    const CONTACT: &'static CStr = c"you@example.com";
321/// #    type ConfigType = ();
322/// #
323/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
324/// #        -> Result<Self, anyhow::Error> {
325/// #        Ok(MySourcePlugin)
326/// #    }
327/// #
328/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
329/// #        Ok(())
330/// #    }
331/// #
332/// #    fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
333/// #        []
334/// #    }
335/// }
336///
337/// struct MySourcePluginInstance;
338///
339/// impl SourcePlugin for MySourcePlugin {
340///     type Instance = MySourcePluginInstance;
341///     const EVENT_SOURCE: &'static CStr = c"my-source-plugin";
342///     const PLUGIN_ID: u32 = 0; // we do not have one assigned for this example :)
343///
344///     fn open(&mut self, params: Option<&str>) -> Result<Self::Instance, Error> {
345///         // we do not use the open parameters in this example
346///         Ok((MySourcePluginInstance))
347///     }
348///
349///     fn event_to_string(&mut self, event: &EventInput) -> Result<CString, Error> {
350///         // a string representation for our event; just copy out the whole event data
351///         // (it's an ASCII string); please note we need the copy because we need to add
352///         // a NUL terminator to convert the byte buffer to a C string
353///
354///         // get the raw event
355///         let event = event.event()?;
356///         // parse the fields into a PluginEvent
357///         let plugin_event = event.load::<PluginEvent>()?;
358///
359///         // take a copy of the event data (it's in an Option because we never know if events
360///         // have all the fields, and it's important to handle short events for backwards
361///         // compatibility).
362///         let data = plugin_event.params.event_data.map(|e| e.to_vec()).unwrap_or_default();
363///
364///         // convert the data to a CString and return it
365///         Ok(CString::new(data)?)
366///     }
367/// }
368///
369/// impl SourcePluginInstance for MySourcePluginInstance {
370///     type Plugin = MySourcePlugin;
371///
372///     fn next_batch(&mut self, plugin: &mut Self::Plugin, batch: &mut EventBatch)
373///     -> Result<(), Error> {
374///         let event = Self::plugin_event(b"hello, world");
375///         batch.add(event)?;
376///
377///         Ok(())
378///     }}
379///
380/// plugin!(MySourcePlugin);
381/// source_plugin!(MySourcePlugin);
382/// ```
383pub mod source {
384    pub use crate::plugin::event::EventInput;
385    pub use crate::plugin::source::event_batch::EventBatch;
386    pub use crate::plugin::source::open_params::{serialize_open_params, OpenParam};
387    pub use crate::plugin::source::{ProgressInfo, SourcePlugin, SourcePluginInstance};
388    pub use falco_event::events::types::PPME_PLUGINEVENT_E as PluginEvent;
389}
390
391/// # Capture listening plugins
392///
393/// Plugins with capture listening capability can receive notifications whenever a capture is
394/// started or stopped. Note that a capture may be stopped and restarted multiple times
395/// over the lifetime of a plugin.
396///
397/// ## Background tasks
398///
399/// Capture listening plugins receive a reference to a thread pool, which can be used to submit
400/// "routines" (tasks running in a separate thread, effectively).
401///
402/// *Note* there is no built-in mechanism to stop a running routine, so you should avoid doing this
403/// in the routine:
404/// ```ignore
405/// loop {
406///     do_something();
407///     std::thread::sleep(some_time);
408/// }
409/// ```
410///
411/// Instead, have your routine just do a single iteration and request a rerun from the scheduler:
412/// ```ignore
413/// do_something();
414/// std::thread::sleep(some_time)
415/// std::ops::ControlFlow::Continue(())
416/// ```
417///
418/// If you insist on using an infinite loop inside a routine, consider using e.g. [`async_event::BackgroundTask`]
419/// to manage the lifetime of the routine.
420///
421/// For your plugin to support event parsing, you will need to implement the [`listen::CaptureListenPlugin`]
422/// trait and invoke the [`capture_listen_plugin`] macro, for example:
423///
424/// ```
425///# use std::ffi::CStr;
426///# use std::time::Duration;
427/// use falco_plugin::anyhow::Error;
428/// use falco_plugin::base::Plugin;
429/// use falco_plugin::{capture_listen_plugin, plugin};
430/// use falco_plugin::listen::{CaptureListenInput, CaptureListenPlugin, Routine};
431///# use falco_plugin::tables::TablesInput;
432///# use log;
433///
434/// struct MyListenPlugin {
435///     tasks: Vec<Routine>,
436/// }
437///
438/// impl Plugin for MyListenPlugin {
439///     // ...
440/// #    const NAME: &'static CStr = c"sample-plugin-rs";
441/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
442/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
443/// #    const CONTACT: &'static CStr = c"you@example.com";
444/// #    type ConfigType = ();
445/// #
446/// #    fn new(input: Option<&TablesInput>, config: Self::ConfigType)
447/// #        -> Result<Self, Error> {
448/// #        Ok(MyListenPlugin {
449/// #             tasks: Vec::new(),
450/// #        })
451/// #    }
452/// }
453///
454/// impl CaptureListenPlugin for MyListenPlugin {
455///     fn capture_open(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> {
456///         log::info!("Capture started");
457///         self.tasks.push(listen_input.thread_pool.subscribe(|| {
458///             log::info!("Doing stuff in the background");
459///             std::thread::sleep(Duration::from_millis(500));
460///             std::ops::ControlFlow::Continue(())
461///         })?);
462///
463///         Ok(())
464///     }
465///
466/// fn capture_close(&mut self, listen_input: &CaptureListenInput) -> Result<(), Error> {
467///         log::info!("Capture stopped");
468///         for routine in self.tasks.drain(..) {
469///             listen_input.thread_pool.unsubscribe(&routine)?;
470///         }
471///
472///         Ok(())
473///     }
474/// }
475///
476/// plugin!(MyListenPlugin);
477/// capture_listen_plugin!(MyListenPlugin);
478/// ```
479pub mod listen {
480    pub use crate::plugin::listen::CaptureListenInput;
481    pub use crate::plugin::listen::CaptureListenPlugin;
482
483    pub use crate::plugin::listen::routine::Routine;
484    pub use crate::plugin::listen::routine::ThreadPool;
485}
486
487/// # Creating and accessing tables
488///
489/// Tables are a mechanism to share data between plugins (and Falco core). There are three major
490/// concepts that relate to working with Falco plugin tables:
491/// - a table is a collection of entries, each under a different key, like a hash map or a SQL
492///   table with a single primary key
493/// - an entry is a struct containing the actual values (corresponding to an entry in the hash map
494///   or a row in the SQL table)
495/// - a field is a descriptor for a particular item in an entry. It does not have an equivalent
496///   in the hash map analogy, but corresponds to a column in the SQL table. In particular, a field
497///   is not attached to any particular entry.
498///
499/// ## Example (in pseudocode)
500///
501/// Consider a table called `threads` that has two fields:
502/// ```ignore
503/// struct Thread {
504///     uid: u64,
505///     comm: CString,
506/// }
507/// ```
508///
509/// and uses the thread id (`tid: u64`) as the key. To read the `comm` of the thread with tid 1,
510/// you would need the following operations:
511///
512/// ```ignore
513/// // get the table (at initialization time)
514/// let threads_table = get_table("threads");
515///
516/// // get the field (at initialization time)
517/// let comm_field = threads_table.get_field("comm");
518///
519/// // get an entry in the table (during parsing or extraction)
520/// let tid1_entry = threads_table.get_entry(1);
521///
522/// // get the field value from an entry
523/// let comm = tid1_entry.get_field_value(comm_field);
524/// ```
525///
526/// The Rust SDK tries to hide this and expose a more struct-oriented approach, though you can
527/// access fields in entries manually if you want (e.g. if you only know the field name at runtime).
528///
529/// # Supported field types
530///
531/// The following types can be used in fields visible over the plugin API:
532/// - integer types (u8/i8, u16/i16, u32/i32, u64/i64)
533/// - the bool type
534/// - CString
535///
536/// Any other types are not supported, including in particular e.g. collections (`Vec<T>`),
537/// enums or any structs.
538///
539/// # Nested tables
540///
541/// Fields can also have a table type. This amounts to nested tables, like:
542/// ```ignore
543/// let fd_type = threads[tid].file_descriptors[fd].fd_type;
544/// ```
545///
546/// One important limitation is that you cannot add a nested table at runtime, so the only
547/// nested tables that exist are defined by the plugin (or Falco core) which owns the parent table.
548///
549/// # Exporting and importing tables
550///
551/// Tables can be exported (exposed to other plugins) using the [`tables::export`] module.
552///
553/// Existing tables (from other plugins) can be imported using the [`tables::import`] module.
554///
555/// See the corresponding modules' documentation for details.
556///
557/// # Access control
558///
559/// Not all plugins are created equal when it comes to accessing tables. Only
560/// [parse plugins](`crate::parse::ParsePlugin`), [listen plugins](`crate::listen::CaptureListenPlugin`)
561/// and [extract plugins](`crate::extract::ExtractPlugin`) can access tables. Moreover, during field
562/// extraction you can only read tables, not write them.
563///
564/// To summarize:
565///
566/// | Plugin type | Initialization phase | Action phase ^1 |
567/// |-------------|----------------------|-----------------|
568/// | source      | no access            | no access       |
569/// | parse       | full access          | read/write      |
570/// | extract     | full access ^2       | read only       |
571/// | listen      | full access          | n/a ^3          |
572/// | async       | no access            | no access       |
573///
574/// **Notes**:
575/// 1. "Action phase" is anything that happens after [`crate::base::Plugin::new`] returns, i.e.
576///    event generation, parsing/extraction or any background activity (in async plugins).
577///
578/// 2. Even though you can create tables and fields during initialization of an extract plugin,
579///    there's no way to modify them later (create table entries or write to fields), so it's
580///    more useful to constrain yourself to looking up existing tables/fields.
581///
582/// 3. Listen plugins don't really have an action phase as they only expose methods to run
583///    on capture start/stop. The routines they spawn cannot access tables, since the table
584///    API is explicitly not thread safe (but with the `thread-safe-tables` feature you can
585///    safely access tables from Rust plugins across many threads).
586///
587/// ## Access control implementation
588///
589/// Access control is implemented by requiring a particular object to actually perform table
590/// operations:
591/// - [`tables::TablesInput`] to manage (look up/create) tables and fields
592/// - [`tables::TableReader`] to look up table entries and get field values
593/// - [`tables::TableWriter`] to create entries and write field values
594///
595/// These get passed to your plugin whenever a particular class of operations is allowed.
596/// Note that [`crate::base::Plugin::new`] receives an `Option<&TablesInput>` and the option
597/// is populated only for parsing and extraction plugins (source and async plugins receive `None`).
598///
599/// # The flow of using tables
600///
601/// The access controls described above push you into structuring your plugins in a specific way.
602/// You cannot e.g. define tables in a source plugin, which is good, since that would break
603/// when reading capture files (the source plugin is not involved in that case). To provide
604/// a full-featured plugin that generates events, maintains some state and exposes it via
605/// extracted fields, you need separate capabilities (that may live in a single plugin or be
606/// spread across different ones):
607/// - a source plugin *only* generates events
608/// - a parse plugin creates the state tables and updates them during event parsing
609/// - an extract plugin reads the tables and returns field values
610///
611/// # Dynamic fields
612///
613/// Tables can have fields added to them at runtime, from other plugins than the one that
614/// created them (you can add dynamic fields to tables you created too, but that makes little sense).
615///
616/// These fields behave just like fields defined statically in the table and can be used by plugins
617/// loaded after the current one. This can be used to e.g. add some data to an existing table
618/// in a parse plugin and expose it in an extract plugin.
619///
620/// # Thread safety
621///
622/// Tables in the Falco plugin API are explicitly *not* thread safe. However, when you enable
623/// the `thread-safe-tables` feature, tables exported from your plugin become thread-safe, so you
624/// can use them from your plugin (e.g. in a separate thread) concurrently to other plugins
625/// (in the main thread).
626pub mod tables {
627    pub use crate::plugin::tables::vtable::reader::LazyTableReader;
628    pub use crate::plugin::tables::vtable::reader::TableReader;
629    pub use crate::plugin::tables::vtable::reader::ValidatedTableReader;
630    pub use crate::plugin::tables::vtable::writer::LazyTableWriter;
631    pub use crate::plugin::tables::vtable::writer::TableWriter;
632    pub use crate::plugin::tables::vtable::writer::ValidatedTableWriter;
633    pub use crate::plugin::tables::vtable::TablesInput;
634
635    /// Exporting tables to other plugins
636    ///
637    /// Exporting a table to other plugins is done using the [`crate::tables::export::Entry`] derive macro.
638    /// It lets you use a struct type as a parameter to [`export::Table`]. You can then create
639    /// a new table using [`TablesInput::add_table`].
640    ///
641    /// Every field in the entry struct must be wrapped in [`Public`](`crate::tables::export::Public`),
642    /// [`Private`](`crate::tables::export::Private`) or [`Readonly`](`crate::tables::export::Readonly`),
643    /// except for nested tables. These just need to be a `Box<Table<K, E>>`, as it makes no sense
644    /// to have a private nested table and the distinction between writable and readonly is meaningless
645    /// for tables (they have no setter to replace the whole table and you can always add/remove
646    /// entries from the nested table).
647    ///
648    /// # Example
649    ///
650    /// ```
651    /// use std::ffi::{CStr, CString};
652    /// use falco_plugin::base::Plugin;
653    ///# use falco_plugin::plugin;
654    /// use falco_plugin::tables::TablesInput;
655    /// use falco_plugin::tables::export;
656    ///
657    /// // define the struct representing each table entry
658    /// #[derive(export::Entry)]
659    /// struct ExportedTable {
660    ///     int_field: export::Readonly<u64>,      // do not allow writes via the plugin API
661    ///     string_field: export::Public<CString>, // allow writes via the plugin API
662    ///     secret: export::Private<Vec<u8>>,      // do not expose over the plugin API at all
663    /// }
664    ///
665    /// // define the type holding the plugin state
666    /// struct MyPlugin {
667    ///     // you can use methods on this instance to access fields bypassing the Falco table API
668    ///     // (for performance within your own plugin)
669    ///     exported_table: Box<export::Table<u64, ExportedTable>>,
670    /// }
671    ///
672    /// // implement the base::Plugin trait
673    /// impl Plugin for MyPlugin {
674    ///     // ...
675    ///#     const NAME: &'static CStr = c"sample-plugin-rs";
676    ///#     const PLUGIN_VERSION: &'static CStr = c"0.0.1";
677    ///#     const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
678    ///#     const CONTACT: &'static CStr = c"you@example.com";
679    ///#     type ConfigType = ();
680    ///
681    ///     fn new(input: Option<&TablesInput>, config: Self::ConfigType)
682    ///         -> Result<Self, anyhow::Error> {
683    ///
684    ///         let Some(input) = input else {
685    ///             anyhow::bail!("Did not get tables input");
686    ///         };
687    ///
688    ///         // create a new table called "exported"
689    ///         //
690    ///         // The concrete type is inferred from the field type the result is stored in.
691    ///         let exported_table = input.add_table(export::Table::new(c"exported")?)?;
692    ///
693    ///         Ok(MyPlugin { exported_table })
694    ///     }
695    /// }
696    ///# plugin!(#[no_capabilities] MyPlugin);
697    /// ```
698    pub mod export {
699        pub use crate::plugin::exported_tables::field::private::Private;
700        pub use crate::plugin::exported_tables::field::public::Public;
701        pub use crate::plugin::exported_tables::field::readonly::Readonly;
702        pub use crate::plugin::exported_tables::table::Table;
703
704        /// Mark a struct type as a table value
705        ///
706        /// See the [module documentation](`crate::tables::export`) for details.
707        pub use falco_plugin_derive::Entry;
708    }
709
710    /// # Importing tables from other plugins (or Falco core)
711    ///
712    /// Your plugin can access tables exported by other plugins (or Falco core) by importing them.
713    /// The recommended approach is to use the `#[derive(TableMetadata)]` macro for that purpose.
714    ///
715    /// You will probably want to define two additional type aliases, so that the full definition
716    /// involves:
717    /// - a type alias for the whole table
718    /// - a type alias for a single table entry
719    /// - a metadata struct, describing an entry (somewhat indirectly)
720    ///
721    /// For example:
722    ///
723    /// ```
724    /// # use std::ffi::CStr;
725    /// # use std::sync::Arc;
726    /// # use falco_plugin::tables::import::{Entry, Field, Table, TableMetadata};
727    /// #
728    /// type NestedThing = Entry<Arc<NestedThingMetadata>>;
729    /// type NestedThingTable = Table<u64, NestedThing>;
730    ///
731    /// #[derive(TableMetadata)]
732    /// #[entry_type(NestedThing)]
733    /// struct NestedThingMetadata {
734    ///     number: Field<u64, NestedThing>,
735    /// }
736    ///
737    /// type ImportedThing = Entry<Arc<ImportedThingMetadata>>;
738    /// type ImportedThingTable = Table<u64, ImportedThing>;
739    ///
740    /// #[derive(TableMetadata)]
741    /// #[entry_type(ImportedThing)]
742    /// struct ImportedThingMetadata {
743    ///     imported: Field<u64, ImportedThing>,
744    ///     nested: Field<NestedThingTable, ImportedThing>,
745    ///
746    ///     #[name(c"type")]
747    ///     thing_type: Field<u64, ImportedThing>,
748    ///
749    ///     #[custom]
750    ///     added: Field<CStr, ImportedThing>,
751    /// }
752    ///
753    /// # // make this doctest a module, not a function: https://github.com/rust-lang/rust/issues/83583#issuecomment-1083300448
754    /// # fn main() {}
755    /// ```
756    ///
757    /// In contrast to [exported tables](`crate::tables::export`), the entry struct does not
758    /// contain any accessible fields. It only provides generated methods to access each field
759    /// using the plugin API. This means that each read/write is fairly expensive (involves
760    /// method calls), so you should probably cache the values in local variables.
761    ///
762    /// ## Declaring fields
763    ///
764    /// You need to declare each field you're going to use in a particular table, by providing
765    /// a corresponding [`import::Field`] field in the metadata struct. You do **not** need
766    /// to declare all fields in the table, or put the fields in any particular order, but you
767    /// **do** need to get the type right (otherwise you'll get an error at initialization time).
768    ///
769    /// The Falco table field name is the same as the field name in your metadata struct,
770    /// unless overridden by `#[name(c"foo")]`. This is useful if a field's name is a Rust reserved
771    /// word (e.g. `type`).
772    ///
773    /// You can also add fields to imported tables. To do that, tag the field with a `#[custom]`
774    /// attribute. It will be then added to the table instead of looking it up in existing fields.
775    /// Note that multiple plugins can add a field with the same name and type, which will make them
776    /// all use the same field (they will share the data). Adding a field multiple times
777    /// with different types is not allowed and will cause an error at initialization time.
778    ///
779    /// ## Generated methods
780    ///
781    /// Each scalar field gets a getter and setter method, e.g. declaring a metadata struct like
782    /// the above example will generate the following methods **on the `ImportedThing` type**
783    /// (for the scalar fields):
784    ///
785    /// ```ignore
786    /// fn get_imported(&self, reader: &TableReader) -> Result<u64, anyhow::Error>;
787    /// fn set_imported(&self, writer: &TableWriter, value: &u64) -> Result<(), anyhow::Error>;
788    ///
789    /// fn get_thing_type(&self, reader: &TableReader) -> Result<u64, anyhow::Error>;
790    /// fn set_thing_type(&self, writer: &TableWriter, value: &u64) -> Result<(), anyhow::Error>;
791    ///
792    /// fn get_added<'a>(&'a self, reader: &TableReader) -> Result<&'a CStr, anyhow::Error>;
793    /// fn set_added(&self, writer: &TableWriter, value: &CStr) -> Result<(), anyhow::Error>;
794    /// ```
795    ///
796    /// Each table-typed field (nested table) gets a getter and a nested getter, so the above example
797    /// will generate the following methods for the `nested` field:
798    ///
799    /// ```ignore
800    /// fn get_nested(&self, reader: &TableReader) -> Result<NestedThingTable, anyhow::Error>;
801    /// fn get_nested_by_key(&self, reader: &TableReader, key: &u64)
802    ///     -> Result<NestedThing, anyhow::Error>;
803    /// ```
804    ///
805    /// **Note**: setters do not take `&mut self` as all the mutation happens on the other side
806    /// of the API (presumably in another plugin).
807    ///
808    /// ### Visibility of generated methods
809    ///
810    /// The generated methods are actually trait implementations, not inherent impls (due to proc
811    /// macro limitations). The traits in question are automatically generated and `use`d
812    /// in the scope that defines the table struct. However, when you try to use them from
813    /// a different module, you will get an error since they're not in scope (as traits need to
814    /// be `use`d explicitly before their methods can be called).
815    ///
816    /// The module they're defined in has an autogenerated name (`__falco_plugin_private_<TABLE_TYPE_NAME>`)
817    /// that doesn't look too nice when explicitly used, so for larger plugins (where the table
818    /// access is split across modules) you can use the `#[accessors_mod]` attribute to choose
819    /// a friendlier name for this module:
820    ///
821    /// ```
822    /// pub mod imported_table {
823    /// #     use std::sync::Arc;
824    /// #     use falco_plugin::tables::import::{Entry, Field, Table, TableMetadata};
825    /// #
826    ///     pub type ImportedThing = Entry<Arc<ImportedThingMetadata>>;
827    ///     type ImportedThingTable = Table<u64, ImportedThing>;
828    ///
829    ///     #[derive(TableMetadata)]
830    ///     #[entry_type(ImportedThing)]
831    ///     #[accessors_mod(thing_accessors)]
832    ///     pub struct ImportedThingMetadata {
833    ///         imported: Field<u64, ImportedThing>,
834    ///     }
835    /// }
836    ///
837    /// mod use_the_table {
838    /// #     use falco_plugin::tables::TableReader;
839    /// #     use falco_plugin::anyhow;
840    ///     use super::imported_table::ImportedThing;
841    ///     use super::imported_table::thing_accessors::*;
842    ///
843    ///     fn use_the_table(thing: &ImportedThing, reader: &impl TableReader)
844    ///         -> Result<u64, anyhow::Error> {
845    ///         thing.get_imported(reader)
846    ///     }
847    /// }
848    ///
849    /// # // make this doctest a module, not a function: https://github.com/rust-lang/rust/issues/83583#issuecomment-1083300448
850    /// # fn main() {}
851    ///
852    /// ```
853    ///
854    /// # Example
855    ///
856    /// ```
857    /// use std::ffi::CStr;
858    /// use std::sync::Arc;
859    /// use falco_plugin::anyhow::Error;
860    /// use falco_plugin::base::Plugin;
861    /// use falco_plugin::event::events::types::EventType;
862    /// use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin};
863    /// use falco_plugin::tables::TablesInput;
864    /// use falco_plugin::tables::import::{Entry, Field, Table, TableMetadata};
865    ///# use falco_plugin::plugin;
866    ///# use falco_plugin::parse_plugin;
867    ///
868    /// #[derive(TableMetadata)]
869    /// #[entry_type(ImportedThing)]
870    /// struct ImportedThingMetadata {
871    ///     imported: Field<u64, ImportedThing>,
872    ///
873    ///     #[name(c"type")]
874    ///     thing_type: Field<u64, ImportedThing>,
875    ///
876    ///     #[custom]
877    ///     added: Field<CStr, ImportedThing>,
878    /// }
879    ///
880    /// type ImportedThing = Entry<Arc<ImportedThingMetadata>>;
881    /// type ImportedThingTable = Table<u64, ImportedThing>;
882    ///
883    /// struct MyPlugin {
884    ///     things: ImportedThingTable,
885    /// }
886    ///
887    /// impl Plugin for MyPlugin {
888    ///     // ...
889    ///#     const NAME: &'static CStr = c"dummy_extract";
890    ///#     const PLUGIN_VERSION: &'static CStr = c"0.0.0";
891    ///#     const DESCRIPTION: &'static CStr = c"test plugin";
892    ///#     const CONTACT: &'static CStr = c"rust@localdomain.pl";
893    ///#     type ConfigType = ();
894    ///
895    ///     fn new(input: Option<&TablesInput>, _config: Self::ConfigType) -> Result<Self, Error> {
896    ///         let input = input.ok_or_else(|| anyhow::anyhow!("did not get table input"))?;
897    ///         let things: ImportedThingTable = input.get_table(c"things")?;
898    ///
899    ///         Ok(Self { things })
900    ///     }
901    /// }
902    ///
903    /// impl ParsePlugin for MyPlugin {
904    ///     const EVENT_TYPES: &'static [EventType] = &[];
905    ///     const EVENT_SOURCES: &'static [&'static str] = &[];
906    ///
907    ///     fn parse_event(&mut self, event: &EventInput, parse_input: &ParseInput)
908    ///         -> anyhow::Result<()> {
909    ///         // creating and accessing entries
910    ///         let reader = &parse_input.reader;
911    ///         let writer = &parse_input.writer;
912    ///
913    ///         // create a new entry (not yet attached to a table key)
914    ///         let entry = self.things.create_entry(writer)?;
915    ///         entry.set_imported(writer, &5u64)?;
916    ///
917    ///         // attach the entry to a table key
918    ///         self.things.insert(reader, writer, &1u64, entry)?;
919    ///
920    ///         // look up the entry we have just added
921    ///         let entry = self.things.get_entry(reader, &1u64)?;
922    ///         assert_eq!(entry.get_imported(reader).ok(), Some(5u64));
923    ///
924    ///         Ok(())
925    ///     }
926    /// }
927    ///
928    /// # plugin!(MyPlugin);
929    /// # parse_plugin!(MyPlugin);
930    /// # // make this doctest a module, not a function: https://github.com/rust-lang/rust/issues/83583#issuecomment-1083300448
931    /// # fn main() {}
932    /// ```
933    ///
934    /// **Note**: The derive macro involves creating a private module (to avoid polluting
935    /// the top-level namespace with a bunch of one-off traits), so you cannot use it inside
936    /// a function due to scoping issues. See <https://github.com/rust-lang/rust/issues/83583>
937    /// for details.
938    ///
939    /// # Bypassing the derive macro
940    ///
941    /// The derive macro boils down to automatically calling get_field/add_field for each
942    /// field defined in the metadata struct (and generating getters/setters). If you don't know
943    /// the field names in advance (e.g. when supporting different versions of "parent" plugins),
944    /// there is the [`import::RuntimeEntry`] type alias, which makes you responsible for holding
945    /// the field structs (probably in your plugin type) and requires you to use the generic
946    /// read_field/write_field methods, in exchange for the flexibility.
947    ///
948    /// The above example can be rewritten without the derive macro as follows:
949    ///
950    /// ```
951    /// use std::ffi::CStr;
952    /// use falco_plugin::anyhow::Error;
953    /// use falco_plugin::base::Plugin;
954    /// use falco_plugin::event::events::types::EventType;
955    /// use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin};
956    ///# use falco_plugin::{parse_plugin, plugin};
957    /// use falco_plugin::tables::TablesInput;
958    /// use falco_plugin::tables::import::{Field, RuntimeEntry, Table};
959    ///
960    /// struct ImportedThingTag;
961    /// type ImportedThing = RuntimeEntry<ImportedThingTag>;
962    /// type ImportedThingTable = Table<u64, ImportedThing>;
963    ///
964    /// struct MyPlugin {
965    ///     things: ImportedThingTable,
966    ///     thing_imported_field: Field<u64, ImportedThing>,
967    ///     thing_type_field: Field<u64, ImportedThing>,
968    ///     thing_added_field: Field<CStr, ImportedThing>,
969    /// }
970    ///
971    /// impl Plugin for MyPlugin {
972    ///     // ...
973    ///#     const NAME: &'static CStr = c"dummy_extract";
974    ///#     const PLUGIN_VERSION: &'static CStr = c"0.0.0";
975    ///#     const DESCRIPTION: &'static CStr = c"test plugin";
976    ///#     const CONTACT: &'static CStr = c"rust@localdomain.pl";
977    ///#     type ConfigType = ();
978    ///
979    ///     fn new(input: Option<&TablesInput>, _config: Self::ConfigType) -> Result<Self, Error> {
980    ///         let input = input.ok_or_else(|| anyhow::anyhow!("did not get table input"))?;
981    ///         let things: ImportedThingTable = input.get_table(c"things")?;
982    ///         let thing_imported_field = things.get_field(input, c"imported")?;
983    ///         let thing_type_field = things.get_field(input, c"type")?;
984    ///         let thing_added_field = things.add_field(input, c"added")?;
985    ///
986    ///         Ok(Self {
987    ///             things,
988    ///             thing_imported_field,
989    ///             thing_type_field,
990    ///             thing_added_field,
991    ///         })
992    ///     }
993    /// }
994    ///
995    /// impl ParsePlugin for MyPlugin {
996    ///     const EVENT_TYPES: &'static [EventType] = &[];
997    ///     const EVENT_SOURCES: &'static [&'static str] = &[];
998    ///
999    ///     fn parse_event(&mut self, event: &EventInput, parse_input: &ParseInput)
1000    ///         -> anyhow::Result<()> {
1001    ///         // creating and accessing entries
1002    ///         let reader = &parse_input.reader;
1003    ///         let writer = &parse_input.writer;
1004    ///
1005    ///         // create a new entry (not yet attached to a table key)
1006    ///         let entry = self.things.create_entry(writer)?;
1007    ///         entry.write_field(writer, &self.thing_imported_field, &5u64)?;
1008    ///
1009    ///         // attach the entry to a table key
1010    ///         self.things.insert(reader, writer, &1u64, entry)?;
1011    ///
1012    ///         // look up the entry we have just added
1013    ///         let entry = self.things.get_entry(reader, &1u64)?;
1014    ///         assert_eq!(
1015    ///             entry.read_field(reader, &self.thing_imported_field).ok(),
1016    ///             Some(5u64),
1017    ///         );
1018    ///
1019    ///         Ok(())
1020    ///     }
1021    /// }
1022    ///# plugin!(MyPlugin);
1023    ///# parse_plugin!(MyPlugin);
1024    /// ```
1025    ///
1026    /// **Note**: in the above example, `ImportedThingTag` is just an empty struct, used to
1027    /// distinguish entries (and fields) from different types between one another. You can
1028    /// skip this and do not pass the second generic argument to `Field` and `Table`
1029    /// (it will default to `RuntimeEntry<()>`), but you lose compile time validation for
1030    /// accessing fields from the wrong table. It will still be caught at runtime.
1031    ///
1032    /// See the [`import::Table`] type for additional methods on tables, to e.g. iterate
1033    /// over entries or clear the whole table.
1034    pub mod import {
1035        pub use crate::plugin::tables::data::Bool;
1036        pub use crate::plugin::tables::data::TableData;
1037        pub use crate::plugin::tables::field::Field;
1038        pub use crate::plugin::tables::runtime::RuntimeEntry;
1039        pub use crate::plugin::tables::table::Table;
1040        pub use crate::plugin::tables::Entry;
1041
1042        /// Mark a struct type as an imported table entry metadata
1043        ///
1044        /// See the [module documentation](`crate::tables::import`) for details.
1045        pub use falco_plugin_derive::TableMetadata;
1046    }
1047}
1048
1049mod plugin;
1050pub mod strings;
1051
1052#[doc(hidden)]
1053pub mod internals {
1054    pub mod base {
1055        pub use crate::plugin::base::wrappers;
1056    }
1057
1058    pub mod source {
1059        pub use crate::plugin::source::wrappers;
1060    }
1061
1062    pub mod extract {
1063        pub use crate::plugin::extract::wrappers;
1064    }
1065
1066    pub mod listen {
1067        pub use crate::plugin::listen::wrappers;
1068    }
1069
1070    pub mod parse {
1071        pub use crate::plugin::parse::wrappers;
1072    }
1073
1074    pub mod async_event {
1075        pub use crate::plugin::async_event::wrappers;
1076    }
1077
1078    pub mod tables {
1079        crate::table_import_expose_internals!();
1080        crate::table_export_expose_internals!();
1081    }
1082}