Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-engine-loader.c
Line
Count
Source
1
/* Copyright (C) 2021-2023 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 */
23
24
#include "suricata-common.h"
25
#include "suricata.h"
26
#include "conf.h"
27
#include "detect.h"
28
#include "detect-parse.h"
29
30
#include "runmodes.h"
31
#include "threads.h"
32
#include "threadvars.h"
33
#include "tm-threads.h"
34
#include "queue.h"
35
#include "util-signal.h"
36
37
#include "detect-engine-loader.h"
38
#include "detect-engine-build.h"
39
#include "detect-engine-analyzer.h"
40
#include "detect-engine-mpm.h"
41
#include "detect-engine-sigorder.h"
42
43
#include "util-detect.h"
44
#include "util-threshold-config.h"
45
#include "util-path.h"
46
47
#include "rust.h"
48
49
#ifdef HAVE_GLOB_H
50
#include <glob.h>
51
#endif
52
53
extern int rule_reload;
54
extern int engine_analysis;
55
static bool fp_engine_analysis_set = false;
56
bool rule_engine_analysis_set = false;
57
58
/**
59
 *  \brief Create the path if default-rule-path was specified
60
 *  \param sig_file The name of the file
61
 *  \retval str Pointer to the string path + sig_file
62
 */
63
char *DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, const char *sig_file)
64
12.1k
{
65
12.1k
    const char *defaultpath = NULL;
66
12.1k
    char *path = NULL;
67
12.1k
    char varname[128];
68
69
12.1k
    if (sig_file == NULL) {
70
0
        SCLogError("invalid sig_file argument - NULL");
71
0
        return NULL;
72
0
    }
73
74
    /* If we have a configuration prefix, only use it if the primary configuration node
75
     * is not marked as final, as that means it was provided on the command line with
76
     * a --set. */
77
12.1k
    ConfNode *default_rule_path = ConfGetNode("default-rule-path");
78
12.1k
    if ((!default_rule_path || !default_rule_path->final) && strlen(de_ctx->config_prefix) > 0) {
79
0
        snprintf(varname, sizeof(varname), "%s.default-rule-path",
80
0
                de_ctx->config_prefix);
81
0
        default_rule_path = ConfGetNode(varname);
82
0
    }
83
12.1k
    if (default_rule_path) {
84
0
        defaultpath = default_rule_path->val;
85
0
    }
86
87
    /* Path not specified */
88
12.1k
    if (PathIsRelative(sig_file)) {
89
12.1k
        if (defaultpath) {
90
0
            path = PathMergeAlloc(defaultpath, sig_file);
91
0
            if (unlikely(path == NULL))
92
0
                return NULL;
93
12.1k
        } else {
94
12.1k
            path = SCStrdup(sig_file);
95
12.1k
            if (unlikely(path == NULL))
96
0
                return NULL;
97
12.1k
        }
98
12.1k
    } else {
99
2
        path = SCStrdup(sig_file);
100
2
        if (unlikely(path == NULL))
101
0
            return NULL;
102
2
    }
103
12.1k
    return path;
104
12.1k
}
105
106
/**
107
 *  \brief Load a file with signatures
108
 *  \param de_ctx Pointer to the detection engine context
109
 *  \param sig_file Filename to load signatures from
110
 *  \param goodsigs_tot Will store number of valid signatures in the file
111
 *  \param badsigs_tot Will store number of invalid signatures in the file
112
 *  \retval 0 on success, -1 on error
113
 */
114
static int DetectLoadSigFile(
115
        DetectEngineCtx *de_ctx, char *sig_file, int *goodsigs, int *badsigs, int *skippedsigs)
