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