/src/suricata8/src/output-filestore.c
Line | Count | Source |
1 | | /* Copyright (C) 2018-2022 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 | | #include "suricata-common.h" |
19 | | #include "output-filestore.h" |
20 | | |
21 | | #include "stream-tcp.h" |
22 | | |
23 | | #include "feature.h" |
24 | | |
25 | | #include "output.h" |
26 | | #include "output-json-file.h" |
27 | | |
28 | | #include "util-conf.h" |
29 | | #include "util-misc.h" |
30 | | #include "util-path.h" |
31 | | #include "util-print.h" |
32 | | |
33 | 78 | #define MODULE_NAME "OutputFilestore" |
34 | | |
35 | | /* Create a filestore specific PATH_MAX that is less than the system |
36 | | * PATH_MAX to prevent newer gcc truncation warnings with snprint. */ |
37 | | #define SHA256_STRING_LEN (SC_SHA256_LEN * 2) |
38 | | #define LEAF_DIR_MAX_LEN 4 |
39 | | #define FILESTORE_PREFIX_MAX (PATH_MAX - SHA256_STRING_LEN - LEAF_DIR_MAX_LEN) |
40 | | |
41 | | /* The default log directory, relative to the default log |
42 | | * directory. */ |
43 | | static const char *default_log_dir = "filestore"; |
44 | | |
45 | | /* Atomic counter of simultaneously open files. */ |
46 | | static SC_ATOMIC_DECLARE(uint32_t, filestore_open_file_cnt); |
47 | | |
48 | | typedef struct OutputFilestoreCtx_ { |
49 | | char prefix[FILESTORE_PREFIX_MAX]; |
50 | | char tmpdir[FILESTORE_PREFIX_MAX]; |
51 | | bool fileinfo; |
52 | | HttpXFFCfg *xff_cfg; |
53 | | } OutputFilestoreCtx; |
54 | | |
55 | | typedef struct OutputFilestoreLogThread_ { |
56 | | OutputFilestoreCtx *ctx; |
57 | | uint16_t counter_max_hits; |
58 | | uint16_t fs_error_counter; |
59 | | } OutputFilestoreLogThread; |
60 | | |
61 | | enum WarnOnceTypes { |
62 | | WOT_OPEN, |
63 | | WOT_WRITE, |
64 | | WOT_UNLINK, |
65 | | WOT_RENAME, |
66 | | WOT_SNPRINTF, |
67 | | |
68 | | WOT_MAX, |
69 | | }; |
70 | | |
71 | | /* For WARN_ONCE, a record of warnings that have already been |
72 | | * issued. */ |
73 | | static thread_local bool once_errs[WOT_MAX]; |
74 | | |
75 | | #define WARN_ONCE(wot_type, ...) \ |
76 | 0 | do { \ |
77 | 0 | if (!once_errs[wot_type]) { \ |
78 | 0 | once_errs[wot_type] = true; \ |
79 | 0 | SCLogWarning(__VA_ARGS__); \ |
80 | 0 | } \ |
81 | 0 | } while (0) |
82 | | |
83 | | static uint64_t OutputFilestoreOpenFilesCounter(void) |
84 | 0 | { |
85 | 0 | return SC_ATOMIC_GET(filestore_open_file_cnt); |
86 | 0 | } |
87 | | |
88 | | static uint32_t g_file_store_max_open_files = 0; |
89 | | |
90 | | static void FileSetMaxOpenFiles(uint32_t count) |
91 | 0 | { |
92 | 0 | g_file_store_max_open_files = count; |
93 | 0 | } |
94 | | |
95 | | static uint32_t FileGetMaxOpenFiles(void) |
96 | 238k | { |
97 | 238k | return g_file_store_max_open_files; |
98 | 238k | } |
99 | | |
100 | | /** |
101 | | * \brief Update the timestamps on a file to match those of another |
102 | | * file. |
103 | | * |
104 | | * \param src_filename Filename to use as timestamp source. |
105 | | * \param filename Filename to apply timestamps to. |
106 | | */ |
107 | | static void OutputFilestoreUpdateFileTime(const char *src_filename, |
108 | | const char *filename) |
109 | 106k | { |
110 | 106k | struct stat sb; |
111 | 106k | if (stat(src_filename, &sb) != 0) { |
112 | 0 | SCLogDebug("Failed to stat %s: %s", filename, strerror(errno)); |
113 | 0 | return; |
114 | 0 | } |
115 | 106k | struct utimbuf utimbuf = { |
116 | 106k | .actime = sb.st_atime, |
117 | 106k | .modtime = sb.st_mtime, |
118 | 106k | }; |
119 | 106k | if (utime(filename, &utimbuf) != 0) { |
120 | 0 | SCLogDebug("Failed to update file timestamps: %s: %s", filename, |
121 | 0 | strerror(errno)); |
122 | 0 | } |
123 | 106k | } |
124 | | |
125 | | static void OutputFilestoreFinalizeFiles(ThreadVars *tv, const OutputFilestoreLogThread *oft, |
126 | | const OutputFilestoreCtx *ctx, const Packet *p, File *ff, void *tx, const uint64_t tx_id, |
127 | | uint8_t dir) |
128 | 46 | { |
129 | | /* Stringify the SHA256 which will be used in the final |
130 | | * filename. */ |
131 | 46 | char sha256string[(SC_SHA256_LEN * 2) + 1]; |
132 | 46 | PrintHexString(sha256string, sizeof(sha256string), ff->sha256, |
133 | 46 | sizeof(ff->sha256)); |
134 | | |
135 | 46 | char tmp_filename[PATH_MAX] = ""; |
136 | 46 | snprintf(tmp_filename, sizeof(tmp_filename), "%s/file.%u", ctx->tmpdir, |
137 | 46 | ff->file_store_id); |
138 | | |
139 | 46 | char final_filename[PATH_MAX] = ""; |
140 | 46 | snprintf(final_filename, sizeof(final_filename), "%s/%c%c/%s", |
141 | 46 | ctx->prefix, sha256string[0], sha256string[1], sha256string); |
142 | | |
143 | 46 | if (SCPathExists(final_filename)) { |
144 | 34 | OutputFilestoreUpdateFileTime(tmp_filename, final_filename); |
145 | 34 | if (unlink(tmp_filename) != 0) { |
146 | 0 | StatsIncr(tv, oft->fs_error_counter); |
147 | 0 | WARN_ONCE(WOT_UNLINK, "Failed to remove temporary file %s: %s", tmp_filename, |
148 | 0 | strerror(errno)); |
149 | 0 | } |
150 | 34 | } else if (rename(tmp_filename, final_filename) != 0) { |
151 | 0 | StatsIncr(tv, oft->fs_error_counter); |
152 | 0 | WARN_ONCE(WOT_RENAME, "Failed to rename %s to %s: %s", tmp_filename, final_filename, |
153 | 0 | strerror(errno)); |
154 | 0 | if (unlink(tmp_filename) != 0) { |
155 | | /* Just increment, don't log as has_fs_errors would |
156 | | * already be set above. */ |
157 | 0 | StatsIncr(tv, oft->fs_error_counter); |
158 | 0 | } |
159 | 0 | return; |
160 | 0 | } |
161 | | |
162 | 46 | if (ctx->fileinfo) { |
163 | 0 | char js_metadata_filename[PATH_MAX]; |
164 | 0 | if (snprintf(js_metadata_filename, sizeof(js_metadata_filename), "%s.%" PRIuMAX ".%u.json", |
165 | 0 | final_filename, (uintmax_t)SCTIME_SECS(p->ts), |
166 | 0 | ff->file_store_id) == (int)sizeof(js_metadata_filename)) { |
167 | 0 | WARN_ONCE(WOT_SNPRINTF, "Failed to write file info record. Output filename truncated."); |
168 | 0 | } else { |
169 | 0 | SCJsonBuilder *js_fileinfo = |
170 | 0 | JsonBuildFileInfoRecord(p, ff, tx, tx_id, true, dir, ctx->xff_cfg, NULL); |
171 | 0 | if (likely(js_fileinfo != NULL)) { |
172 | 0 | SCJbClose(js_fileinfo); |
173 | 0 | FILE *out = fopen(js_metadata_filename, "w"); |
174 | 0 | if (out != NULL) { |
175 | 0 | size_t js_len = SCJbLen(js_fileinfo); |
176 | 0 | fwrite(SCJbPtr(js_fileinfo), js_len, 1, out); |
177 | 0 | fclose(out); |
178 | 0 | } |
179 | 0 | SCJbFree(js_fileinfo); |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | 46 | } |
184 | | |
185 | | static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet *p, File *ff, |
186 | | void *tx, const uint64_t tx_id, const uint8_t *data, uint32_t data_len, uint8_t flags, |
187 | | uint8_t dir) |
188 | 2.99k | { |
189 | 2.99k | SCEnter(); |
190 | 2.99k | OutputFilestoreLogThread *aft = (OutputFilestoreLogThread *)thread_data; |
191 | 2.99k | OutputFilestoreCtx *ctx = aft->ctx; |
192 | 2.99k | char filename[PATH_MAX] = ""; |
193 | 2.99k | int file_fd = -1; |
194 | | |
195 | 2.99k | SCLogDebug("ff %p, data %p, data_len %u", ff, data, data_len); |
196 | | |
197 | 2.99k | if (flags & OUTPUT_FILEDATA_FLAG_OPEN) { |
198 | 160 | snprintf(filename, sizeof(filename), "%s/file.%u", ctx->tmpdir, ff->file_store_id); |
199 | 160 | file_fd = open(filename, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, |
200 | 160 | 0644); |
201 | 160 | if (file_fd == -1) { |
202 | 0 | StatsIncr(tv, aft->fs_error_counter); |
203 | 0 | SCLogWarning("Filestore (v2) failed to create %s: %s", filename, strerror(errno)); |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | 160 | if (SC_ATOMIC_GET(filestore_open_file_cnt) < FileGetMaxOpenFiles()) { |
208 | 0 | SC_ATOMIC_ADD(filestore_open_file_cnt, 1); |
209 | 0 | ff->fd = file_fd; |
210 | 160 | } else { |
211 | 160 | if (FileGetMaxOpenFiles() > 0) { |
212 | 0 | StatsIncr(tv, aft->counter_max_hits); |
213 | 0 | } |
214 | 160 | ff->fd = -1; |
215 | 160 | } |
216 | | /* we can get called with a NULL ffd when we need to close */ |
217 | 2.83k | } else if (data != NULL) { |
218 | 1.46k | if (ff->fd == -1) { |
219 | 1.46k | snprintf(filename, sizeof(filename), "%s/file.%u", ctx->tmpdir, ff->file_store_id); |
220 | 1.46k | file_fd = open(filename, O_APPEND | O_NOFOLLOW | O_WRONLY); |
221 | 1.46k | if (file_fd == -1) { |
222 | 0 | StatsIncr(tv, aft->fs_error_counter); |
223 | 0 | WARN_ONCE(WOT_OPEN, "Filestore (v2) failed to open file %s: %s", filename, |
224 | 0 | strerror(errno)); |
225 | 0 | return -1; |
226 | 0 | } |
227 | 1.46k | } else { |
228 | 0 | file_fd = ff->fd; |
229 | 0 | } |
230 | 1.46k | } |
231 | | |
232 | 2.99k | if (file_fd != -1) { |
233 | 1.62k | ssize_t r = write(file_fd, (const void *)data, (size_t)data_len); |
234 | 1.62k | if (r == -1) { |
235 | 0 | snprintf(filename, sizeof(filename), "%s/file.%u", ctx->tmpdir, ff->file_store_id); |
236 | 0 | StatsIncr(tv, aft->fs_error_counter); |
237 | 0 | WARN_ONCE(WOT_WRITE, "Filestore (v2) failed to write to %s: %s", filename, |
238 | 0 | strerror(errno)); |
239 | 0 | if (ff->fd != -1) { |
240 | 0 | SC_ATOMIC_SUB(filestore_open_file_cnt, 1); |
241 | 0 | } |
242 | 0 | ff->fd = -1; |
243 | 0 | } |
244 | 1.62k | if (ff->fd == -1) { |
245 | 1.62k | close(file_fd); |
246 | 1.62k | } |
247 | 1.62k | } |
248 | | |
249 | 2.99k | if (flags & OUTPUT_FILEDATA_FLAG_CLOSE) { |
250 | 46 | if (ff->fd != -1) { |
251 | 0 | close(ff->fd); |
252 | 0 | ff->fd = -1; |
253 | 0 | SC_ATOMIC_SUB(filestore_open_file_cnt, 1); |
254 | 0 | } |
255 | 46 | OutputFilestoreFinalizeFiles(tv, aft, ctx, p, ff, tx, tx_id, dir); |
256 | 46 | } |
257 | | |
258 | 2.99k | return 0; |
259 | 2.99k | } |
260 | | |
261 | | static TmEcode OutputFilestoreLogThreadInit(ThreadVars *t, const void *initdata, |
262 | | void **data) |
263 | 4 | { |
264 | 4 | OutputFilestoreLogThread *aft = SCCalloc(1, sizeof(OutputFilestoreLogThread)); |
265 | 4 | if (unlikely(aft == NULL)) |
266 | 0 | return TM_ECODE_FAILED; |
267 | | |
268 | 4 | if (initdata == NULL) { |
269 | 0 | SCLogDebug("Error getting context for LogFileStore. \"initdata\" argument NULL"); |
270 | 0 | SCFree(aft); |
271 | 0 | return TM_ECODE_FAILED; |
272 | 0 | } |
273 | | |
274 | 4 | OutputFilestoreCtx *ctx = ((OutputCtx *)initdata)->data; |
275 | 4 | aft->ctx = ctx; |
276 | | |
277 | 4 | aft->counter_max_hits = |
278 | 4 | StatsRegisterCounter("file_store.open_files_max_hit", t); |
279 | | |
280 | | /* File system type errors (open, write, rename) will only be |
281 | | * logged once. But this stat will be incremented for every |
282 | | * occurrence. */ |
283 | 4 | aft->fs_error_counter = StatsRegisterCounter("file_store.fs_errors", t); |
284 | | |
285 | 4 | *data = (void *)aft; |
286 | 4 | return TM_ECODE_OK; |
287 | 4 | } |
288 | | |
289 | | static TmEcode OutputFilestoreLogThreadDeinit(ThreadVars *t, void *data) |
290 | 0 | { |
291 | 0 | OutputFilestoreLogThread *aft = (OutputFilestoreLogThread *)data; |
292 | 0 | if (aft == NULL) { |
293 | 0 | return TM_ECODE_OK; |
294 | 0 | } |
295 | | |
296 | | /* clear memory */ |
297 | 0 | memset(aft, 0, sizeof(OutputFilestoreLogThread)); |
298 | |
|
299 | 0 | SCFree(aft); |
300 | 0 | return TM_ECODE_OK; |
301 | 0 | } |
302 | | |
303 | | static void OutputFilestoreLogDeInitCtx(OutputCtx *output_ctx) |
304 | 0 | { |
305 | 0 | OutputFilestoreCtx *ctx = (OutputFilestoreCtx *)output_ctx->data; |
306 | 0 | if (ctx->xff_cfg != NULL) { |
307 | 0 | SCFree(ctx->xff_cfg); |
308 | 0 | } |
309 | 0 | SCFree(ctx); |
310 | 0 | SCFree(output_ctx); |
311 | 0 | } |
312 | | |
313 | | static void GetLogDirectory(const SCConfNode *conf, char *out, size_t out_size) |
314 | 2 | { |
315 | 2 | const char *log_base_dir = SCConfNodeLookupChildValue(conf, "dir"); |
316 | 2 | if (log_base_dir == NULL) { |
317 | 2 | SCLogConfig("Filestore (v2) default log directory %s", default_log_dir); |
318 | 2 | log_base_dir = default_log_dir; |
319 | 2 | } |
320 | 2 | if (PathIsAbsolute(log_base_dir)) { |
321 | 0 | strlcpy(out, log_base_dir, out_size); |
322 | 2 | } else { |
323 | 2 | const char *default_log_prefix = SCConfigGetLogDirectory(); |
324 | 2 | snprintf(out, out_size, "%s/%s", default_log_prefix, log_base_dir); |
325 | 2 | } |
326 | 2 | } |
327 | | |
328 | | static bool InitFilestoreDirectory(const char *dir) |
329 | 2 | { |
330 | 2 | const uint8_t dir_count = 0xff; |
331 | | |
332 | 2 | if (!SCPathExists(dir)) { |
333 | 1 | SCLogInfo("Filestore (v2) creating directory %s", dir); |
334 | 1 | if (SCCreateDirectoryTree(dir, true) != 0) { |
335 | 0 | SCLogError("Filestore (v2) failed to create directory %s: %s", dir, strerror(errno)); |
336 | 0 | return false; |
337 | 0 | } |
338 | 1 | } |
339 | | |
340 | 514 | for (int i = 0; i <= dir_count; i++) { |
341 | 512 | char leaf[PATH_MAX]; |
342 | 512 | int n = snprintf(leaf, sizeof(leaf), "%s/%02x", dir, i); |
343 | 512 | if (n < 0 || n >= PATH_MAX) { |
344 | 0 | SCLogError("Filestore (v2) failed to create leaf directory: " |
345 | 0 | "path too long"); |
346 | 0 | return false; |
347 | 0 | } |
348 | 512 | if (!SCPathExists(leaf)) { |
349 | 256 | SCLogInfo("Filestore (v2) creating directory %s", leaf); |
350 | 256 | if (SCDefaultMkDir(leaf) != 0) { |
351 | 0 | SCLogError( |
352 | 0 | "Filestore (v2) failed to create directory %s: %s", leaf, strerror(errno)); |
353 | 0 | return false; |
354 | 0 | } |
355 | 256 | } |
356 | 512 | } |
357 | | |
358 | | /* Make sure the tmp directory exists. */ |
359 | 2 | char tmpdir[PATH_MAX]; |
360 | 2 | int n = snprintf(tmpdir, sizeof(tmpdir), "%s/tmp", dir); |
361 | 2 | if (n < 0 || n >= PATH_MAX) { |
362 | 0 | SCLogError("Filestore (v2) failed to create tmp directory: path too long"); |
363 | 0 | return false; |
364 | 0 | } |
365 | 2 | if (!SCPathExists(tmpdir)) { |
366 | 1 | SCLogInfo("Filestore (v2) creating directory %s", tmpdir); |
367 | 1 | if (SCDefaultMkDir(tmpdir) != 0) { |
368 | 0 | SCLogError("Filestore (v2) failed to create directory %s: %s", tmpdir, strerror(errno)); |
369 | 0 | return false; |
370 | 0 | } |
371 | 1 | } |
372 | | |
373 | 2 | return true; |
374 | 2 | } |
375 | | |
376 | | /** \brief Create a new http log OutputFilestoreCtx. |
377 | | * \param conf Pointer to ConfNode containing this loggers configuration. |
378 | | * \return NULL if failure, OutputFilestoreCtx* to the file_ctx if succesful |
379 | | * */ |
380 | | static OutputInitResult OutputFilestoreLogInitCtx(SCConfNode *conf) |
381 | 2 | { |
382 | 2 | OutputInitResult result = { NULL, false }; |
383 | | |
384 | 2 | intmax_t version = 0; |
385 | 2 | if (!SCConfGetChildValueInt(conf, "version", &version) || version < 2) { |
386 | 0 | SCLogWarning("File-store v1 has been removed. Please update to file-store v2."); |
387 | 0 | return result; |
388 | 0 | } |
389 | | |
390 | 2 | if (RunModeOutputFiledataEnabled()) { |
391 | 0 | SCLogWarning("A file data logger is already enabled. Filestore (v2) " |
392 | 0 | "will not be enabled."); |
393 | 0 | return result; |
394 | 0 | } |
395 | | |
396 | 2 | char log_directory[PATH_MAX] = ""; |
397 | 2 | GetLogDirectory(conf, log_directory, sizeof(log_directory)); |
398 | 2 | if (!InitFilestoreDirectory(log_directory)) { |
399 | 0 | return result; |
400 | 0 | } |
401 | | |
402 | 2 | OutputFilestoreCtx *ctx = SCCalloc(1, sizeof(*ctx)); |
403 | 2 | if (unlikely(ctx == NULL)) { |
404 | 0 | return result; |
405 | 0 | } |
406 | | |
407 | 2 | strlcpy(ctx->prefix, log_directory, sizeof(ctx->prefix)); |
408 | 2 | int written = snprintf(ctx->tmpdir, sizeof(ctx->tmpdir) - 1, "%s/tmp", |
409 | 2 | log_directory); |
410 | 2 | if (written == sizeof(ctx->tmpdir)) { |
411 | 0 | SCLogError("File-store output directory overflow."); |
412 | 0 | SCFree(ctx); |
413 | 0 | return result; |
414 | 0 | } |
415 | | |
416 | 2 | ctx->xff_cfg = SCCalloc(1, sizeof(HttpXFFCfg)); |
417 | 2 | if (ctx->xff_cfg != NULL) { |
418 | 2 | HttpXFFGetCfg(conf, ctx->xff_cfg); |
419 | 2 | } |
420 | | |
421 | 2 | OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); |
422 | 2 | if (unlikely(output_ctx == NULL)) { |
423 | 0 | SCFree(ctx->xff_cfg); |
424 | 0 | SCFree(ctx); |
425 | 0 | return result; |
426 | 0 | } |
427 | | |
428 | 2 | output_ctx->data = ctx; |
429 | 2 | output_ctx->DeInit = OutputFilestoreLogDeInitCtx; |
430 | | |
431 | 2 | const char *write_fileinfo = SCConfNodeLookupChildValue(conf, "write-fileinfo"); |
432 | 2 | if (write_fileinfo != NULL && SCConfValIsTrue(write_fileinfo)) { |
433 | 0 | SCLogConfig("Filestore (v2) will output fileinfo records."); |
434 | 0 | ctx->fileinfo = true; |
435 | 0 | } |
436 | | |
437 | 2 | const char *force_filestore = SCConfNodeLookupChildValue(conf, "force-filestore"); |
438 | 2 | if (force_filestore != NULL && SCConfValIsTrue(force_filestore)) { |
439 | 2 | FileForceFilestoreEnable(); |
440 | 2 | SCLogInfo("forcing filestore of all files"); |
441 | 2 | } |
442 | | |
443 | 2 | const char *force_magic = SCConfNodeLookupChildValue(conf, "force-magic"); |
444 | 2 | if (force_magic != NULL && SCConfValIsTrue(force_magic)) { |
445 | 0 | FileForceMagicEnable(); |
446 | 0 | SCLogConfig("Filestore (v2) forcing magic lookup for stored files"); |
447 | 0 | } |
448 | | |
449 | 2 | FileForceHashParseCfg(conf); |
450 | | |
451 | | /* The new filestore requires SHA256. */ |
452 | 2 | FileForceSha256Enable(); |
453 | | |
454 | 2 | ProvidesFeature(FEATURE_OUTPUT_FILESTORE); |
455 | | |
456 | 2 | const char *stream_depth_str = SCConfNodeLookupChildValue(conf, "stream-depth"); |
457 | 2 | if (stream_depth_str != NULL && strcmp(stream_depth_str, "no")) { |
458 | 0 | uint32_t stream_depth = 0; |
459 | 0 | if (ParseSizeStringU32(stream_depth_str, |
460 | 0 | &stream_depth) < 0) { |
461 | 0 | SCLogError("Error parsing " |
462 | 0 | "file-store.stream-depth " |
463 | 0 | "from conf file - %s. Killing engine", |
464 | 0 | stream_depth_str); |
465 | 0 | exit(EXIT_FAILURE); |
466 | 0 | } |
467 | 0 | if (stream_depth) { |
468 | 0 | if (stream_depth <= stream_config.reassembly_depth) { |
469 | 0 | SCLogWarning("file-store.stream-depth value %" PRIu32 " has " |
470 | 0 | "no effect since it's less than stream.reassembly.depth " |
471 | 0 | "value.", |
472 | 0 | stream_depth); |
473 | 0 | } else { |
474 | 0 | FileReassemblyDepthEnable(stream_depth); |
475 | 0 | } |
476 | 0 | } |
477 | 0 | } |
478 | | |
479 | 2 | const char *file_count_str = SCConfNodeLookupChildValue(conf, "max-open-files"); |
480 | 2 | if (file_count_str != NULL) { |
481 | 0 | uint32_t file_count = 0; |
482 | 0 | if (ParseSizeStringU32(file_count_str, |
483 | 0 | &file_count) < 0) { |
484 | 0 | SCLogError("Error parsing " |
485 | 0 | "file-store.max-open-files " |
486 | 0 | "from conf file - %s. Killing engine", |
487 | 0 | file_count_str); |
488 | 0 | exit(EXIT_FAILURE); |
489 | 0 | } else { |
490 | 0 | if (file_count != 0) { |
491 | 0 | FileSetMaxOpenFiles(file_count); |
492 | 0 | SCLogConfig("Filestore (v2) will keep a max of %d " |
493 | 0 | "simultaneously open files", file_count); |
494 | 0 | } |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | 2 | result.ctx = output_ctx; |
499 | 2 | result.ok = true; |
500 | 2 | SCReturnCT(result, "OutputInitResult"); |
501 | 2 | } |
502 | | |
503 | | void OutputFilestoreRegister(void) |
504 | 78 | { |
505 | 78 | OutputRegisterFiledataModule(LOGGER_FILE_STORE, MODULE_NAME, "file-store", |
506 | 78 | OutputFilestoreLogInitCtx, OutputFilestoreLogger, OutputFilestoreLogThreadInit, |
507 | 78 | OutputFilestoreLogThreadDeinit); |
508 | | |
509 | 78 | SC_ATOMIC_INIT(filestore_open_file_cnt); |
510 | 78 | SC_ATOMIC_SET(filestore_open_file_cnt, 0); |
511 | 78 | } |
512 | | |
513 | | void OutputFilestoreRegisterGlobalCounters(void) |
514 | 78 | { |
515 | 78 | StatsRegisterGlobalCounter("file_store.open_files", OutputFilestoreOpenFilesCounter); |
516 | 78 | } |