falco_plugin/plugin/tables/table/
mod.rs

1use crate::plugin::tables::data::{seal, FieldTypeId, Key, TableData, Value};
2use crate::plugin::tables::field::Field;
3use crate::plugin::tables::runtime::NoMetadata;
4use crate::plugin::tables::runtime_table_validator::RuntimeTableValidator;
5use crate::plugin::tables::table::raw::RawTable;
6use crate::plugin::tables::traits::{Entry, TableAccess, TableMetadata};
7use crate::plugin::tables::vtable::fields::TableFields;
8use crate::plugin::tables::vtable::reader::TableReader;
9use crate::plugin::tables::vtable::writer::TableWriter;
10use crate::plugin::tables::vtable::TablesInput;
11use anyhow::Error;
12use falco_plugin_api::{ss_plugin_state_data, ss_plugin_table_field_t, ss_plugin_table_fieldinfo};
13use std::ffi::CStr;
14use std::marker::PhantomData;
15use std::ops::ControlFlow;
16
17pub(in crate::plugin::tables) mod raw;
18
19/// # A table imported via the Falco plugin API
20#[derive(Debug)]
21pub struct Table<K, E = super::entry::Entry<NoMetadata<()>>, M = <E as Entry>::Metadata> {
22    pub(in crate::plugin::tables) raw_table: RawTable,
23    pub(in crate::plugin::tables) metadata: M,
24    pub(in crate::plugin::tables) is_nested: bool,
25    pub(in crate::plugin::tables) key_type: PhantomData<K>,
26    pub(in crate::plugin::tables) entry_type: PhantomData<E>,
27}
28
29impl<K, E, M> TableAccess for Table<K, E, M>
30where
31    K: Key,
32    E: Entry<Metadata = M>,
33    M: TableMetadata + Clone,
34{
35    type Key = K;
36    type Entry = E;
37    type Metadata = M;
38
39    fn new(raw_table: RawTable, metadata: Self::Metadata, is_nested: bool) -> Self {
40        Self {
41            raw_table,
42            metadata,
43            is_nested,
44            key_type: PhantomData,
45            entry_type: PhantomData,
46        }
47    }
48
49    fn get_entry(
50        &self,
51        reader_vtable: &impl TableReader,
52        key: &Self::Key,
53    ) -> Result<Self::Entry, Error>
54    where
55        Self::Key: Key,
56        Self::Entry: Entry,
57    {
58        Table::get_entry(self, reader_vtable, key)
59    }
60}
61
62impl<K, E, M> Table<K, E, M>
63where
64    K: Key,
65    E: Entry<Metadata = M>,
66    M: TableMetadata + Clone,
67{
68    /// Look up an entry in `table` corresponding to `key`
69    pub fn get_entry(&self, reader_vtable: &impl TableReader, key: &K) -> Result<E, Error> {
70        let raw_entry = self.raw_table.get_entry(reader_vtable, key)?;
71        Ok(E::new(
72            raw_entry,
73            self.raw_table.table,
74            self.metadata.clone(),
75        ))
76    }
77
78    /// Erase a table entry by key
79    pub fn erase(&self, writer_vtable: &impl TableWriter, key: &K) -> Result<(), Error> {
80        unsafe { self.raw_table.erase(writer_vtable, key) }
81    }
82
83    /// Attach an entry to a table key (insert an entry to the table)
84    pub fn insert(
85        &self,
86        reader_vtable: &impl TableReader,
87        writer_vtable: &impl TableWriter,
88        key: &K,
89        entry: E,
90    ) -> Result<E, Error> {
91        let raw_entry = unsafe {
92            self.raw_table
93                .insert(reader_vtable, writer_vtable, key, entry.into_raw())
94        }?;
95        Ok(E::new(
96            raw_entry,
97            self.raw_table.table,
98            self.metadata.clone(),
99        ))
100    }
101}
102
103impl<K, E, M> Table<K, E, M>
104where
105    E: Entry<Metadata = M>,
106    M: TableMetadata + Clone,
107{
108    pub(in crate::plugin::tables) fn new_without_key(
109        raw_table: RawTable,
110        metadata: E::Metadata,
111        is_nested: bool,
112    ) -> Self {
113        Self {
114            raw_table,
115            metadata,
116            is_nested,
117            key_type: PhantomData,
118            entry_type: PhantomData,
119        }
120    }
121
122    pub(in crate::plugin::tables) fn table_validator(&self) -> RuntimeTableValidator {
123        let ptr = if self.is_nested {
124            std::ptr::null_mut()
125        } else {
126            self.raw_table.table
127        };
128        RuntimeTableValidator::new(ptr)
129    }
130
131    /// Create a new table entry (not yet attached to a key)
132    pub fn create_entry(&self, writer_vtable: &impl TableWriter) -> Result<E, Error> {
133        let raw_entry = self.raw_table.create_entry(writer_vtable)?;
134        Ok(E::new(
135            raw_entry,
136            self.raw_table.table,
137            self.metadata.clone(),
138        ))
139    }
140
141    /// Remove all entries from the table
142    pub fn clear(&self, writer_vtable: &impl TableWriter) -> Result<(), Error> {
143        self.raw_table.clear(writer_vtable)
144    }
145
146    /// # List the available fields
147    ///
148    /// **Note**: this method is of limited utility in actual plugin code (you know the fields you
149    /// want to access), so it returns the unmodified structure from the plugin API, including
150    /// raw pointers to C-style strings. This may change later.
151    pub fn list_fields(&self, fields_vtable: &TableFields) -> &[ss_plugin_table_fieldinfo] {
152        self.raw_table.list_fields(fields_vtable)
153    }
154
155    /// # Get a table field by name
156    ///
157    /// The field must exist in the table and must be of the type `V`, otherwise an error
158    /// will be returned.
159    ///
160    /// Note that for regular tables the field objects remembers the table it was retrieved from
161    /// and accessing an entry from a different table will cause an error at runtime.
162    /// However, for nested tables there's no such validation in the Rust SDK (because using
163    /// the same fields across different nested tables is critical for supporting nested tables)
164    pub fn get_field<V: Value + ?Sized>(
165        &self,
166        tables_input: &TablesInput,
167        name: &CStr,
168    ) -> Result<Field<V, E>, Error> {
169        let field = self.raw_table.get_field(tables_input, name)?;
170        Ok(Field::new(field, self.table_validator()))
171    }
172
173    /// # Get a nested table field
174    ///
175    /// This method takes a closure and executes it with a nested table as an argument.
176    /// It's used to get (at runtime) field descriptors for nested table fields.
177    ///
178    /// You will usually just use the derive macro which hides all the complexity, but if you
179    /// need to handle nested tables at runtime, you can use this method to get the table field
180    /// and all subfields you need like this:
181    ///
182    /// ```ignore
183    /// // get the parent table
184    /// let thread_table: Table<i64> = input.get_table(c"threads")?;
185    ///
186    /// let (fd_field, fd_type_field) =
187    ///     thread_table.get_table_field(input, c"file_descriptors", |table| {
188    ///         table.get_field(input, c"type")
189    ///     })?;
190    /// ```
191    ///
192    /// The call to `get_table_field` takes a closure, which is passed a reference to
193    /// a sample entry in the nested table (enough to manage its fields) and returns a tuple
194    /// of `(the table field, (whatever_the_closure_returns))`
195    pub fn get_table_field<NK, V, U, F, R>(
196        &self,
197        tables_input: &TablesInput,
198        name: &CStr,
199        func: F,
200    ) -> Result<(Field<V, E>, R), Error>
201    where
202        NK: Key,
203        for<'a> V::AssocData: From<&'a M>,
204        V: Value + ?Sized,
205        U: Entry,
206        U::Metadata: for<'a> From<&'a V::AssocData>,
207        F: FnOnce(&Table<(), U>) -> Result<R, Error>,
208    {
209        let field = self.raw_table.get_field::<V>(tables_input, name)?;
210        let metadata = U::Metadata::from(&field.assoc_data);
211
212        let fields = unsafe {
213            self.raw_table
214                .with_subtable::<NK, _, _>(field.field, tables_input, |subtable| {
215                    let owned = RawTable {
216                        table: subtable.table,
217                    };
218                    let table = Table::new_without_key(owned, metadata, true);
219                    func(&table)
220                })??
221        };
222
223        let field = Field::new(field, self.table_validator());
224        Ok((field, fields))
225    }
226
227    /// # Add a table field
228    ///
229    /// The field will have the specified name and the type is derived from the generic argument.
230    ///
231    /// Note that for regular tables the field objects remembers the table it was retrieved from
232    /// and accessing an entry from a different table will cause an error at runtime.
233    /// However, for nested tables there's no such validation in the Rust SDK (because using
234    /// the same fields across different nested tables is critical for supporting nested tables)
235    pub fn add_field<V: Value<AssocData = ()> + ?Sized>(
236        &self,
237        tables_input: &TablesInput,
238        name: &CStr,
239    ) -> Result<Field<V, E>, Error> {
240        let field = self.raw_table.add_field(tables_input, name)?;
241        Ok(Field::new(field, self.table_validator()))
242    }
243
244    /// # Get the table name
245    ///
246    /// This method returns an error if the name cannot be represented as UTF-8
247    pub fn get_name(&self, reader_vtable: &impl TableReader) -> anyhow::Result<&str> {
248        self.raw_table.get_name(reader_vtable)
249    }
250
251    /// # Get the table size
252    ///
253    /// Return the number of entries in the table
254    pub fn get_size(&self, reader_vtable: &impl TableReader) -> anyhow::Result<usize> {
255        self.raw_table.get_size(reader_vtable)
256    }
257
258    /// # Iterate over all entries in a table with mutable access
259    ///
260    /// The closure is called once for each table entry with a corresponding entry
261    /// object as a parameter.
262    ///
263    /// The iteration stops when either all entries have been processed or the closure returns
264    /// [`ControlFlow::Break`].
265    pub fn iter_entries_mut<F>(
266        &self,
267        reader_vtable: &impl TableReader,
268        mut func: F,
269    ) -> anyhow::Result<ControlFlow<()>>
270    where
271        F: FnMut(&mut E) -> ControlFlow<()>,
272    {
273        self.raw_table.iter_entries_mut(reader_vtable, move |raw| {
274            let mut entry = E::new(raw, self.raw_table.table, self.metadata.clone());
275            func(&mut entry)
276        })
277    }
278}
279
280impl<K, E, M> seal::Sealed for Table<K, E, M> {}
281
282impl<K, E, M> TableData for Table<K, E, M> {
283    const TYPE_ID: FieldTypeId = FieldTypeId::Table;
284
285    fn to_data(&self) -> ss_plugin_state_data {
286        ss_plugin_state_data {
287            table: self.raw_table.table,
288        }
289    }
290}
291
292impl<K, E, M> Value for Table<K, E, M>
293where
294    K: Key + 'static,
295    E: Entry<Metadata = M> + 'static,
296    M: TableMetadata + Clone + 'static,
297{
298    type AssocData = M;
299    type Value<'a>
300        = Self
301    where
302        Self: 'a;
303
304    unsafe fn from_data_with_assoc<'a>(
305        data: &ss_plugin_state_data,
306        assoc: &Self::AssocData,
307    ) -> Self::Value<'a> {
308        let table = unsafe { RawTable { table: data.table } };
309        Table::new(table, assoc.clone(), true)
310    }
311
312    unsafe fn get_assoc_from_raw_table(
313        table: &RawTable,
314        field: *mut ss_plugin_table_field_t,
315        tables_input: &TablesInput,
316    ) -> Result<Self::AssocData, Error> {
317        table.with_subtable::<K, _, _>(field, tables_input, |subtable| {
318            M::new(subtable, tables_input)
319        })?
320    }
321}