]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
output: delayed initialization for custom loggers 13397/head
authorJason Ish <jason.ish@oisf.net>
Thu, 5 Jun 2025 21:41:40 +0000 (15:41 -0600)
committerVictor Julien <victor@inliniac.net>
Sat, 7 Jun 2025 08:36:46 +0000 (10:36 +0200)
When a plugin is first initialized, it is too early to register
transaction loggers. Instead, a plugin can register a callback to be
called when Suricata is ready for outputs like transaction loggers to
be registered.

Likewise for library users, there is a window in SuricataInit where
transaction loggers can be registered that library users don't have
access to. So a lifecycle callback useful here as well.

Ticket #7236

examples/plugins/c-custom-loggers/Makefile.in
examples/plugins/c-custom-loggers/custom-logger.c
src/output.c
src/output.h
src/suricata.c

index a30bc8a385cfab4f865a1f62a93d6b90ad8bde97..d4656d800d756e907d63142320b9269d1be61e50 100644 (file)
@@ -5,7 +5,7 @@
 
 # But as this is an example in the Suricata source tree we'll look for
 # includes in the source tree.
-CPPFLAGS +=     -I@top_srcdir@/src -DHAVE_CONFIG_H
+CPPFLAGS +=     -I@top_srcdir@/src -I@top_srcdir@/rust/gen -DHAVE_CONFIG_H
 
 # Currently the Suricata logging system requires this to be even for
 # plugins.
@@ -26,7 +26,7 @@ distclean: clean
 # Regenerate Makefile on change of Makefile.in since we're not using
 # Makefile.am.
 Makefile: Makefile.in
-       cd @top_builddir@ && ./config.status examples/plugins/c-custom-logger/Makefile
+       cd @top_builddir@ && ./config.status examples/plugins/c-custom-loggers/Makefile
 
 # Dummy rules to satisfy make dist.
 dist distdir:
index c74066faf83d1152e341fa083f33a4ea3e80631e..e2f36f1d843223bdbbf8b57eb26988eb6c250f5c 100644 (file)
@@ -22,6 +22,7 @@
 #include "output-flow.h"
 #include "output-tx.h"
 #include "util-print.h"
+#include "output.h"
 
 static int CustomPacketLogger(ThreadVars *tv, void *thread_data, const Packet *p)
 {
@@ -79,14 +80,12 @@ static int CustomFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
     return 0;
 }
 
-#if 0
 static int CustomDnsLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state,
         void *tx, uint64_t tx_id)
 {
     SCLogNotice("We have a DNS transaction");
     return 0;
 }
-#endif
 
 static TmEcode ThreadInit(ThreadVars *tv, const void *initdata, void **data)
 {
@@ -100,21 +99,20 @@ static TmEcode ThreadDeinit(ThreadVars *tv, void *data)
     return TM_ECODE_OK;
 }
 
-static void Init(void)
+static void OnLoggingReady(void *arg)
 {
     SCOutputRegisterPacketLogger(LOGGER_USER, "custom-packet-logger", CustomPacketLogger,
             CustomPacketLoggerCondition, NULL, ThreadInit, ThreadDeinit);
     SCOutputRegisterFlowLogger(
             "custom-flow-logger", CustomFlowLogger, NULL, ThreadInit, ThreadDeinit);
+    SCOutputRegisterTxLogger(LOGGER_USER, "custom-dns-logger", ALPROTO_DNS, CustomDnsLogger, NULL,
+            -1, -1, NULL, ThreadInit, ThreadDeinit);
+}
 
