• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22550237931

01 Mar 2026 06:56PM UTC coverage: 64.812% (-8.9%) from 73.687%
22550237931

Pull #14920

github

web-flow
Merge e05854a6d into 90823fa90
Pull Request #14920: draft: rust based configuration file parser and loader - v4

561 of 789 new or added lines in 4 files covered. (71.1%)

23225 existing lines in 498 files now uncovered.

131605 of 203055 relevant lines covered (64.81%)

2328818.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.98
/src/detect-engine-loader.c
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

36
#include "detect-engine.h"
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
static char *DetectLoadCompleteSigPathWithKey(
59
        const DetectEngineCtx *de_ctx, const char *default_key, const char *sig_file)
60
{
44,618✔
61
    const char *defaultpath = NULL;
44,618✔
62
    char *path = NULL;
44,618✔
63
    char varname[128];
44,618✔
64

65
    if (sig_file == NULL) {
44,618✔
66
        SCLogError("invalid sig_file argument - NULL");
×
67
        return NULL;
×
68
    }
×
69

70
    /* If we have a configuration prefix, only use it if the primary configuration node
71
     * is not marked as final, as that means it was provided on the command line with
72
     * a --set. */
73
    SCConfNode *default_rule_path = SCConfGetNode(default_key);
44,618✔
74
    if ((!default_rule_path || !default_rule_path->final) && strlen(de_ctx->config_prefix) > 0) {
44,618✔
UNCOV
75
        snprintf(varname, sizeof(varname), "%s.%s", de_ctx->config_prefix, default_key);
×
UNCOV
76
        default_rule_path = SCConfGetNode(varname);
×
UNCOV
77
    }
×
78
    if (default_rule_path) {
44,618✔
79
        defaultpath = default_rule_path->val;
21✔
80
    }
21✔
81

82
    /* Path not specified */
83
    if (PathIsRelative(sig_file)) {
44,618✔
84
        if (defaultpath) {
44,610✔
85
            path = PathMergeAlloc(defaultpath, sig_file);
21✔
86
            if (unlikely(path == NULL))
21✔
87
                return NULL;
×
88
        } else {
44,610✔
89
            path = SCStrdup(sig_file);
44,589✔
90
            if (unlikely(path == NULL))
44,589✔
91
                return NULL;
×
92
        }
44,589✔
93
    } else {
44,610✔
94
        path = SCStrdup(sig_file);
8✔
95
        if (unlikely(path == NULL))
8✔
96
            return NULL;
×
97
    }
8✔
98
    return path;
44,618✔
99
}
44,618✔
100

101
/**
102
 *  \brief Create the path if default-rule-path was specified
103
 *  \param sig_file The name of the file
104
 *  \retval str Pointer to the string path + sig_file
105
 */
106
char *DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, const char *sig_file)
107
{
44,618✔
108
    return DetectLoadCompleteSigPathWithKey(de_ctx, "default-rule-path", sig_file);
44,618✔
109
}
44,618✔
110

111
/**
112
 *  \brief Load a file with signatures
113
 *  \param de_ctx Pointer to the detection engine context
114
 *  \param sig_file Filename to load signatures from
115
 *  \param goodsigs_tot Will store number of valid signatures in the file
116
 *  \param badsigs_tot Will store number of invalid signatures in the file
117
 *  \retval 0 on success, -1 on error
118
 */
119
static int DetectLoadSigFile(DetectEngineCtx *de_ctx, const char *sig_file, int *goodsigs,
120
        int *badsigs, int *skippedsigs, const bool firewall_rule)
