/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 | } |