116
66.3k
{
117
66.3k
    Signature *sig = NULL;
118
66.3k
    int good = 0, bad = 0, skipped = 0;
119
66.3k
    char line[DETECT_MAX_RULE_SIZE] = "";
120
66.3k
    size_t offset = 0;
121
66.3k
    int lineno = 0, multiline = 0;
122
123
66.3k
    (*goodsigs) = 0;
124
66.3k
    (*badsigs) = 0;
125
66.3k
    (*skippedsigs) = 0;
126
127
66.3k
    FILE *fp = fopen(sig_file, "r");
128
66.3k
    if (fp == NULL) {
129
115
        SCLogError("opening rule file %s:"
130
115
                   " %s.",
131
115
                sig_file, strerror(errno));
132
115
        return -1;
133
115
    }
134
135
5.10M
    while(fgets(line + offset, (int)sizeof(line) - offset, fp) != NULL) {
136
5.03M
        lineno++;
137
5.03M
        size_t len = strlen(line);
138
139
        /* ignore comments and empty lines */
140
5.03M
        if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
141
1.35M
            continue;
142
143
        /* Check for multiline rules. */
144
7.68M
        while (len > 0 && isspace((unsigned char)line[--len]));
145
3.68M
        if (line[len] == '\\') {
146
286k
            multiline++;
147
286k
            offset = len;
148
286k
            if (offset < sizeof(line) - 1) {
149
                /* We have room for more. */
150
286k
                continue;
151
286k
            }
152
            /* No more room in line buffer, continue, rule will fail
153
             * to parse. */
154
286k
        }
155
156
        /* Check if we have a trailing newline, and remove it */
157
3.39M
        len = strlen(line);
158
3.39M
        if (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
159
3.33M
            line[len - 1] = '\0';
160
3.33M
        }
161
162
        /* Reset offset. */
163
3.39M
        offset = 0;
164
165
3.39M
        de_ctx->rule_file = sig_file;
166
3.39M
        de_ctx->rule_line = lineno - multiline;
167
168
3.39M
        sig = DetectEngineAppendSig(de_ctx, line);
169
3.39M
        if (sig != NULL) {
170
75.8k
            if (rule_engine_analysis_set || fp_engine_analysis_set) {
171
0
                RetrieveFPForSig(de_ctx, sig);
172
0
                if (fp_engine_analysis_set) {
173
0
                    EngineAnalysisFP(de_ctx, sig, line);
174
0
                }
175
0
                if (rule_engine_analysis_set) {
176
0
                    EngineAnalysisRules(de_ctx, sig, line);
177
0
                }
178
0
            }
179
75.8k
            SCLogDebug("signature %"PRIu32" loaded", sig->id);
180
75.8k
            good++;
181
3.31M
        } else {
182
3.31M
            if (!de_ctx->sigerror_silent) {
183
3.30M
                SCLogError("error parsing signature \"%s\" from "
184
3.30M
                           "file %s at line %" PRId32 "",
185
3.30M
                        line, sig_file, lineno - multiline);
186
187
3.30M
                if (!SigStringAppend(&de_ctx->sig_stat, sig_file, line, de_ctx->sigerror, (lineno - multiline))) {
188
0
                    SCLogError("Error adding sig \"%s\" from "
189
0
                               "file %s at line %" PRId32 "",
190
0
                            line, sig_file, lineno - multiline);
191
0
                }
192
3.30M
                if (de_ctx->sigerror) {
193
5.78k
                    de_ctx->sigerror = NULL;
194
5.78k
                }
195
3.30M
            }
196
3.31M
            if (rule_engine_analysis_set) {
197
0
                EngineAnalysisRulesFailure(de_ctx, line, sig_file, lineno - multiline);
198
0
            }
199
3.31M
            if (!de_ctx->sigerror_ok) {
200
3.30M
                bad++;
201
3.30M
            }
202
3.31M
            if (de_ctx->sigerror_requires) {
203
7.15k
                SCLogInfo("Skipping signature due to missing requirements: %s from file %s at line "
204
7.15k
                          "%" PRId32,
205
7.15k
                        line, sig_file, lineno - multiline);
206
7.15k
                skipped++;
207
7.15k
            }
208
3.31M
        }
209
3.39M
        multiline = 0;
210
3.39M
    }
211
66.2k
    fclose(fp);
212
213
66.2k
    *goodsigs = good;
214
66.2k
    *badsigs = bad;
215
66.2k
    *skippedsigs = skipped;
216
66.2k
    return 0;
217
66.3k
}
218
219
/**
220
 *  \brief Expands wildcards and reads signatures from each matching file
221
 *  \param de_ctx Pointer to the detection engine context
222
 *  \param sig_file Filename (or pattern) holding signatures
223
 *  \retval -1 on error
224
 */