121
{
35,559✔
122
    int good = 0, bad = 0, skipped = 0;
35,559✔
123
    char line[DETECT_MAX_RULE_SIZE] = "";
35,559✔
124
    size_t offset = 0;
35,559✔
125
    int lineno = 0, multiline = 0;
35,559✔
126

127
    (*goodsigs) = 0;
35,559✔
128
    (*badsigs) = 0;
35,559✔
129
    (*skippedsigs) = 0;
35,559✔
130

131
    FILE *fp = fopen(sig_file, "r");
35,559✔
132
    if (fp == NULL) {
35,559✔
133
        SCLogError("opening rule file %s:"
×
134
                   " %s.",
×
135
                sig_file, strerror(errno));
×
136
        return -1;
×
137
    }
×
138

139
    while (1) {
6,494,296✔
140
        /* help clang to understand offset can't get > sizeof(line), so the argument to
141
         * fgets can't get negative. */
142
        BUG_ON(offset >= sizeof(line));
6,494,296✔
143
        char *res = fgets(line + offset, (int)(sizeof(line) - offset), fp);
6,494,296✔
144
        if (res == NULL)
6,494,296✔
145
            break;
35,559✔
146

147
        lineno++;
6,458,737✔
148
        size_t len = strlen(line);
6,458,737✔
149

150
        /* ignore comments and empty lines */
151
        if (line[0] == '\n' || line [0] == '\r' || line[0] == ' ' || line[0] == '#' || line[0] == '\t')
6,458,738✔
152
            continue;
655,671✔
153

154
        /* Check for multiline rules. */
155
        while (len > 0 && isspace((unsigned char)line[--len]));
11,940,600✔
156
        if (line[len] == '\\') {
5,803,066✔
157
            multiline++;
818,579✔
158
            offset = len;
818,579✔
159
            if (offset < sizeof(line) - 1) {
818,579✔
160
                /* We have room for more. */
161
                continue;
818,579✔
162
            }
818,579✔
163
            /* No more room in line buffer, continue, rule will fail
164
             * to parse. */
165
        }
818,579✔
166

167
        /* Check if we have a trailing newline, and remove it */
168
        len = strlen(line);
4,984,487✔
169
        if (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
4,984,488✔
170
            line[len - 1] = '\0';
4,955,814✔
171
        }
4,955,814✔
172

173
        /* Reset offset. */
174
        offset = 0;
4,984,487✔
175

176
        de_ctx->rule_file = sig_file;
4,984,487✔
177
        de_ctx->rule_line = lineno - multiline;
4,984,487✔
178

179
        Signature *sig = NULL;
4,984,487✔
180
        if (firewall_rule)
4,984,487✔
UNCOV
181
            sig = DetectFirewallRuleAppendNew(de_ctx, line);
×
182
        else
4,984,487✔
183
            sig = DetectEngineAppendSig(de_ctx, line);
4,984,487✔
184
        if (sig != NULL) {
4,984,488✔
185
            if (rule_engine_analysis_set || fp_engine_analysis_set) {
108,223✔
UNCOV
186
                if (fp_engine_analysis_set) {
×
UNCOV
187
                    EngineAnalysisFP(de_ctx, sig, line);
×
UNCOV
188
                }
×
UNCOV
189
                if (rule_engine_analysis_set) {
×
UNCOV
190
                    EngineAnalysisRules(de_ctx, sig, line);
×
UNCOV
191
                }
×
UNCOV
192
            }
×
193
            SCLogDebug("signature %"PRIu32" loaded", sig->id);
108,223✔
194
            good++;
108,223✔
195
        } else {
2,152,359,912✔
196
            if (!de_ctx->sigerror_silent) {
2,152,359,912✔
197
                SCLogError("error parsing signature \"%s\" from "
4,848,584✔
198
                           "file %s at line %" PRId32 "",
4,848,584✔
199
                        line, sig_file, lineno - multiline);
4,848,584✔
200

201
                if (!SigStringAppend(&de_ctx->sig_stat, sig_file, line, de_ctx->sigerror, (lineno - multiline))) {
4,848,584✔
202
                    SCLogError("Error adding sig \"%s\" from "
×
203
                               "file %s at line %" PRId32 "",
×
204
                            line, sig_file, lineno - multiline);
×
205
                }
×
206
                if (de_ctx->sigerror) {
4,848,584✔
207
                    de_ctx->sigerror = NULL;
12,248✔
208
                }
12,248✔
209
            }
4,848,584✔
210
            if (rule_engine_analysis_set) {
2,152,359,912✔
UNCOV
211
                EngineAnalysisRulesFailure(de_ctx, line, sig_file, lineno - multiline);
×
UNCOV
212
            }
×
213
            if (!de_ctx->sigerror_ok) {
2,152,359,912✔
214
                bad++;
4,848,584✔
215
            }
4,848,584✔
216
            if (de_ctx->sigerror_requires) {
2,152,359,912✔
217
                SCLogInfo("Skipping signature due to missing requirements: %s from file %s at line "
10,002✔
218
                          "%" PRId32,
10,002✔
219
                        line, sig_file, lineno - multiline);
10,002✔
220
                skipped++;
10,002✔
221
            }
10,002✔
222
        }
2,152,359,912✔
223
        multiline = 0;
4,984,487✔
224
    }
4,984,487✔
225
    fclose(fp);
35,559✔
226

227
    *goodsigs = good;
35,559✔
228
    *badsigs = bad;
35,559✔
229
    *skippedsigs = skipped;
35,559✔
230
    return 0;
35,559✔
231
}
35,559✔
232