-    /* Register a custom DNS transaction logger.
-     *
-     * Currently disabled due to https://redmine.openinfosecfoundation.org/issues/7236.
-     */
-#if 0
-    OutputRegisterTxLogger(LOGGER_USER, "custom-dns-logger", ALPROTO_DNS, CustomDnsLogger, NULL, -1,
-            -1, NULL, ThreadInit, ThreadDeinit);
-#endif
+static void Init(void)
+{
+    // Register our callback for when logging is ready.
+    SCRegisterOnLoggingReady(OnLoggingReady, NULL);
 }
 
 const SCPlugin PluginRegistration = {
index a913d31bf3a8f1ecff2567ccb587640051dfa22e..5d3794a4e0e9039ed489939b33604cc59db88340 100644 (file)
@@ -132,6 +132,28 @@ static SCMutex output_file_rotation_mutex = SCMUTEX_INITIALIZER;
 TAILQ_HEAD(, OutputFileRolloverFlag_) output_file_rotation_flags =
     TAILQ_HEAD_INITIALIZER(output_file_rotation_flags);
 
+/**
+ * Callback function to be called when logging is ready.
+ */
+typedef void (*SCOnLoggingReadyCallback)(void *arg);
+
+/**
+ * List entries for callbacks registered to be called when the logging system is
+ * ready. This is useful for both plugins and library users who need to register
+ * application transaction loggers after logging initialization is complete.
+ */
+typedef struct OnLoggingReadyCallbackNode_ {
+    SCOnLoggingReadyCallback callback;
+    void *arg;
+    TAILQ_ENTRY(OnLoggingReadyCallbackNode_) entries;
+} OnLoggingReadyCallbackNode;
+
+/**
+ * The list of callbacks to be called when logging is ready.
+ */
+static TAILQ_HEAD(, OnLoggingReadyCallbackNode_)
+        on_logging_ready_callbacks = TAILQ_HEAD_INITIALIZER(on_logging_ready_callbacks);
+
 void OutputRegisterRootLoggers(void);
 void OutputRegisterLoggers(void);
 
@@ -720,6 +742,49 @@ void OutputNotifyFileRotation(void) {
     SCMutexUnlock(&output_file_rotation_mutex);
 }
 
+/**
+ * \brief Register a callback to be called when logging is ready.
+ *
+ * This function registers a callback that will be invoked when the logging
+ * system has been fully initialized. This is useful for both plugins and
+ * library users who need to register application transaction loggers after
+ * logging initialization is complete.
+ *
+ * \param callback The callback function to be called
+ * \param arg An argument to be passed to the callback function
+ * \return 0 on success, -1 on failure
+ */
+int SCRegisterOnLoggingReady(SCOnLoggingReadyCallback callback, void *arg)
+{
+    OnLoggingReadyCallbackNode *node = SCCalloc(1, sizeof(*node));
+    if (node == NULL) {
+        SCLogError("Failed to allocate memory for callback node");
+        return -1;
+    }
+
+    node->callback = callback;
+    node->arg = arg;
+    TAILQ_INSERT_TAIL(&on_logging_ready_callbacks, node, entries);
+
+    return 0;
+}
+
+/**
+ * \brief Invokes all registered logging ready callbacks.
+ *
+ * This function should be called after the logging system has been fully
+ * initialized to notify all registered callbacks that logging is ready.
+ */
+void SCOnLoggingReady(void)
+{
+    OnLoggingReadyCallbackNode *node = NULL;
+    TAILQ_FOREACH (node, &on_logging_ready_callbacks, entries) {
+        if (node->callback) {
+            (*node->callback)(node->arg);
+        }
+    }
+}
+
 TmEcode OutputLoggerFlush(ThreadVars *tv, Packet *p, void *thread_data)
 {
     LoggerThreadStore *thread_store = (LoggerThreadStore *)thread_data;
index 64196716c9314465b4da3690c1a441f7b26d5476..dd47fa488046bf9446b5325ed59f8eab95104cdf 100644 (file)
@@ -159,6 +159,10 @@ void OutputRegisterFileRotationFlag(int *flag);
 void OutputUnregisterFileRotationFlag(int *flag);
 void OutputNotifyFileRotation(void);
 
+typedef void (*SCOnLoggingReadyCallback)(void *arg);
+int SCRegisterOnLoggingReady(SCOnLoggingReadyCallback callback, void *arg);
+void SCOnLoggingReady(void);
+
 void OutputRegisterRootLogger(ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit,
         OutputLogFunc LogFunc, OutputGetActiveCountFunc ActiveCntFunc);
 void TmModuleLoggerRegister(void);
index 742ed6d000d7f4f2e98f9154f04f62160a352c03..4ce1a9d10f789ca6bee081631d3a3ca1435b95d3 100644 (file)
@@ -2995,6 +2995,8 @@ void SuricataInit(void)
 
     PreRunPostPrivsDropInit(suricata.run_mode);
 
+    SCOnLoggingReady();
+
     LandlockSandboxing(&suricata);
 
     PostConfLoadedDetectSetup(&suricata);