From 25e32f4f7a35802e5bab98908ef83869e48f5de6 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Thu, 5 Jun 2025 15:41:40 -0600 Subject: [PATCH] output: delayed initialization for custom loggers 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 | 4 +- .../plugins/c-custom-loggers/custom-logger.c | 20 +++--- src/output.c | 65 +++++++++++++++++++ src/output.h | 4 ++ src/suricata.c | 2 + 5 files changed, 82 insertions(+), 13 deletions(-) diff --git a/examples/plugins/c-custom-loggers/Makefile.in b/examples/plugins/c-custom-loggers/Makefile.in index a30bc8a385..d4656d800d 100644 --- a/examples/plugins/c-custom-loggers/Makefile.in +++ b/examples/plugins/c-custom-loggers/Makefile.in @@ -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: diff --git a/examples/plugins/c-custom-loggers/custom-logger.c b/examples/plugins/c-custom-loggers/custom-logger.c index c74066faf8..e2f36f1d84 100644 --- a/examples/plugins/c-custom-loggers/custom-logger.c +++ b/examples/plugins/c-custom-loggers/custom-logger.c @@ -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 = { diff --git a/src/output.c b/src/output.c index a913d31bf3..5d3794a4e0 100644 --- a/src/output.c +++ b/src/output.c @@ -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; diff --git a/src/output.h b/src/output.h index 64196716c9..dd47fa4880 100644 --- a/src/output.h +++ b/src/output.h @@ -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); diff --git a/src/suricata.c b/src/suricata.c index 742ed6d000..4ce1a9d10f 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -2995,6 +2995,8 @@ void SuricataInit(void) PreRunPostPrivsDropInit(suricata.run_mode); + SCOnLoggingReady(); + LandlockSandboxing(&suricata); PostConfLoadedDetectSetup(&suricata); -- 2.47.2