233
/**
234
 *  \brief Expands wildcards and reads signatures from each matching file
235
 *  \param de_ctx Pointer to the detection engine context
236
 *  \param sig_file Filename (or pattern) holding signatures
237
 *  \retval -1 on error
238
 */
239
static int ProcessSigFiles(DetectEngineCtx *de_ctx, char *pattern, SigFileLoaderStat *st,
240
        int *good_sigs, int *bad_sigs, int *skipped_sigs)
241
{
35,578✔
242
    int r = 0;
35,578✔
243

244
    if (pattern == NULL) {
35,578✔
245
        SCLogError("opening rule file null");
×
246
        return -1;
×
247
    }
×
248

249
#ifdef HAVE_GLOB_H
35,578✔
250
    glob_t files;
35,578✔
251
    r = glob(pattern, 0, NULL, &files);
35,578✔
252

253
    if (r == GLOB_NOMATCH) {
35,578✔
254
        SCLogWarning("No rule files match the pattern %s", pattern);
1✔
255
        ++(st->bad_files);
1✔
256
        ++(st->total_files);
1✔
257
        return -1;
1✔
258
    } else if (r != 0) {
35,577✔
259
        SCLogError("error expanding template %s: %s", pattern, strerror(errno));
×
260
        return -1;
×
261
    }
×
262

263
    for (size_t i = 0; i < (size_t)files.gl_pathc; i++) {
71,154✔
264
        char *fname = files.gl_pathv[i];
35,577✔
265
        if (strcmp("/dev/null", fname) == 0)
35,577✔
266
            continue;
18✔
267
#else
268
        char *fname = pattern;
269
        if (strcmp("/dev/null", fname) == 0)
270
            return 0;
271
#endif
272
        if (strlen(de_ctx->config_prefix) > 0) {
35,559✔
273
            SCLogConfig("tenant id %d: Loading rule file: %s", de_ctx->tenant_id, fname);
15✔
274
        } else {
35,553✔
275
            SCLogConfig("Loading rule file: %s", fname);
35,544✔
276
        }
35,544✔
277
        r = DetectLoadSigFile(de_ctx, fname, good_sigs, bad_sigs, skipped_sigs, false);
35,559✔
278
        if (r < 0) {
35,559✔
279
            ++(st->bad_files);
×
280
        }
×
281

282
        ++(st->total_files);
35,559✔
283

284
        st->good_sigs_total += *good_sigs;
35,559✔
285
        st->bad_sigs_total += *bad_sigs;
35,559✔
286
        st->skipped_sigs_total += *skipped_sigs;
35,559✔
287

288
#ifdef HAVE_GLOB_H
35,559✔
289
    }
35,559✔
290
    globfree(&files);
35,577✔
291
#endif
35,577✔
292
    return r;
35,577✔
293
}
35,578✔
294