225
static int ProcessSigFiles(DetectEngineCtx *de_ctx, char *pattern, SigFileLoaderStat *st,
226
        int *good_sigs, int *bad_sigs, int *skipped_sigs)
227
67.5k
{
228
67.5k
    int r = 0;
229
230
67.5k
    if (pattern == NULL) {
231
0
        SCLogError("opening rule file null");
232
0
        return -1;
233
0
    }
234
235
67.5k
#ifdef HAVE_GLOB_H
236
67.5k
    glob_t files;
237
67.5k
    r = glob(pattern, 0, NULL, &files);
238
239
67.5k
    if (r == GLOB_NOMATCH) {
240
1.18k
        SCLogWarning("No rule files match the pattern %s", pattern);
241
1.18k
        ++(st->bad_files);
242
1.18k
        ++(st->total_files);
243
1.18k
        return -1;
244
66.3k
    } else if (r != 0) {
245
0
        SCLogError("error expanding template %s: %s", pattern, strerror(errno));
246
0
        return -1;
247
0
    }
248
249
132k
    for (size_t i = 0; i < (size_t)files.gl_pathc; i++) {
250
66.3k
        char *fname = files.gl_pathv[i];
251
66.3k
        if (strcmp("/dev/null", fname) == 0)
252
0
            continue;
253
#else
254
        char *fname = pattern;
255
        if (strcmp("/dev/null", fname) == 0)
256
            return 0;
257
#endif
258
66.3k
        SCLogConfig("Loading rule file: %s", fname);
259
66.3k
        r = DetectLoadSigFile(de_ctx, fname, good_sigs, bad_sigs, skipped_sigs);
260
66.3k
        if (r < 0) {
261
115
            ++(st->bad_files);
262
115
        }
263
264
66.3k
        ++(st->total_files);
265
266
66.3k
        st->good_sigs_total += *good_sigs;
267
66.3k
        st->bad_sigs_total += *bad_sigs;
268
66.3k
        st->skipped_sigs_total += *skipped_sigs;
269
270
66.3k
#ifdef HAVE_GLOB_H
271
66.3k
    }
272
66.3k
    globfree(&files);
273
66.3k
#endif
274
66.3k
    return r;
275
67.5k
}
276
277
/**
278
 *  \brief Load signatures
279
 *  \param de_ctx Pointer to the detection engine context
280
 *  \param sig_file Filename (or pattern) holding signatures
281
 *  \param sig_file_exclusive File passed in 'sig_file' should be loaded exclusively.
282
 *  \retval -1 on error
283
 */
