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}