295
static int LoadFirewallRuleFiles(DetectEngineCtx *de_ctx)
UNCOV
296
{
×
UNCOV
297
    if (de_ctx->firewall_rule_file_exclusive) {
×
UNCOV
298
        int32_t good_sigs = 0;
×
UNCOV
299
        int32_t bad_sigs = 0;
×
UNCOV
300
        int32_t skipped_sigs = 0;
×
301

UNCOV
302
        SCLogDebug("fw: rule file full path \"%s\"", de_ctx->firewall_rule_file_exclusive);
×
303

UNCOV
304
        int ret = DetectLoadSigFile(de_ctx, de_ctx->firewall_rule_file_exclusive, &good_sigs,
×
UNCOV
305
                &bad_sigs, &skipped_sigs, true);
×
306

307
        /* for now be as strict as possible */
UNCOV
308
        if (ret != 0 || bad_sigs != 0 || skipped_sigs != 0) {
×
309
            /* Some rules failed to load, just exit as
310
             * errors would have already been logged. */
311
            exit(EXIT_FAILURE);
×
312
        }
×
313

UNCOV
314
        if (good_sigs == 0) {
×
315
            SCLogNotice("fw: No rules loaded from %s.", de_ctx->firewall_rule_file_exclusive);
×
UNCOV
316
        } else {
×
UNCOV
317
            SCLogNotice("fw: %d rules loaded from %s.", good_sigs,
×
UNCOV
318
                    de_ctx->firewall_rule_file_exclusive);
×
UNCOV
319
            de_ctx->sig_stat.good_sigs_total += good_sigs;
×
UNCOV
320
        }
×
321

UNCOV
322
        return 0;
×
UNCOV
323
    }
×
324

UNCOV
325
    SCConfNode *default_fw_rule_path = SCConfGetNode("firewall.rule-path");
×
UNCOV
326
    if (default_fw_rule_path == NULL) {
×
327
        SCLogNotice("fw: firewall.rule-path not defined, skip loading firewall rules");
×
328
        return 0;
×
329
    }
×
UNCOV
330
    SCConfNode *rule_files = SCConfGetNode("firewall.rule-files");
×
UNCOV
331
    if (rule_files == NULL) {
×
332
        SCLogNotice("fw: firewall.rule-files not defined, skip loading firewall rules");
×
333
        return 0;
×
334
    }
×
335

UNCOV
336
    SCConfNode *file = NULL;
×
UNCOV
337
    TAILQ_FOREACH (file, &rule_files->head, next) {
×
UNCOV
338
        int32_t good_sigs = 0;
×
UNCOV
339
        int32_t bad_sigs = 0;
×
UNCOV
340
        int32_t skipped_sigs = 0;
×
341

UNCOV
342
        char *sfile = DetectLoadCompleteSigPathWithKey(de_ctx, "firewall.rule-path", file->val);
×
UNCOV
343
        SCLogNotice("fw: rule file full path \"%s\"", sfile);
×
344

UNCOV
345
        int ret = DetectLoadSigFile(de_ctx, sfile, &good_sigs, &bad_sigs, &skipped_sigs, true);
×
UNCOV
346
        SCFree(sfile);
×
347

348
        /* for now be as strict as possible */
UNCOV
349
        if (ret != 0 || bad_sigs != 0 || skipped_sigs != 0) {
×
350
            /* Some rules failed to load, just exit as
351
             * errors would have already been logged. */
352
            exit(EXIT_FAILURE);
×
353
        }
×
354

UNCOV
355
        if (good_sigs == 0) {
×
356
            SCLogNotice("fw: No rules loaded from %s.", file->val);
×
UNCOV
357
        } else {
×
UNCOV
358
            SCLogNotice("fw: %d rules loaded from %s.", good_sigs, file->val);
×
UNCOV
359
            de_ctx->sig_stat.good_sigs_total += good_sigs;
×
UNCOV
360
        }
×
UNCOV
361
    }
×
UNCOV
362
    return 0;
×
UNCOV
363
}
×
364

365
/**
366
 *  \brief Load signatures
367
 *  \param de_ctx Pointer to the detection engine context
368
 *  \param sig_file Filename (or pattern) holding signatures
369
 *  \param sig_file_exclusive File passed in 'sig_file' should be loaded exclusively.
370
 *  \retval -1 on error
371
 */
