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