From b7447b1437c017291d4e7bcf239c98b8edf28e6b Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Thu, 26 Feb 2026 17:09:43 -0600 Subject: [PATCH] examples/lib/custom: use own custom run mode Debug validation revealed that library ThreadVars were being created *after* the threads were sealed. And the only way to create your ThreadVars that fits within the current application life-cycle is to create them in your own custom run mode. This is likely a better model for users who are bringing their own packets and threads anyways, as they are essentially providing their own capture method, and all capture methods provide their own run mode. They're also using their own threads, which means adapting to their own threading model. This is suitable for a backport to 8.0. But for 9.0 we can go further and remove the built-in library run mode, which will be done in a follow-up commit. Ticket: #8259 (cherry picked from commit 445de77c71468d56450d995a2a99a7d4670136e0) --- examples/lib/custom/main.c | 44 +++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/examples/lib/custom/main.c b/examples/lib/custom/main.c index 0f0616690f..30e99b05fc 100644 --- a/examples/lib/custom/main.c +++ b/examples/lib/custom/main.c @@ -30,6 +30,9 @@ static int worker_id = 1; +/* ThreadVars created during runmode setup, before thread sealing. */ +static ThreadVars *g_worker_tv = NULL; + /** * Struct to pass arguments into a worker thread. */ @@ -154,6 +157,26 @@ static uint8_t RateFilterCallback(const Packet *p, const uint32_t sid, const uin return new_action; } +/** + * Application runmode setup that creates ThreadVars before thread + * sealing. This follows the pattern used by other runmodes (e.g., + * pcap-file) where threads are registered during the runmode + * callback. + */ +static int AppRunModeSetup(void) +{ + /* This example uses pcap files, so set time mode to offline. */ + TimeModeSetOffline(); + + g_worker_tv = SCRunModeLibCreateThreadVars(worker_id++); + if (!g_worker_tv) { + SCLogError("Failed to create ThreadVars"); + return -1; + } + + return 0; +} + int main(int argc, char **argv) { SuricataPreInit(argv[0]); @@ -208,8 +231,13 @@ int main(int argc, char **argv) * loaded. */ SCEnableDefaultSignalHandlers(); - /* Set "offline" runmode to replay a pcap in library mode. */ - if (!SCConfSetFromString("runmode=offline", 1)) { + /* Register our own library run mode. At this time, the ThreadVars + * for each capture thread need to be created in the provided + * callback to meet thread synchronization requirements. */ + RunModeRegisterNewRunMode( + RUNMODE_LIB, "custom", "Custom application run mode", AppRunModeSetup, NULL); + + if (!SCConfSetFromString("runmode=custom", 1)) { exit(EXIT_FAILURE); } @@ -221,20 +249,16 @@ int main(int argc, char **argv) exit(1); } + /* SuricataInit will call our AppRunModeSetup, when it returns our + * ThreadVars will be ready. */ SuricataInit(); SCDetectEngineRegisterRateFilterCallback(RateFilterCallback, NULL); - /* Create and start worker on its own thread, passing the PCAP - * file as argument. This needs to be done in between SuricataInit - * and SuricataPostInit. */ + /* Spawn our worker threads. */ pthread_t worker; - ThreadVars *tv = SCRunModeLibCreateThreadVars(worker_id++); - if (!tv) { - FatalError("Failed to create ThreadVars"); - } struct WorkerArgs args = { - .tv = tv, + .tv = g_worker_tv, .pcap_filename = argv[argc - 1], }; if (pthread_create(&worker, NULL, SimpleWorker, &args) != 0) {