372
int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, bool sig_file_exclusive)
373
{
35,582✔
374
    SCEnter();
35,582✔
375

376
    SCConfNode *rule_files;
35,582✔
377
    SCConfNode *file = NULL;
35,582✔
378
    SigFileLoaderStat *sig_stat = &de_ctx->sig_stat;
35,582✔
379
    int ret = 0;
35,582✔
380
    char *sfile = NULL;
35,582✔
381
    char varname[128] = "rule-files";
35,582✔
382
    int good_sigs = 0;
35,582✔
383
    int bad_sigs = 0;
35,582✔
384
    int skipped_sigs = 0;
35,582✔
385

386
    if (strlen(de_ctx->config_prefix) > 0) {
35,582✔
387
        snprintf(varname, sizeof(varname), "%s.rule-files", de_ctx->config_prefix);
17✔
388
    }
17✔
389

390
    if (SCRunmodeGet() == RUNMODE_ENGINE_ANALYSIS) {
35,582✔
UNCOV
391
        SetupEngineAnalysis(de_ctx, &fp_engine_analysis_set, &rule_engine_analysis_set);
×
UNCOV
392
    }
×
393

394
    if (EngineModeIsFirewall() || de_ctx->firewall_rule_file_exclusive) {
35,582✔
UNCOV
395
        if (LoadFirewallRuleFiles(de_ctx) < 0) {
×
396
            if (de_ctx->failure_fatal) {
×
397
                exit(EXIT_FAILURE);
×
398
            }
×
399
            ret = -1;
×
400
            goto end;
×
401
        }
×
402

403
        /* skip regular rules if we used a exclusive firewall rule file */
UNCOV
404
        if (!sig_file_exclusive && de_ctx->firewall_rule_file_exclusive) {
×
UNCOV
405
            ret = 0;
×
UNCOV
406
            goto skip_regular_rules;
×
UNCOV
407
        }
×
UNCOV
408
    }
×
409

410
    /* ok, let's load signature files from the general config */
411
    if (!(sig_file != NULL && sig_file_exclusive)) {
35,582✔
412
        rule_files = SCConfGetNode(varname);
25✔
413
        if (rule_files != NULL) {
25✔
414
            if (!SCConfNodeIsSequence(rule_files)) {
21✔
415
                SCLogWarning("Invalid rule-files configuration section: "
×
416
                             "expected a list of filenames.");
×
417
            } else {
21✔
418
                TAILQ_FOREACH(file, &rule_files->head, next) {
21✔
419
                    sfile = DetectLoadCompleteSigPath(de_ctx, file->val);
21✔
420
                    good_sigs = bad_sigs = skipped_sigs = 0;
21✔
421
                    ret = ProcessSigFiles(
21✔
422
                            de_ctx, sfile, sig_stat, &good_sigs, &bad_sigs, &skipped_sigs);
21✔
423
                    SCFree(sfile);
21✔
424

425
                    if (de_ctx->failure_fatal && ret != 0) {
21✔
426
                        /* Some rules failed to load, just exit as
427
                         * errors would have already been logged. */
428
                        exit(EXIT_FAILURE);
×
429
                    }
×
430

431
                    if (good_sigs == 0) {
21✔
UNCOV
432
                        SCLogConfig("No rules loaded from %s.", file->val);
×
UNCOV
433
                    }
×
434
                }
21✔
435
            }
21✔
436
        }
21✔
437
    }
25✔
438

439
    /* If a Signature file is specified from command-line, parse it too */
440
    if (sig_file != NULL) {
35,582✔
441
        ret = ProcessSigFiles(de_ctx, sig_file, sig_stat, &good_sigs, &bad_sigs, &skipped_sigs);
35,557✔
442

443
        if (ret != 0) {
35,557✔
444
            if (de_ctx->failure_fatal) {
1✔
445
                exit(EXIT_FAILURE);
×
446
            }
×
447
        }
1✔
448

449
        if (good_sigs == 0) {
35,557✔
450
            SCLogConfig("No rules loaded from %s", sig_file);
14,984✔
451
        }
14,984✔
452
    }
35,557✔
453

454
skip_regular_rules:
35,582✔
455
    /* now we should have signatures to work with */
456
    if (sig_stat->good_sigs_total <= 0) {
35,582✔
457
        if (sig_stat->total_files > 0) {
14,988✔
458
            SCLogWarning(
14,966✔
459
                    "%d rule files specified, but no rules were loaded!", sig_stat->total_files);
14,966✔
460
        } else {
14,988✔
461
            SCLogInfo("No signatures supplied.");
22✔
462
            goto end;
22✔
463
        }
22✔
464
    } else {
20,595✔
465
        /* we report the total of files and rules successfully loaded and failed */
466
        if (strlen(de_ctx->config_prefix) > 0) {
20,594✔
467
            SCLogInfo("tenant id %d:  %" PRId32 " rule files processed. %" PRId32
14✔
468
                      " rules successfully loaded, %" PRId32 " rules failed, %" PRId32
14✔
469
                      " rules skipped",
14✔
470
                    de_ctx->tenant_id, sig_stat->total_files, sig_stat->good_sigs_total,
14✔
471
                    sig_stat->bad_sigs_total, sig_stat->skipped_sigs_total);
14✔
472
        } else {
20,587✔
473
            SCLogInfo("%" PRId32 " rule files processed. %" PRId32
20,580✔
474
                      " rules successfully loaded, %" PRId32 " rules failed, %" PRId32
20,580✔
475
                      " rules skipped",
20,580✔
476
                    sig_stat->total_files, sig_stat->good_sigs_total, sig_stat->bad_sigs_total,
20,580✔
477
                    sig_stat->skipped_sigs_total);
20,580✔
478
        }
20,580✔
479
        if (de_ctx->requirements != NULL && sig_stat->skipped_sigs_total > 0) {
20,594✔
480
            SCDetectRequiresStatusLog(de_ctx->requirements, PROG_VER,
309✔
481
                    strlen(de_ctx->config_prefix) > 0 ? de_ctx->tenant_id : 0);
309✔
482
        }
309✔
483
    }
20,594✔
484

485
    if ((sig_stat->bad_sigs_total || sig_stat->bad_files) && de_ctx->failure_fatal) {
35,560✔
UNCOV
486
        ret = -1;
×
UNCOV
487
        goto end;
×
UNCOV
488
    }
×
489

490
    SCSigRegisterSignatureOrderingFuncs(de_ctx);
35,560✔
491
    SCSigOrderSignatures(de_ctx);
35,560✔
492
    SCSigSignatureOrderingModuleCleanup(de_ctx);
35,560✔
493

494
    if (SCThresholdConfInitContext(de_ctx) < 0) {
35,560✔
UNCOV
495
        ret = -1;
×
UNCOV
496
        goto end;
×
UNCOV
497
    }
×
498

499
    /* Setup the signature group lookup structure and pattern matchers */
500
    if (SigGroupBuild(de_ctx) < 0)
35,560✔
501
        goto end;
×
502

503
    ret = 0;
35,560✔
504

505
 end:
35,582✔
506
    gettimeofday(&de_ctx->last_reload, NULL);
35,582✔
507
    if (SCRunmodeGet() == RUNMODE_ENGINE_ANALYSIS) {
35,582✔
UNCOV
508
        CleanupEngineAnalysis(de_ctx);
×
UNCOV
509
    }
×
510

511
    DetectParseDupSigHashFree(de_ctx);
35,582✔
512
    SCReturnInt(ret);
35,582✔
513
}
35,560✔
514