284
int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, int sig_file_exclusive)
285
67.5k
{
286
67.5k
    SCEnter();
287
288
67.5k
    ConfNode *rule_files;
289
67.5k
    ConfNode *file = NULL;
290
67.5k
    SigFileLoaderStat *sig_stat = &de_ctx->sig_stat;
291
67.5k
    int ret = 0;
292
67.5k
    char *sfile = NULL;
293
67.5k
    char varname[128] = "rule-files";
294
67.5k
    int good_sigs = 0;
295
67.5k
    int bad_sigs = 0;
296
67.5k
    int skipped_sigs = 0;
297
298
67.5k
    if (strlen(de_ctx->config_prefix) > 0) {
299
0
        snprintf(varname, sizeof(varname), "%s.rule-files",
300
0
                de_ctx->config_prefix);
301
0
    }
302
303
67.5k
    if (RunmodeGetCurrent() == RUNMODE_ENGINE_ANALYSIS) {
304
0
        SetupEngineAnalysis(de_ctx, &fp_engine_analysis_set, &rule_engine_analysis_set);
305
0
    }
306
307
    /* ok, let's load signature files from the general config */
308
67.5k
    if (!(sig_file != NULL && sig_file_exclusive == TRUE)) {
309
0
        rule_files = ConfGetNode(varname);
310
0
        if (rule_files != NULL) {
311
0
            if (!ConfNodeIsSequence(rule_files)) {
312
0
                SCLogWarning("Invalid rule-files configuration section: "
313
0
                             "expected a list of filenames.");
314
0
            }
315
0
            else {
316
0
                TAILQ_FOREACH(file, &rule_files->head, next) {
317
0
                    sfile = DetectLoadCompleteSigPath(de_ctx, file->val);
318
0
                    good_sigs = bad_sigs = skipped_sigs = 0;
319
0
                    ret = ProcessSigFiles(
320
0
                            de_ctx, sfile, sig_stat, &good_sigs, &bad_sigs, &skipped_sigs);
321
0
                    SCFree(sfile);
322
323
0
                    if (de_ctx->failure_fatal && ret != 0) {
324
                        /* Some rules failed to load, just exit as
325
                         * errors would have already been logged. */
326
0
                        exit(EXIT_FAILURE);
327
0
                    }
328
329
0
                    if (good_sigs == 0) {
330
0
                        SCLogConfig("No rules loaded from %s.", file->val);
331
0
                    }
332
0
                }
333
0
            }
334
0
        }
335
0
    }
336
337
    /* If a Signature file is specified from command-line, parse it too */
338
67.5k
    if (sig_file != NULL) {
339
67.5k
        ret = ProcessSigFiles(de_ctx, sig_file, sig_stat, &good_sigs, &bad_sigs, &skipped_sigs);
340
341
67.5k
        if (ret != 0) {
342
1.30k
            if (de_ctx->failure_fatal) {
343
0
                exit(EXIT_FAILURE);
344
0
            }
345
1.30k
        }
346
347
67.5k
        if (good_sigs == 0) {
348
43.5k
            SCLogConfig("No rules loaded from %s", sig_file);
349
43.5k
        }
350
67.5k
    }
351
352
    /* now we should have signatures to work with */
353
67.5k
    if (sig_stat->good_sigs_total <= 0) {
354
43.5k
        if (sig_stat->total_files > 0) {
355
43.5k
            SCLogWarning(
356
43.5k
                    "%d rule files specified, but no rules were loaded!", sig_stat->total_files);
357
43.5k
        } else {
358
0
            SCLogInfo("No signatures supplied.");
359
0
            goto end;
360
0
        }
361
43.5k
    } else {
362
        /* we report the total of files and rules successfully loaded and failed */
363
24.0k
        SCLogInfo("%" PRId32 " rule files processed. %" PRId32
364
24.0k
                  " rules successfully loaded, %" PRId32 " rules failed, %" PRId32,
365
24.0k
                sig_stat->total_files, sig_stat->good_sigs_total, sig_stat->bad_sigs_total,
366
24.0k
                sig_stat->skipped_sigs_total);
367
24.0k
        if (de_ctx->requirements != NULL && sig_stat->skipped_sigs_total > 0) {
368
262
            SCDetectRequiresStatusLog(de_ctx->requirements, PROG_VER,
369
262
                    strlen(de_ctx->config_prefix) > 0 ? de_ctx->tenant_id : 0);
370
262
        }
371
24.0k
    }
372
373
67.5k
    if ((sig_stat->bad_sigs_total || sig_stat->bad_files) && de_ctx->failure_fatal) {
374
0
        ret = -1;
375
0
        goto end;
376
0
    }
377
378
67.5k
    SCSigRegisterSignatureOrderingFuncs(de_ctx);
379
67.5k
    SCSigOrderSignatures(de_ctx);
380
67.5k
    SCSigSignatureOrderingModuleCleanup(de_ctx);
381
382
67.5k
    if (SCThresholdConfInitContext(de_ctx) < 0) {
383
0
        ret = -1;
384
0
        goto end;
385
0
    }
386
387
    /* Setup the signature group lookup structure and pattern matchers */
388
67.5k
    if (SigGroupBuild(de_ctx) < 0)
389
0
        goto end;
390
391
67.5k
    ret = 0;
392
393
67.5k
 end:
394
67.5k
    gettimeofday(&de_ctx->last_reload, NULL);
395
67.5k
    if (RunmodeGetCurrent() == RUNMODE_ENGINE_ANALYSIS) {
396
0
        CleanupEngineAnalysis(de_ctx);
397
0
    }
398
399
67.5k
    DetectParseDupSigHashFree(de_ctx);
400
67.5k
    SCReturnInt(ret);
401
67.5k
}
402
403
0
#define NLOADERS 4
404
static DetectLoaderControl *loaders = NULL;
405
static int cur_loader = 0;
406
static void TmThreadWakeupDetectLoaderThreads(void);
407
static int num_loaders = NLOADERS;
408
409
/** \param loader -1 for auto select
410
 *  \retval loader_id or negative in case of error */
