diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index 67097e5edb..9a50655038 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -344,6 +344,11 @@ extern "C" { node: *const SCConfNode, key: *const ::std::os::raw::c_char, ) -> *const ::std::os::raw::c_char; } +extern "C" { + pub fn SCConfNodeLookupInSequence( + seq: *const SCConfNode, key: *const ::std::os::raw::c_char, prev: *const SCConfNode, + ) -> *mut SCConfNode; +} extern "C" { pub fn SCConfNodeRemove(arg1: *mut SCConfNode); } diff --git a/src/conf.c b/src/conf.c index 04486b6ee4..c8271eef39 100644 --- a/src/conf.c +++ b/src/conf.c @@ -96,6 +96,18 @@ SCConfNode *SCConfNodeGetNodeOrCreate(SCConfNode *parent, const char *name, int node->parent = parent; node->final = final; TAILQ_INSERT_TAIL(&parent->head, node, next); + + /* Numeric-only key implies the parent is a sequence (yaml + * "- foo" produces the same shape). This lets --set style + * overrides populate list-typed config nodes. */ + if (parent != NULL) { + const char *p = key; + while (*p != '\0' && isdigit((unsigned char)*p)) + p++; + if (*p == '\0' && p != key) { + parent->is_seq = 1; + } + } } key = next; parent = node; @@ -885,6 +897,48 @@ const char *SCConfNodeLookupChildValue(const SCConfNode *node, const char *name) return NULL; } +/** + * \brief Find a named entry inside a sequence node. + * + * A YAML sequence of single key maps -- the shape used by "outputs" -- + * stores each entry as an unnamed node holding one child, so an entry is + * found at seq.. and not at seq.. This walks the sequence and + * returns the child named \a key. + * + * A sequence may hold several entries with the same name (several eve-log + * outputs for instance), so \a prev allows iterating over all of them: + * pass NULL to get the first match, then the previous result to get the + * next one. + * + * \param seq The sequence node, may be NULL. + * \param key The name of the entry to look for. + * \param prev NULL to start, or the previously returned node to continue. + * + * \retval The matching SCConfNode or NULL when there is no (further) match. + */ +SCConfNode *SCConfNodeLookupInSequence( + const SCConfNode *seq, const char *key, const SCConfNode *prev) +{ + if (seq == NULL || key == NULL) + return NULL; + + bool seen = (prev == NULL); + SCConfNode *entry; + TAILQ_FOREACH (entry, &seq->head, next) { + SCConfNode *child = SCConfNodeLookupChild(entry, key); + if (child == NULL) + continue; + if (!seen) { + if (child == prev) + seen = true; + continue; + } + return child; + } + + return NULL; +} + /** * \brief Lookup for a key value under a specific node * diff --git a/src/conf.h b/src/conf.h index 34f1c0709f..9cf84b53a2 100644 --- a/src/conf.h +++ b/src/conf.h @@ -81,6 +81,8 @@ void SCConfCreateContextBackup(void); void SCConfRestoreContextBackup(void); SCConfNode *SCConfNodeLookupChild(const SCConfNode *node, const char *key); const char *SCConfNodeLookupChildValue(const SCConfNode *node, const char *key); +SCConfNode *SCConfNodeLookupInSequence( + const SCConfNode *seq, const char *key, const SCConfNode *prev); void SCConfNodeRemove(SCConfNode *); void SCConfRegisterTests(void); int SCConfNodeChildValueIsTrue(const SCConfNode *node, const char *key);