515
#define NLOADERS 4
1✔
516
static DetectLoaderControl *loaders = NULL;
517
static int cur_loader = 0;
518
static void TmThreadWakeupDetectLoaderThreads(void);
519
static int num_loaders = NLOADERS;
520

521
/** \param loader -1 for auto select
522
 *  \retval loader_id or negative in case of error */
523
int DetectLoaderQueueTask(int loader_id, LoaderFunc Func, void *func_ctx, LoaderFreeFunc FreeFunc)
524
{
9✔
525
    if (loader_id == -1) {
9✔
526
        loader_id = cur_loader;
3✔
527
        cur_loader++;
3✔
528
        if (cur_loader >= num_loaders)
3✔
529
            cur_loader = 0;
1✔
530
    }
3✔
531
    if (loader_id >= num_loaders || loader_id < 0) {
9✔
532
        return -ERANGE;
×
533
    }
×
534

535
    DetectLoaderControl *loader = &loaders[loader_id];
9✔
536

537
    DetectLoaderTask *t = SCCalloc(1, sizeof(*t));
9✔
538
    if (t == NULL)
9✔
539
        return -ENOMEM;
×
540

541
    t->Func = Func;
9✔
542
    t->ctx = func_ctx;
9✔
543
    t->FreeFunc = FreeFunc;
9✔
544

545
    SCMutexLock(&loader->m);
9✔
546
    TAILQ_INSERT_TAIL(&loader->task_list, t, next);
9✔
547
    SCMutexUnlock(&loader->m);
9✔
548

549
    TmThreadWakeupDetectLoaderThreads();
9✔
550

551
    SCLogDebug("%d %p %p", loader_id, Func, func_ctx);
9✔
552
    return loader_id;
9✔
553
}
9✔
554

555
/** \brief wait for loader tasks to complete
556
 *  \retval result 0 for ok, -1 for errors */
557
int DetectLoadersSync(void)
558
{
6✔
559
    SCLogDebug("waiting");
6✔
560
    int errors = 0;
6✔
561
    for (int i = 0; i < num_loaders; i++) {
24✔
562
        bool done = false;
18✔
563

564
        DetectLoaderControl *loader = &loaders[i];
18✔
565
        while (!done) {
44,245✔
566
            SCMutexLock(&loader->m);
44,227✔
567
            if (TAILQ_EMPTY(&loader->task_list)) {
44,227✔
568
                done = true;
18✔
569
            }
18✔
570
            SCMutexUnlock(&loader->m);
44,227✔
571
            if (!done) {
44,227✔
572
                /* nudge thread in case it's sleeping */
573
                SCCtrlMutexLock(loader->tv->ctrl_mutex);
44,209✔
574
                pthread_cond_broadcast(loader->tv->ctrl_cond);
44,209✔
575
                SCCtrlMutexUnlock(loader->tv->ctrl_mutex);
44,209✔
576
            }
44,209✔
577
        }
44,227✔
578
        SCMutexLock(&loader->m);
18✔
579
        if (loader->result != 0) {
18✔
580
            errors++;
×
581
            loader->result = 0;
×
582
        }
×
583
        SCMutexUnlock(&loader->m);
18✔
584
    }
18✔
585
    if (errors) {
6✔
586
        SCLogError("%d loaders reported errors", errors);
×
587
        return -1;
×
588
    }
×
589
    SCLogDebug("done");
6✔
590
    return 0;
6✔
591
}
6✔
592

