falco_plugin/tables/import/table/
mod.rs

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