411
int DetectLoaderQueueTask(int loader_id, LoaderFunc Func, void *func_ctx, LoaderFreeFunc FreeFunc)
412
0
{
413
0
    if (loader_id == -1) {
414
0
        loader_id = cur_loader;
415
0
        cur_loader++;
416
0
        if (cur_loader >= num_loaders)
417
0
            cur_loader = 0;
418
0
    }
419
0
    if (loader_id >= num_loaders || loader_id < 0) {
420
0
        return -ERANGE;
421
0
    }
422
423
0
    DetectLoaderControl *loader = &loaders[loader_id];
424
425
0
    DetectLoaderTask *t = SCCalloc(1, sizeof(*t));
426
0
    if (t == NULL)
427
0
        return -ENOMEM;
428
429
0
    t->Func = Func;
430
0
    t->ctx = func_ctx;
431
0
    t->FreeFunc = FreeFunc;
432
433
0
    SCMutexLock(&loader->m);
434
0
    TAILQ_INSERT_TAIL(&loader->task_list, t, next);
435
0
    SCMutexUnlock(&loader->m);
436
437
0
    TmThreadWakeupDetectLoaderThreads();
438
439
0
    SCLogDebug("%d %p %p", loader_id, Func, func_ctx);
440
0
    return loader_id;
441
0
}
442
443
/** \brief wait for loader tasks to complete
444
 *  \retval result 0 for ok, -1 for errors */
445
int DetectLoadersSync(void)
446
0
{
447
0
    SCLogDebug("waiting");
448
0
    int errors = 0;
449
0
    for (int i = 0; i < num_loaders; i++) {
450
0
        bool done = false;
451
452
0
        DetectLoaderControl *loader = &loaders[i];
453
0
        while (!done) {
454
0
            SCMutexLock(&loader->m);
455
0
            if (TAILQ_EMPTY(&loader->task_list)) {
456
0
                done = true;
457
0
            }
458
0
            SCMutexUnlock(&loader->m);
459
0
            if (!done) {
460
                /* nudge thread in case it's sleeping */
461
0
                SCCtrlMutexLock(loader->tv->ctrl_mutex);
462
0
                pthread_cond_broadcast(loader->tv->ctrl_cond);
463
0
                SCCtrlMutexUnlock(loader->tv->ctrl_mutex);
464
0
            }
465
0
        }
466
0
        SCMutexLock(&loader->m);
467
0
        if (loader->result != 0) {
468
0
            errors++;
469
0
            loader->result = 0;
470
0
        }
471
0
        SCMutexUnlock(&loader->m);
472
0
    }
473
0
    if (errors) {
474
0
        SCLogError("%d loaders reported errors", errors);
475
0
        return -1;
476
0
    }
477
0
    SCLogDebug("done");
478
0
    return 0;
479
0
}
480
481
static void DetectLoaderInit(DetectLoaderControl *loader)
482
0
{
483
0
    memset(loader, 0x00, sizeof(*loader));
484
0
    SCMutexInit(&loader->m, NULL);
485
0
    TAILQ_INIT(&loader->task_list);
486
0
}
487
488
void DetectLoadersInit(void)
489
0
{
490
0
    intmax_t setting = NLOADERS;
491
0
    (void)ConfGetInt("multi-detect.loaders", &setting);
492
493
0
    if (setting < 1 || setting > 1024) {
494
0
        FatalError("invalid multi-detect.loaders setting %" PRIdMAX, setting);
495
0
    }
496
497
0
    num_loaders = (int32_t)setting;
498
0
    SCLogInfo("using %d detect loader threads", num_loaders);
499
500
0
    BUG_ON(loaders != NULL);
501
0
    loaders = SCCalloc(num_loaders, sizeof(DetectLoaderControl));
502
0
    BUG_ON(loaders == NULL);
503
504
0
    for (int i = 0; i < num_loaders; i++) {
505
0
        DetectLoaderInit(&loaders[i]);
506
0
    }
507
0
}
508
509
/**
510
 * \brief Unpauses all threads present in tv_root
511
 */
512
static void TmThreadWakeupDetectLoaderThreads(void)
513
0
{
514
0
    SCMutexLock(&tv_root_lock);
515
0
    for (int i = 0; i < TVT_MAX; i++) {
516
0
        ThreadVars *tv = tv_root[i];
517
0
        while (tv != NULL) {
518
0
            if (strncmp(tv->name,"DL#",3) == 0) {
519
0
                BUG_ON(tv->ctrl_cond == NULL);
520
0
                SCCtrlMutexLock(tv->ctrl_mutex);
521
0
                pthread_cond_broadcast(tv->ctrl_cond);
522
0
                SCCtrlMutexUnlock(tv->ctrl_mutex);
523
0
            }
524
0
            tv = tv->next;
525
0
        }
526
0
    }
527
0
    SCMutexUnlock(&tv_root_lock);
528
0
}
529
530
/**
531
 * \brief Unpauses all threads present in tv_root
532
 */
533
void TmThreadContinueDetectLoaderThreads(void)
534
0
{
535
0
    SCMutexLock(&tv_root_lock);
536
0
    for (int i = 0; i < TVT_MAX; i++) {
537
0
        ThreadVars *tv = tv_root[i];
538
0
        while (tv != NULL) {
539
0
            if (strncmp(tv->name,"DL#",3) == 0)
540
0
                TmThreadContinue(tv);
541
542
0
            tv = tv->next;
543
0
        }
544
0
    }
545
0
    SCMutexUnlock(&tv_root_lock);
546
0
}
547
548
SC_ATOMIC_DECLARE(int, detect_loader_cnt);
549
550
typedef struct DetectLoaderThreadData_ {
551
    uint32_t instance;
552
} DetectLoaderThreadData;
553
554
static TmEcode DetectLoaderThreadInit(ThreadVars *t, const void *initdata, void **data)
555
0
{
556
0
    DetectLoaderThreadData *ftd = SCCalloc(1, sizeof(DetectLoaderThreadData));
557
0
    if (ftd == NULL)
558
0
        return TM_ECODE_FAILED;
559
560
0
    ftd->instance = SC_ATOMIC_ADD(detect_loader_cnt, 1); /* id's start at 0 */
561
0
    SCLogDebug("detect loader instance %u", ftd->instance);
562
563
    /* pass thread data back to caller */
564
0
    *data = ftd;
565
566
0
    DetectLoaderControl *loader = &loaders[ftd->instance];
567
0
    loader->tv = t;
568
569
0
    return TM_ECODE_OK;
570
0
}
571
572
static TmEcode DetectLoaderThreadDeinit(ThreadVars *t, void *data)
573
0
{
574
0
    SCFree(data);
575
0
    return TM_ECODE_OK;
576
0
}
577
578
579
static TmEcode DetectLoader(ThreadVars *th_v, void *thread_data)
580
0
{
581
0
    DetectLoaderThreadData *ftd = (DetectLoaderThreadData *)thread_data;
582
0
    BUG_ON(ftd == NULL);
583
584
0
    TmThreadsSetFlag(th_v, THV_INIT_DONE | THV_RUNNING);
585
0
    SCLogDebug("loader thread started");
586
0
    while (1)
587
0
    {
588
0
        if (TmThreadsCheckFlag(th_v, THV_PAUSE)) {
589
0
            TmThreadsSetFlag(th_v, THV_PAUSED);
590
0
            TmThreadTestThreadUnPaused(th_v);
591
0
            TmThreadsUnsetFlag(th_v, THV_PAUSED);
592
0
        }
593
594
        /* see if we have tasks */
595
596
0
        DetectLoaderControl *loader = &loaders[ftd->instance];
597
0
        SCMutexLock(&loader->m);
598
599
0
        DetectLoaderTask *task = NULL, *tmptask = NULL;
600
0
        TAILQ_FOREACH_SAFE(task, &loader->task_list, next, tmptask) {
601
0
            int r = task->Func(task->ctx, ftd->instance);
602
0
            loader->result |= r;
603
0
            TAILQ_REMOVE(&loader->task_list, task, next);
604
0
            task->FreeFunc(task->ctx);
605
0
            SCFree(task);
606
0
        }
607
608
0
        SCMutexUnlock(&loader->m);
609
610
0
        if (TmThreadsCheckFlag(th_v, THV_KILL)) {
611
0
            break;
612
0
        }
613
614
        /* just wait until someone wakes us up */
615
0
        SCCtrlMutexLock(th_v->ctrl_mutex);
616
0
        SCCtrlCondWait(th_v->ctrl_cond, th_v->ctrl_mutex);
617
0
        SCCtrlMutexUnlock(th_v->ctrl_mutex);
618
619
0
        SCLogDebug("woke up...");
620
0
    }
621
622
0
    TmThreadsSetFlag(th_v, THV_RUNNING_DONE);
623
0
    TmThreadWaitForFlag(th_v, THV_DEINIT);
624
0
    TmThreadsSetFlag(th_v, THV_CLOSED);
625
626
0
    return TM_ECODE_OK;
627
0
}
628
629
/** \brief spawn the detect loader manager thread */
630
void DetectLoaderThreadSpawn(void)
631
0
{
632
0
    for (int i = 0; i < num_loaders; i++) {
633
0
        char name[TM_THREAD_NAME_MAX];
634
0
        snprintf(name, sizeof(name), "%s#%02d", thread_name_detect_loader, i+1);
635
636
0
        ThreadVars *tv_loader = TmThreadCreateCmdThreadByName(name, "DetectLoader", 1);
637
0
        if (tv_loader == NULL) {
638
0
            FatalError("failed to create thread %s", name);
639
0
        }
640
0
        if (TmThreadSpawn(tv_loader) != TM_ECODE_OK) {
641
0
            FatalError("failed to create spawn %s", name);
642
0
        }
643
0
    }
644
0
}
645
646
void TmModuleDetectLoaderRegister (void)
647
0
{
648
0
    tmm_modules[TMM_DETECTLOADER].name = "DetectLoader";
649
0
    tmm_modules[TMM_DETECTLOADER].ThreadInit = DetectLoaderThreadInit;
650
0
    tmm_modules[TMM_DETECTLOADER].ThreadDeinit = DetectLoaderThreadDeinit;
651
0
    tmm_modules[TMM_DETECTLOADER].Management = DetectLoader;
652
0
    tmm_modules[TMM_DETECTLOADER].cap_flags = 0;
653
0
    tmm_modules[TMM_DETECTLOADER].flags = TM_FLAG_MANAGEMENT_TM;
654
0
    SCLogDebug("%s registered", tmm_modules[TMM_DETECTLOADER].name);
655
656
0
    SC_ATOMIC_INIT(detect_loader_cnt);
657
0
}