593
static void DetectLoaderInit(DetectLoaderControl *loader)
594
{
3✔
595
    memset(loader, 0x00, sizeof(*loader));
3✔
596
    SCMutexInit(&loader->m, NULL);
3✔
597
    TAILQ_INIT(&loader->task_list);
3✔
598
}
3✔
599

600
void DetectLoadersInit(void)
601
{
1✔
602
    intmax_t setting = NLOADERS;
1✔
603
    (void)SCConfGetInt("multi-detect.loaders", &setting);
1✔
604

605
    if (setting < 1 || setting > 1024) {
1✔
606
        FatalError("invalid multi-detect.loaders setting %" PRIdMAX, setting);
×
607
    }
×
608

609
    num_loaders = (int32_t)setting;
1✔
610
    SCLogInfo("using %d detect loader threads", num_loaders);
1✔
611

612
    BUG_ON(loaders != NULL);
1✔
613
    loaders = SCCalloc(num_loaders, sizeof(DetectLoaderControl));
1✔
614
    BUG_ON(loaders == NULL);
1✔
615

616
    for (int i = 0; i < num_loaders; i++) {
4✔
617
        DetectLoaderInit(&loaders[i]);
3✔
618
    }
3✔
619
}
1✔
620

621
/**
622
 * \brief Unpauses all threads present in tv_root
623
 */
624
static void TmThreadWakeupDetectLoaderThreads(void)
625
{
9✔
626
    SCMutexLock(&tv_root_lock);
9✔
627
    for (int i = 0; i < TVT_MAX; i++) {
36✔
628
        ThreadVars *tv = tv_root[i];
27✔
629
        while (tv != NULL) {
134✔
630
            if (strncmp(tv->name,"DL#",3) == 0) {
107✔
631
                BUG_ON(tv->ctrl_cond == NULL);
27✔
632
                SCCtrlMutexLock(tv->ctrl_mutex);
27✔
633
                pthread_cond_broadcast(tv->ctrl_cond);
27✔
634
                SCCtrlMutexUnlock(tv->ctrl_mutex);
27✔
635
            }
27✔
636
            tv = tv->next;
107✔
637
        }
107✔
638
    }
27✔
639
    SCMutexUnlock(&tv_root_lock);
9✔
640
}
9✔
641

642
/**
643
 * \brief Unpauses all threads present in tv_root
644
 */
645
void TmThreadContinueDetectLoaderThreads(void)
646
{
1✔
647
    SCMutexLock(&tv_root_lock);
1✔
648
    for (int i = 0; i < TVT_MAX; i++) {
4✔
649
        ThreadVars *tv = tv_root[i];
3✔
650
        while (tv != NULL) {
6✔
651
            if (strncmp(tv->name,"DL#",3) == 0)
3✔
652
                TmThreadContinue(tv);
3✔
653

654
            tv = tv->next;
3✔
655
        }
3✔
656
    }
3✔
657
    SCMutexUnlock(&tv_root_lock);
1✔
658
}
1✔
659

660
SC_ATOMIC_DECLARE(int, detect_loader_cnt);
661

662
typedef struct DetectLoaderThreadData_ {
663
    uint32_t instance;
664
} DetectLoaderThreadData;
665

666
static TmEcode DetectLoaderThreadInit(ThreadVars *t, const void *initdata, void **data)
667
{
3✔
668
    DetectLoaderThreadData *ftd = SCCalloc(1, sizeof(DetectLoaderThreadData));
3✔
669
    if (ftd == NULL)
3✔
670
        return TM_ECODE_FAILED;
×
671

672
    ftd->instance = SC_ATOMIC_ADD(detect_loader_cnt, 1); /* id's start at 0 */
3✔
673
    SCLogDebug("detect loader instance %u", ftd->instance);
3✔
674

675
    /* pass thread data back to caller */
676
    *data = ftd;
3✔
677

678
    DetectLoaderControl *loader = &loaders[ftd->instance];
3✔
679
    loader->tv = t;
3✔
680

681
    return TM_ECODE_OK;
3✔
682
}
3✔
683

684
static TmEcode DetectLoaderThreadDeinit(ThreadVars *t, void *data)
685
{
3✔
686
    SCFree(data);
3✔
687
    return TM_ECODE_OK;
3✔
688
}
3✔
689

690

691
static TmEcode DetectLoader(ThreadVars *th_v, void *thread_data)
692
{
3✔
693
    DetectLoaderThreadData *ftd = (DetectLoaderThreadData *)thread_data;
3✔
694
    BUG_ON(ftd == NULL);
3✔
695

696
    TmThreadsSetFlag(th_v, THV_INIT_DONE | THV_RUNNING);
3✔
697
    SCLogDebug("loader thread started");
3✔
698
    bool run = TmThreadsWaitForUnpause(th_v);
3✔
699
    while (run) {
15✔
700
        /* see if we have tasks */
701

702
        DetectLoaderControl *loader = &loaders[ftd->instance];
12✔
703
        SCMutexLock(&loader->m);
12✔
704

705
        DetectLoaderTask *task = NULL, *tmptask = NULL;
12✔
706
        TAILQ_FOREACH_SAFE(task, &loader->task_list, next, tmptask) {
12✔
707
            int r = task->Func(task->ctx, ftd->instance);
9✔
708
            loader->result |= r;
9✔
709
            TAILQ_REMOVE(&loader->task_list, task, next);
9✔
710
            task->FreeFunc(task->ctx);
9✔
711
            SCFree(task);
9✔
712
        }
9✔
713

714
        SCMutexUnlock(&loader->m);
12✔
715

716
        /* just wait until someone wakes us up */
717
        SCCtrlMutexLock(th_v->ctrl_mutex);
12✔
718
        int rc = 0;
12✔
719
        while (rc == 0) {
33✔
720
            if (TmThreadsCheckFlag(th_v, THV_KILL)) {
33✔
721
                run = false;
3✔
722
                break;
3✔
723
            }
3✔
724
            SCMutexLock(&loader->m);
30✔
725
            bool has_work = loader->task_list.tqh_first != NULL;
30✔
726
            SCMutexUnlock(&loader->m);
30✔
727
            if (has_work)
30✔
728
                break;
9✔
729

730
            rc = SCCtrlCondWait(th_v->ctrl_cond, th_v->ctrl_mutex);
21✔
731
        }
21✔
732
        SCCtrlMutexUnlock(th_v->ctrl_mutex);
12✔
733

734
        SCLogDebug("woke up...");
12✔
735
    }
12✔
736

737
    TmThreadsSetFlag(th_v, THV_RUNNING_DONE);
3✔
738
    TmThreadWaitForFlag(th_v, THV_DEINIT);
3✔
739
    TmThreadsSetFlag(th_v, THV_CLOSED);
3✔
740

741
    return TM_ECODE_OK;
3✔
742
}
3✔
743

744
/** \brief spawn the detect loader manager thread */
745
void DetectLoaderThreadSpawn(void)
746
{
1✔
747
    for (int i = 0; i < num_loaders; i++) {
4✔
748
        char name[TM_THREAD_NAME_MAX];
3✔
749
        snprintf(name, sizeof(name), "%s#%02d", thread_name_detect_loader, i+1);
3✔
750

751
        ThreadVars *tv_loader = TmThreadCreateCmdThreadByName(name, "DetectLoader", 1);
3✔
752
        if (tv_loader == NULL) {
3✔
753
            FatalError("failed to create thread %s", name);
×
754
        }
×
755
        if (TmThreadSpawn(tv_loader) != TM_ECODE_OK) {
3✔
756
            FatalError("failed to create spawn %s", name);
×
757
        }
×
758
    }
3✔
759
}
1✔
760

761
void TmModuleDetectLoaderRegister (void)
762
{
1✔
763
    tmm_modules[TMM_DETECTLOADER].name = "DetectLoader";
1✔
764
    tmm_modules[TMM_DETECTLOADER].ThreadInit = DetectLoaderThreadInit;
1✔
765
    tmm_modules[TMM_DETECTLOADER].ThreadDeinit = DetectLoaderThreadDeinit;
1✔
766
    tmm_modules[TMM_DETECTLOADER].Management = DetectLoader;
1✔
767
    tmm_modules[TMM_DETECTLOADER].cap_flags = 0;
1✔
768
    tmm_modules[TMM_DETECTLOADER].flags = TM_FLAG_MANAGEMENT_TM;
1✔
769
    SCLogDebug("%s registered", tmm_modules[TMM_DETECTLOADER].name);
1✔
770

771
    SC_ATOMIC_INIT(detect_loader_cnt);
1✔
772
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc