suricata
util-logopenfile.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2026 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 Mike Pomraning <mpomraning@qualys.com>
22  *
23  * File-like output for logging: regular files and sockets.
24  */
25 
26 #include "suricata-common.h" /* errno.h, string.h, etc. */
27 #include "util-logopenfile.h"
28 #include "suricata.h"
29 #include "conf.h" /* ConfNode, etc. */
30 #include "output.h" /* DEFAULT_LOG_* */
31 #include "util-byte.h"
32 #include "util-conf.h"
33 #include "util-path.h"
34 #include "util-misc.h"
35 #include "util-time.h"
36 #include "log-maintenance.h"
37 
38 #if defined(HAVE_SYS_UN_H) && defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SYS_TYPES_H)
39 #define BUILD_WITH_UNIXSOCKET
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/un.h>
43 #endif
44 
45 #ifdef HAVE_LIBHIREDIS
46 #include "util-log-redis.h"
47 #endif /* HAVE_LIBHIREDIS */
48 
49 #define LOGFILE_NAME_MAX 255
50 
51 static bool LogFileNewThreadedCtx(LogFileCtx *parent_ctx, const char *log_path, const char *append,
52  ThreadLogFileHashEntry *entry);
53 
54 // Threaded eve.json identifier
55 static SC_ATOMIC_DECL_AND_INIT_WITH_VAL(uint16_t, eve_file_id, 1);
56 
57 /* Log file list for heartbeat-triggered flushing and rotation */
58 static SCMutex log_file_list_mutex = SCMUTEX_INITIALIZER;
59 static TAILQ_HEAD(, LogFileEntry_) log_file_list = TAILQ_HEAD_INITIALIZER(log_file_list);
60 
61 #ifdef BUILD_WITH_UNIXSOCKET
62 /** \brief connect to the indicated local stream socket, logging any errors
63  * \param path filesystem path to connect to
64  * \param log_err, non-zero if connect failure should be logged.
65  * \retval FILE* on success (fdopen'd wrapper of underlying socket)
66  * \retval NULL on error
67  */
68 static FILE *
69 SCLogOpenUnixSocketFp(const char *path, int sock_type, int log_err)
70 {
71  struct sockaddr_un saun;
72  int s = -1;
73  FILE * ret = NULL;
74 
75  memset(&saun, 0x00, sizeof(saun));
76 
77  s = socket(PF_UNIX, sock_type, 0);
78  if (s < 0) goto err;
79 
80  saun.sun_family = AF_UNIX;
81  strlcpy(saun.sun_path, path, sizeof(saun.sun_path));
82 
83  if (connect(s, (const struct sockaddr *)&saun, sizeof(saun)) < 0)
84  goto err;
85 
86  ret = fdopen(s, "w");
87  if (ret == NULL)
88  goto err;
89 
90  return ret;
91 
92 err:
93  if (log_err)
95  "Error connecting to socket \"%s\": %s (will keep trying)", path, strerror(errno));
96 
97  if (s >= 0)
98  close(s);
99 
100  return NULL;
101 }
102 
103 /**
104  * \brief Attempt to reconnect a disconnected (or never-connected) Unix domain socket.
105  * \retval 1 if it is now connected; otherwise 0
106  */
107 static int SCLogUnixSocketReconnect(LogFileCtx *log_ctx)
108 {
109  int disconnected = 0;
110  if (log_ctx->fp) {
111  SCLogWarning("Write error on Unix socket \"%s\": %s; reconnecting...", log_ctx->filename,
112  strerror(errno));
113  fclose(log_ctx->fp);
114  log_ctx->fp = NULL;
115  log_ctx->reconn_timer = 0;
116  disconnected = 1;
117  }
118 
119  struct timeval tv;
120  uint64_t now;
121  gettimeofday(&tv, NULL);
122  now = (uint64_t)tv.tv_sec * 1000;
123  now += tv.tv_usec / 1000; /* msec resolution */
124  if (log_ctx->reconn_timer != 0 &&
125  (now - log_ctx->reconn_timer) < LOGFILE_RECONN_MIN_TIME) {
126  /* Don't bother to try reconnecting too often. */
127  return 0;
128  }
129  log_ctx->reconn_timer = now;
130 
131  log_ctx->fp = SCLogOpenUnixSocketFp(log_ctx->filename, log_ctx->sock_type, 0);
132  if (log_ctx->fp) {
133  /* Connected at last (or reconnected) */
134  SCLogDebug("Reconnected socket \"%s\"", log_ctx->filename);
135  } else if (disconnected) {
136  SCLogWarning("Reconnect failed: %s (will keep trying)", strerror(errno));
137  }
138 
139  return log_ctx->fp ? 1 : 0;
140 }
141 
142 static int SCLogFileWriteSocket(const char *buffer, int buffer_len,
143  LogFileCtx *ctx)
144 {
145  int tries = 0;
146  int ret = 0;
147  bool reopen = false;
148  if (ctx->fp == NULL && ctx->is_sock) {
149  SCLogUnixSocketReconnect(ctx);
150  }
151 tryagain:
152  ret = -1;
153  reopen = 0;
154  errno = 0;
155  if (ctx->fp != NULL) {
156  int fd = fileno(ctx->fp);
157  ssize_t size = send(fd, buffer, buffer_len, ctx->send_flags);
158  if (size > -1) {
159  ret = 0;
160  } else {
161  if (errno == EAGAIN || errno == EWOULDBLOCK) {
162  SCLogDebug("Socket would block, dropping event.");
163  } else if (errno == EINTR) {
164  if (tries++ == 0) {
165  SCLogDebug("Interrupted system call, trying again.");
166  goto tryagain;
167  }
168  SCLogDebug("Too many interrupted system calls, "
169  "dropping event.");
170  } else {
171  /* Some other error. Assume badness and reopen. */
172  SCLogDebug("Send failed: %s", strerror(errno));
173  reopen = true;
174  }
175  }
176  }
177 
178  if (reopen && tries++ == 0) {
179  if (SCLogUnixSocketReconnect(ctx)) {
180  goto tryagain;
181  }
182  }
183 
184  if (ret == -1) {
185  ctx->dropped++;
186  }
187 
188  return ret;
189 }
190 #endif /* BUILD_WITH_UNIXSOCKET */
191 static inline void OutputWriteLock(pthread_mutex_t *m)
192 {
193  SCMutexLock(m);
194 
195 }
196 
197 /**
198  * \brief Flush a log file.
199  */
200 static void SCLogFileFlushNoLock(LogFileCtx *log_ctx)
201 {
202  log_ctx->bytes_since_last_flush = 0;
203  SCFflushUnlocked(log_ctx->fp);
204 }
205 
206 static void SCLogFileFlush(LogFileCtx *log_ctx)
207 {
208  OutputWriteLock(&log_ctx->fp_mutex);
209  SCLogFileFlushNoLock(log_ctx);
210  SCMutexUnlock(&log_ctx->fp_mutex);
211 }
212 
213 /**
214  * \brief Handle log file rotation checks and updates
215  * \param log_ctx Log file context
216  * \retval true if rotation occurred
217  */
218 static bool HandleLogRotation(LogFileCtx *log_ctx)
219 {
220  if (log_ctx->rotation_flag) {
221  log_ctx->rotation_flag = 0;
222  SCConfLogReopen(log_ctx);
223  if (log_ctx->flags & LOGFILE_ROTATE_INTERVAL) {
224  log_ctx->rotate_time = time(NULL) + log_ctx->rotate_interval;
225  }
226  return true;
227  } else if (log_ctx->flags & LOGFILE_ROTATE_INTERVAL) {
228  time_t now = time(NULL);
229  if (now >= log_ctx->rotate_time) {
230  SCConfLogReopen(log_ctx);
231  log_ctx->rotate_time = now + log_ctx->rotate_interval;
232  return true;
233  }
234  }
235 
236  return false;
237 }
238 
239 /**
240  * \brief Write buffer to log file.
241  * \retval 0 on failure; otherwise, the return value of fwrite_unlocked (number of
242  * characters successfully written).
243  */
244 static int SCLogFileWriteNoLock(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
245 {
246  int ret = 0;
247 
248  DEBUG_VALIDATE_BUG_ON(log_ctx->is_sock);
249 
250  HandleLogRotation(log_ctx);
251 
252  if (log_ctx->fp) {
253  SCClearErrUnlocked(log_ctx->fp);
254  if (1 != SCFwriteUnlocked(buffer, buffer_len, 1, log_ctx->fp)) {
255  /* Only the first error is logged */
256  if (!log_ctx->output_errors) {
257  SCLogError("%s error while writing to %s",
258  SCFerrorUnlocked(log_ctx->fp) ? strerror(errno) : "unknown error",
259  log_ctx->filename);
260  }
261  log_ctx->output_errors++;
262  return ret;
263  }
264 
265  log_ctx->bytes_since_last_flush += buffer_len;
266 
267  if (log_ctx->buffer_size && log_ctx->bytes_since_last_flush >= log_ctx->buffer_size) {
268  SCLogDebug("%s: flushing %" PRIu64 " during write", log_ctx->filename,
269  log_ctx->bytes_since_last_flush);
270  SCLogFileFlushNoLock(log_ctx);
271  }
272  }
273 
274  return ret;
275 }
276 
277 /**
278  * \brief Check and perform log file rotation if needed.
279  *
280  * Called by the maintenance thread to drive rotation independently of
281  * traffic-driven writes. Takes the file lock so it is safe to run
282  * concurrently with worker threads writing to the same context.
283  */
284 static void SCLogFileRotate(LogFileCtx *log_ctx)
285 {
286  OutputWriteLock(&log_ctx->fp_mutex);
287  HandleLogRotation(log_ctx);
288  SCMutexUnlock(&log_ctx->fp_mutex);
289 }
290 
291 /**
292  * \brief Write buffer to log file.
293  * \retval 0 on failure; otherwise, the return value of fwrite (number of
294  * characters successfully written).
295  */
296 static int SCLogFileWrite(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
297 {
298  OutputWriteLock(&log_ctx->fp_mutex);
299  int ret = 0;
300 
301 #ifdef BUILD_WITH_UNIXSOCKET
302  if (log_ctx->is_sock) {
303  ret = SCLogFileWriteSocket(buffer, buffer_len, log_ctx);
304  } else
305 #endif
306  {
307  ret = SCLogFileWriteNoLock(buffer, buffer_len, log_ctx);
308  }
309 
310  SCMutexUnlock(&log_ctx->fp_mutex);
311 
312  return ret;
313 }
314 
315 /** \brief generate filename based on pattern
316  * \param pattern pattern to use
317  * \retval char* on success
318  * \retval NULL on error
319  */
320 static char *SCLogFilenameFromPattern(const char *pattern)
321 {
322  char *filename = SCMalloc(PATH_MAX);
323  if (filename == NULL) {
324  return NULL;
325  }
326 
327  int rc = SCTimeToStringPattern(time(NULL), pattern, filename, PATH_MAX);
328  if (rc != 0) {
329  SCFree(filename);
330  return NULL;
331  }
332 
333  return filename;
334 }
335 
336 static void SCLogFileCloseNoLock(LogFileCtx *log_ctx)
337 {
338  SCLogDebug("Closing %s", log_ctx->filename);
339  if (log_ctx->fp) {
340  if (log_ctx->buffer_size)
341  SCFflushUnlocked(log_ctx->fp);
342  fclose(log_ctx->fp);
343  }
344 
345  if (log_ctx->output_errors) {
346  SCLogError("There were %" PRIu64 " output errors to %s", log_ctx->output_errors,
347  log_ctx->filename);
348  }
349 }
350 
351 static void SCLogFileClose(LogFileCtx *log_ctx)
352 {
353  SCMutexLock(&log_ctx->fp_mutex);
354  SCLogFileCloseNoLock(log_ctx);
355  SCMutexUnlock(&log_ctx->fp_mutex);
356 }
357 
358 static char ThreadLogFileHashCompareFunc(
359  void *data1, uint16_t datalen1, void *data2, uint16_t datalen2)
360 {
363 
364  if (p1 == NULL || p2 == NULL)
365  return 0;
366 
367  return p1->thread_id == p2->thread_id;
368 }
369 static uint32_t ThreadLogFileHashFunc(HashTable *ht, void *data, uint16_t datalen)
370 {
371  const ThreadLogFileHashEntry *ent = (ThreadLogFileHashEntry *)data;
372 
373  return ent->thread_id % ht->array_size;
374 }
375 
376 static void ThreadLogFileHashFreeFunc(void *data)
377 {
378  BUG_ON(data == NULL);
379  ThreadLogFileHashEntry *thread_ent = (ThreadLogFileHashEntry *)data;
380 
381  if (!thread_ent)
382  return;
383 
384  if (thread_ent->isopen) {
385  LogFileCtx *lf_ctx = thread_ent->ctx;
386  /* Free the leaf log file entries */
387  if (!lf_ctx->threaded) {
388  LogFileFreeCtx(lf_ctx);
389  }
390  }
391  SCFree(thread_ent);
392 }
393 
394 bool SCLogOpenThreadedFile(const char *log_path, const char *append, LogFileCtx *parent_ctx)
395 {
396  parent_ctx->threads = SCCalloc(1, sizeof(LogThreadedFileCtx));
397  if (!parent_ctx->threads) {
398  SCLogError("Unable to allocate threads container");
399  return false;
400  }
401 
402  parent_ctx->threads->ht = HashTableInit(255, ThreadLogFileHashFunc,
403  ThreadLogFileHashCompareFunc, ThreadLogFileHashFreeFunc);
404  if (!parent_ctx->threads->ht) {
405  FatalError("Unable to initialize thread/entry hash table");
406  }
407 
408  parent_ctx->threads->append = SCStrdup(append == NULL ? DEFAULT_LOG_MODE_APPEND : append);
409  if (!parent_ctx->threads->append) {
410  SCLogError("Unable to allocate threads append setting");
411  goto error_exit;
412  }
413 
414  SCMutexInit(&parent_ctx->threads->mutex, NULL);
415  return true;
416 
417 error_exit:
418 
419  if (parent_ctx->threads->append) {
420  SCFree(parent_ctx->threads->append);
421  }
422  if (parent_ctx->threads->ht) {
423  HashTableFree(parent_ctx->threads->ht);
424  }
425  SCFree(parent_ctx->threads);
426  parent_ctx->threads = NULL;
427  return false;
428 }
429 
430 /** \brief open the indicated file, logging any errors
431  * \param path filesystem path to open
432  * \param append_setting open file with O_APPEND: "yes" or "no"
433  * \param mode permissions to set on file
434  * \retval FILE* on success
435  * \retval NULL on error
436  */
437 static FILE *SCLogOpenFileFp(
438  const char *path, const char *append_setting, uint32_t mode, const uint32_t buffer_size)
439 {
440  FILE *ret = NULL;
441 
442  char *filename = SCLogFilenameFromPattern(path);
443  if (filename == NULL) {
444  return NULL;
445  }
446 
447  int rc = SCCreateDirectoryTree(filename, false);
448  if (rc < 0) {
449  SCFree(filename);
450  return NULL;
451  }
452 
453  if (SCConfValIsTrue(append_setting)) {
454  ret = fopen(filename, "a");
455  } else {
456  ret = fopen(filename, "w");
457  }
458 
459  if (ret == NULL) {
460  SCLogError("Error opening file: \"%s\": %s", filename, strerror(errno));
461  goto error_exit;
462  } else {
463  if (mode != 0) {
464 #ifdef OS_WIN32
465  int r = _chmod(filename, (mode_t)mode);
466 #else
467  int r = fchmod(fileno(ret), (mode_t)mode);
468 #endif
469  if (r < 0) {
470  SCLogWarning("Could not chmod %s to %o: %s", filename, mode, strerror(errno));
471  }
472  }
473  }
474 
475  /* Set buffering behavior */
476  if (buffer_size == 0) {
477  setbuf(ret, NULL);
478  SCLogConfig("Setting output to %s non-buffered", filename);
479  } else {
480  if (setvbuf(ret, NULL, _IOFBF, buffer_size) < 0)
481  FatalError("unable to set %s to buffered: %d", filename, buffer_size);
482  SCLogConfig("Setting output to %s buffered [limit %d bytes]", filename, buffer_size);
483  }
484 
485 error_exit:
486  SCFree(filename);
487 
488  return ret;
489 }
490 
491 /** \brief open a generic output "log file", which may be a regular file or a socket
492  * \param conf ConfNode structure for the output section in question
493  * \param log_ctx Log file context allocated by caller
494  * \param default_filename Default name of file to open, if not specified in ConfNode
495  * \param rotate Register the file for rotation in HUP.
496  * \retval 0 on success
497  * \retval -1 on error
498  */
500  SCConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
501 {
502  char log_path[PATH_MAX];
503  const char *log_dir;
504  const char *filename, *filetype;
505 
506  // Arg check
507  if (conf == NULL || log_ctx == NULL || default_filename == NULL) {
508  SCLogError("SCConfLogOpenGeneric(conf %p, ctx %p, default %p) "
509  "missing an argument",
510  conf, log_ctx, default_filename);
511  return -1;
512  }
513  if (log_ctx->fp != NULL) {
514  SCLogError("SCConfLogOpenGeneric: previously initialized Log CTX "
515  "encountered");
516  return -1;
517  }
518 
519  // Resolve the given config
520  filename = SCConfNodeLookupChildValue(conf, "filename");
521  if (filename == NULL)
522  filename = default_filename;
523 
524  log_dir = SCConfigGetLogDirectory();
525 
526  if (PathIsAbsolute(filename)) {
527  snprintf(log_path, PATH_MAX, "%s", filename);
528  } else {
529  snprintf(log_path, PATH_MAX, "%s/%s", log_dir, filename);
530  }
531 
532  /* Compress IPv6 addresses */
533  const char *compress = SCConfNodeLookupChildValue(conf, "ipv6-compress");
534  log_ctx->compress_ipv6 = false;
535  if (compress != NULL && SCConfValIsTrue(compress))
536  log_ctx->compress_ipv6 = true;
537 
538  /* Rotate log file based on time */
539  const char *rotate_int = SCConfNodeLookupChildValue(conf, "rotate-interval");
540  if (rotate_int != NULL) {
541  time_t now = time(NULL);
542  log_ctx->flags |= LOGFILE_ROTATE_INTERVAL;
543 
544  /* Use a specific time */
545  if (strcmp(rotate_int, "minute") == 0) {
546  log_ctx->rotate_time = now + SCGetSecondsUntil(rotate_int, now);
547  log_ctx->rotate_interval = 60;
548  } else if (strcmp(rotate_int, "hour") == 0) {
549  log_ctx->rotate_time = now + SCGetSecondsUntil(rotate_int, now);
550  log_ctx->rotate_interval = 3600;
551  } else if (strcmp(rotate_int, "day") == 0) {
552  log_ctx->rotate_time = now + SCGetSecondsUntil(rotate_int, now);
553  log_ctx->rotate_interval = 86400;
554  }
555 
556  /* Use a timer */
557  else {
558  log_ctx->rotate_interval = SCParseTimeSizeString(rotate_int);
559  if (log_ctx->rotate_interval == 0) {
560  FatalError("invalid rotate-interval value");
561  }
562  log_ctx->rotate_time = now + log_ctx->rotate_interval;
563  }
564  }
565 
566  filetype = SCConfNodeLookupChildValue(conf, "filetype");
567  if (filetype == NULL)
568  filetype = DEFAULT_LOG_FILETYPE;
569 
570  /* Determine the buffering for this output device; a value of 0 means to not buffer;
571  * any other value must be a multiple of 4096
572  * The default value is 0 (no buffering)
573  */
574  uint32_t buffer_size = LOGFILE_EVE_BUFFER_SIZE;
575  const char *buffer_size_value = SCConfNodeLookupChildValue(conf, "buffer-size");
576  if (buffer_size_value != NULL) {
577  uint32_t value;
578  if (ParseSizeStringU32(buffer_size_value, &value) < 0) {
579  FatalError("Error parsing "
580  "buffer-size - %s. Killing engine",
581  buffer_size_value);
582  }
583  buffer_size = value;
584  }
585 
586  SCLogDebug("buffering: %s -> %d", buffer_size_value, buffer_size);
587  const char *filemode = SCConfNodeLookupChildValue(conf, "filemode");
588  uint32_t mode = 0;
589  if (filemode != NULL && StringParseUint32(&mode, 8, (uint16_t)strlen(filemode), filemode) > 0) {
590  log_ctx->filemode = mode;
591  }
592 
593  const char *append = SCConfNodeLookupChildValue(conf, "append");
594  if (append == NULL)
595  append = DEFAULT_LOG_MODE_APPEND;
596 
597  /* JSON flags */
598  log_ctx->json_flags = JSON_PRESERVE_ORDER|JSON_COMPACT|
599  JSON_ENSURE_ASCII|JSON_ESCAPE_SLASH;
600 
601  SCConfNode *json_flags = SCConfNodeLookupChild(conf, "json");
602 
603  if (json_flags != 0) {
604  const char *preserve_order = SCConfNodeLookupChildValue(json_flags, "preserve-order");
605  if (preserve_order != NULL && SCConfValIsFalse(preserve_order))
606  log_ctx->json_flags &= ~(JSON_PRESERVE_ORDER);
607 
608  const char *compact = SCConfNodeLookupChildValue(json_flags, "compact");
609  if (compact != NULL && SCConfValIsFalse(compact))
610  log_ctx->json_flags &= ~(JSON_COMPACT);
611 
612  const char *ensure_ascii = SCConfNodeLookupChildValue(json_flags, "ensure-ascii");
613  if (ensure_ascii != NULL && SCConfValIsFalse(ensure_ascii))
614  log_ctx->json_flags &= ~(JSON_ENSURE_ASCII);
615 
616  const char *escape_slash = SCConfNodeLookupChildValue(json_flags, "escape-slash");
617  if (escape_slash != NULL && SCConfValIsFalse(escape_slash))
618  log_ctx->json_flags &= ~(JSON_ESCAPE_SLASH);
619  }
620 
621 #ifdef BUILD_WITH_UNIXSOCKET
622  if (log_ctx->threaded) {
623  if (strcasecmp(filetype, "unix_stream") == 0 || strcasecmp(filetype, "unix_dgram") == 0) {
624  FatalError("Socket file types do not support threaded output");
625  }
626  }
627 #endif
628  if (!(strcasecmp(filetype, DEFAULT_LOG_FILETYPE) == 0 || strcasecmp(filetype, "file") == 0)) {
629  SCLogConfig("buffering setting ignored for %s output types", filetype);
630  }
631 
632  // Now, what have we been asked to open?
633  if (strcasecmp(filetype, "unix_stream") == 0) {
634 #ifdef BUILD_WITH_UNIXSOCKET
635  /* Don't bail. May be able to connect later. */
636  log_ctx->is_sock = 1;
637  log_ctx->sock_type = SOCK_STREAM;
638  log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1);
639 #else
640  return -1;
641 #endif
642  } else if (strcasecmp(filetype, "unix_dgram") == 0) {
643 #ifdef BUILD_WITH_UNIXSOCKET
644  /* Don't bail. May be able to connect later. */
645  log_ctx->is_sock = 1;
646  log_ctx->sock_type = SOCK_DGRAM;
647  log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1);
648 #else
649  return -1;
650 #endif
651  } else if (strcasecmp(filetype, DEFAULT_LOG_FILETYPE) == 0 ||
652  strcasecmp(filetype, "file") == 0) {
653  log_ctx->is_regular = 1;
654  log_ctx->buffer_size = buffer_size;
655  if (!log_ctx->threaded) {
656  log_ctx->fp =
657  SCLogOpenFileFp(log_path, append, log_ctx->filemode, log_ctx->buffer_size);
658  if (log_ctx->fp == NULL)
659  return -1; // Error already logged by Open...Fp routine
660  } else {
661  if (!SCLogOpenThreadedFile(log_path, append, log_ctx)) {
662  return -1;
663  }
664  }
665  if (rotate) {
667  }
668  /* Register non-threaded regular files for heartbeat maintenance */
669  if (!log_ctx->threaded && log_ctx->is_regular) {
670  LogFileRegister(log_ctx);
671  }
672  } else {
673  SCLogError("Invalid entry for "
674  "%s.filetype. Expected \"regular\" (default), \"unix_stream\", "
675  "or \"unix_dgram\"",
676  conf->name);
677  }
678  log_ctx->filename = SCStrdup(log_path);
679  if (unlikely(log_ctx->filename == NULL)) {
680  SCLogError("Failed to allocate memory for filename");
681  return -1;
682  }
683 
684 #ifdef BUILD_WITH_UNIXSOCKET
685  /* If a socket and running live, do non-blocking writes. */
686  if (log_ctx->is_sock && !IsRunModeOffline(SCRunmodeGet())) {
687  SCLogInfo("Setting logging socket of non-blocking in live mode.");
688  log_ctx->send_flags |= MSG_DONTWAIT;
689  }
690 #endif
691  SCLogInfo("%s output device (%s) initialized: %s", conf->name, filetype,
692  filename);
693 
694  return 0;
695 }
696 
697 /**
698  * \brief Reopen a regular log file with the side-affect of truncating it.
699  *
700  * This is useful to clear the log file and start a new one, or to
701  * re-open the file after its been moved by something external
702  * (eg. logrotate).
703  */
705 {
706  if (!log_ctx->is_regular) {
707  /* Not supported and not needed on non-regular files. */
708  return 0;
709  }
710 
711  if (log_ctx->filename == NULL) {
712  SCLogWarning("Can't re-open LogFileCtx without a filename.");
713  return -1;
714  }
715 
716  if (log_ctx->fp != NULL) {
717  fclose(log_ctx->fp);
718  }
719 
720  /* Reopen the file. Append is forced in case the file was not
721  * moved as part of a rotation process. */
722  SCLogDebug("Reopening log file %s.", log_ctx->filename);
723  log_ctx->fp =
724  SCLogOpenFileFp(log_ctx->filename, "yes", log_ctx->filemode, log_ctx->buffer_size);
725  if (log_ctx->fp == NULL) {
726  return -1; // Already logged by Open..Fp routine.
727  }
728 
729  return 0;
730 }
731 
732 /** \brief LogFileNewCtx() Get a new LogFileCtx
733  * \retval LogFileCtx * pointer if successful, NULL if error
734  * */
736 {
737  LogFileCtx* lf_ctx;
738  lf_ctx = (LogFileCtx*)SCCalloc(1, sizeof(LogFileCtx));
739 
740  if (lf_ctx == NULL)
741  return NULL;
742 
743  lf_ctx->Write = SCLogFileWrite;
744  lf_ctx->Close = SCLogFileClose;
745  lf_ctx->Flush = SCLogFileFlush;
746  lf_ctx->Rotate = SCLogFileRotate;
747 
748  return lf_ctx;
749 }
750 
751 /** \brief LogFileThread2Slot() Return a file entry
752  * \retval ThreadLogFileHashEntry * file entry for caller
753  *
754  * This function returns the file entry for the calling thread.
755  * Each thread -- identified by its operating system thread-id -- has its
756  * own file entry that includes a file pointer.
757  */
758 static ThreadLogFileHashEntry *LogFileThread2Slot(LogThreadedFileCtx *parent, ThreadId thread_id)
759 {
760  ThreadLogFileHashEntry thread_hash_entry;
761 
762  /* Check hash table for thread id*/
763  thread_hash_entry.thread_id = SCGetThreadIdLong();
765  HashTableLookup(parent->ht, &thread_hash_entry, sizeof(thread_hash_entry));
766 
767  if (!ent) {
768  ent = SCCalloc(1, sizeof(*ent));
769  if (!ent) {
770  FatalError("Unable to allocate thread/hash-entry entry");
771  }
772  ent->thread_id = thread_hash_entry.thread_id;
773  ent->internal_thread_id = thread_id;
774  SCLogDebug(
775  "Trying to add thread %" PRIi64 " to entry %d", ent->thread_id, ent->slot_number);
776  if (0 != HashTableAdd(parent->ht, ent, 0)) {
777  FatalError("Unable to add thread/hash-entry mapping");
778  }
779  }
780  return ent;
781 }
782 
783 /** \brief LogFileEnsureExists() Ensure a log file context for the thread exists
784  * \param parent_ctx
785  * \retval LogFileCtx * pointer if successful; NULL otherwise
786  */
788 {
789  /* threaded output disabled */
790  if (!parent_ctx->threaded)
791  return parent_ctx;
792 
793  LogFileCtx *ret_ctx = NULL;
794  SCMutexLock(&parent_ctx->threads->mutex);
795  /* Find this thread's entry */
796  ThreadLogFileHashEntry *entry = LogFileThread2Slot(parent_ctx->threads, thread_id);
797  SCLogDebug("%s: Adding reference for thread %" PRIi64
798  " (local thread id %d) to file %s [ctx %p]",
799  t_thread_name, SCGetThreadIdLong(), thread_id, parent_ctx->filename, parent_ctx);
800 
801  bool new = entry->isopen;
802  /* has it been opened yet? */
803  if (!new) {
804  SCLogDebug("%s: Opening new file for thread/id %d to file %s [ctx %p]", t_thread_name,
805  thread_id, parent_ctx->filename, parent_ctx);
806  if (LogFileNewThreadedCtx(
807  parent_ctx, parent_ctx->filename, parent_ctx->threads->append, entry)) {
808  entry->isopen = true;
809  ret_ctx = entry->ctx;
810  } else {
811  SCLogDebug(
812  "Unable to open slot %d for file %s", entry->slot_number, parent_ctx->filename);
813  (void)HashTableRemove(parent_ctx->threads->ht, entry, 0);
814  }
815  } else {
816  ret_ctx = entry->ctx;
817  }
818  SCMutexUnlock(&parent_ctx->threads->mutex);
819 
821  if (new) {
822  SCLogDebug("Existing file for thread/entry %p reference to file %s [ctx %p]", entry,
823  parent_ctx->filename, parent_ctx);
824  }
825  }
826 
827  return ret_ctx;
828 }
829 
830 /** \brief LogFileThreadedName() Create file name for threaded EVE storage
831  *
832  */
833 static bool LogFileThreadedName(
834  const char *original_name, char *threaded_name, size_t len, uint32_t unique_id)
835 {
836  sc_errno = SC_OK;
837 
838  if (strcmp("/dev/null", original_name) == 0) {
839  strlcpy(threaded_name, original_name, len);
840  return true;
841  }
842 
843  const char *base = SCBasename(original_name);
844  if (!base) {
845  FatalError("Invalid filename for threaded mode \"%s\"; "
846  "no basename found.",
847  original_name);
848  }
849 
850  /* Check if basename has an extension */
851  const char *dot = strrchr(base, '.');
852  if (dot) {
853  char *tname = SCStrdup(original_name);
854  if (!tname) {
856  return false;
857  }
858 
859  /* Fetch extension location from original, not base
860  * for update
861  */
862  dot = strrchr(original_name, '.');
863  ptrdiff_t dotpos = dot - original_name;
864  tname[dotpos] = '\0';
865  char *ext = tname + dotpos + 1;
866  if (strlen(tname) && strlen(ext)) {
867  snprintf(threaded_name, len, "%s.%u.%s", tname, unique_id, ext);
868  } else {
869  FatalError("Invalid filename for threaded mode \"%s\"; "
870  "filenames must include an extension, e.g: \"name.ext\"",
871  original_name);
872  }
873  SCFree(tname);
874  } else {
875  snprintf(threaded_name, len, "%s.%u", original_name, unique_id);
876  }
877  return true;
878 }
879 
880 /** \brief LogFileNewThreadedCtx() Create file context for threaded output
881  * \param parent_ctx
882  * \param log_path
883  * \param append
884  * \param entry
885  */
886 static bool LogFileNewThreadedCtx(LogFileCtx *parent_ctx, const char *log_path, const char *append,
887  ThreadLogFileHashEntry *entry)
888 {
889  LogFileCtx *thread = SCCalloc(1, sizeof(LogFileCtx));
890  if (!thread) {
891  SCLogError("Unable to allocate thread file context entry %p", entry);
892  return false;
893  }
894 
895  *thread = *parent_ctx;
896  if (parent_ctx->type == LOGFILE_TYPE_FILE) {
897  char fname[LOGFILE_NAME_MAX];
898  entry->slot_number = SC_ATOMIC_ADD(eve_file_id, 1);
899  if (!LogFileThreadedName(log_path, fname, sizeof(fname), entry->slot_number)) {
900  SCLogError("Unable to create threaded filename for log");
901  goto error;
902  }
903  SCLogDebug("%s: thread open -- using name %s [replaces %s] - thread %d [slot %d]",
904  t_thread_name, fname, log_path, entry->internal_thread_id, entry->slot_number);
905  thread->fp = SCLogOpenFileFp(fname, append, thread->filemode, parent_ctx->buffer_size);
906  if (thread->fp == NULL) {
907  goto error;
908  }
909  thread->filename = SCStrdup(fname);
910  if (!thread->filename) {
911  SCLogError("Unable to duplicate filename for context entry %p", entry);
912  goto error;
913  }
914  thread->is_regular = true;
915  thread->Write = SCLogFileWrite;
916  thread->Close = SCLogFileClose;
917  thread->Rotate = SCLogFileRotate;
919  LogFileRegister(thread);
920  } else if (parent_ctx->type == LOGFILE_TYPE_FILETYPE) {
921  entry->slot_number = SC_ATOMIC_ADD(eve_file_id, 1);
922  SCLogDebug("%s - thread %d [slot %d]", log_path, entry->internal_thread_id,
923  entry->slot_number);
925  &thread->filetype.thread_data);
926  }
927  thread->threaded = false;
928  thread->parent = parent_ctx;
929  thread->entry = entry;
930  entry->ctx = thread;
931 
932  return true;
933 
934 error:
935  if (parent_ctx->type == LOGFILE_TYPE_FILE) {
936  SC_ATOMIC_SUB(eve_file_id, 1);
937  if (thread->fp) {
938  thread->Close(thread);
939  }
940  }
941 
942  if (thread) {
943  SCFree(thread);
944  }
945  return false;
946 }
947 
948 /** \brief LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
949  * \param lf_ctx pointer to the OutputCtx
950  * \retval int 1 if successful, 0 if error
951  * */
953 {
954  if (lf_ctx == NULL) {
955  SCReturnInt(0);
956  }
957 
958  /* Unregister from flush list first, before closing files.
959  * This ensures the heartbeat thread won't try to flush a context
960  * that's being destroyed. */
961  LogFileUnregister(lf_ctx);
962 
963  if (lf_ctx->type == LOGFILE_TYPE_FILETYPE && lf_ctx->filetype.filetype->ThreadDeinit) {
964  lf_ctx->filetype.filetype->ThreadDeinit(
965  lf_ctx->filetype.init_data, lf_ctx->filetype.thread_data);
966  }
967 
968  if (lf_ctx->threaded) {
969  BUG_ON(lf_ctx->threads == NULL);
970  SCMutexDestroy(&lf_ctx->threads->mutex);
971  if (lf_ctx->threads->append)
972  SCFree(lf_ctx->threads->append);
973  if (lf_ctx->threads->ht) {
974  HashTableFree(lf_ctx->threads->ht);
975  }
976  SCFree(lf_ctx->threads);
977  } else {
978  if (lf_ctx->type != LOGFILE_TYPE_FILETYPE) {
979  if (lf_ctx->fp != NULL) {
980  lf_ctx->Close(lf_ctx);
981  }
982  }
983  SCMutexDestroy(&lf_ctx->fp_mutex);
984  }
985 
986  if (lf_ctx->prefix != NULL) {
987  SCFree(lf_ctx->prefix);
988  lf_ctx->prefix_len = 0;
989  }
990 
991  if(lf_ctx->filename != NULL)
992  SCFree(lf_ctx->filename);
993 
994  if (lf_ctx->sensor_name)
995  SCFree(lf_ctx->sensor_name);
996 
997  if (!lf_ctx->threaded) {
999  }
1000 
1001  /* Deinitialize output filetypes. We only want to call this for
1002  * the parent of threaded output, or always for non-threaded
1003  * output. */
1004  if (lf_ctx->type == LOGFILE_TYPE_FILETYPE && lf_ctx->parent == NULL) {
1005  lf_ctx->filetype.filetype->Deinit(lf_ctx->filetype.init_data);
1006  }
1007 
1008 #ifdef HAVE_LIBHIREDIS
1009  if (lf_ctx->type == LOGFILE_TYPE_REDIS) {
1010  if (lf_ctx->redis_setup.stream_format != NULL) {
1011  SCFree(lf_ctx->redis_setup.stream_format);
1012  }
1013  }
1014 #endif
1015 
1016  memset(lf_ctx, 0, sizeof(*lf_ctx));
1017  SCFree(lf_ctx);
1018 
1019  SCReturnInt(1);
1020 }
1021 
1022 void LogFileFlush(LogFileCtx *file_ctx)
1023 {
1024  SCLogDebug("%s: bytes-to-flush %ld", file_ctx->filename, file_ctx->bytes_since_last_flush);
1025  file_ctx->Flush(file_ctx);
1026 }
1027 
1028 /**
1029  * \brief Register a LogFileCtx for maintenance operations
1030  *
1031  * Adds a LogFileCtx to the global log file list so the heartbeat thread
1032  * can perform flush and rotation on it.
1033  *
1034  * \param ctx The LogFileCtx to register (must be LOGFILE_TYPE_FILE)
1035  */
1037 {
1038  if (ctx == NULL || ctx->type != LOGFILE_TYPE_FILE) {
1039  return;
1040  }
1041 
1042  LogFileEntry *entry = SCMalloc(sizeof(LogFileEntry));
1043  if (entry == NULL) {
1044  SCLogError("Unable to allocate memory for log file entry");
1045  return;
1046  }
1047 
1048  entry->ctx = ctx;
1049 
1050  SCMutexLock(&log_file_list_mutex);
1051  TAILQ_INSERT_TAIL(&log_file_list, entry, entries);
1052  SCMutexUnlock(&log_file_list_mutex);
1053 }
1054 
1055 /**
1056  * \brief Unregister a LogFileCtx from maintenance operations
1057  *
1058  * Removes a LogFileCtx from the global log file list.
1059  *
1060  * \param ctx The LogFileCtx to unregister
1061  */
1063 {
1064  if (ctx == NULL) {
1065  return;
1066  }
1067 
1068  SCMutexLock(&log_file_list_mutex);
1069  LogFileEntry *entry, *safe;
1070  TAILQ_FOREACH_SAFE (entry, &log_file_list, entries, safe) {
1071  if (entry->ctx == ctx) {
1072  TAILQ_REMOVE(&log_file_list, entry, entries);
1073  SCFree(entry);
1074  break;
1075  }
1076  }
1077  SCMutexUnlock(&log_file_list_mutex);
1078 }
1079 
1080 /**
1081  * \brief Flush all registered LogFileCtx instances
1082  *
1083  * Called by the maintenance thread to flush all active file-based loggers.
1084  */
1086 {
1087  SCMutexLock(&log_file_list_mutex);
1088  LogFileEntry *entry;
1089  TAILQ_FOREACH (entry, &log_file_list, entries) {
1090  if (entry->ctx != NULL) {
1091  LogFileFlush(entry->ctx);
1092  }
1093  }
1094  SCMutexUnlock(&log_file_list_mutex);
1095 }
1096 
1097 /**
1098  * \brief Check rotation for all registered LogFileCtx instances
1099  *
1100  * Called by the maintenance thread to trigger rotation checks on all
1101  * registered log contexts during zero-traffic periods.
1102  */
1104 {
1105  SCMutexLock(&log_file_list_mutex);
1106  LogFileEntry *entry;
1107  TAILQ_FOREACH (entry, &log_file_list, entries) {
1108  if (entry->ctx != NULL && entry->ctx->Rotate != NULL) {
1109  entry->ctx->Rotate(entry->ctx);
1110  }
1111  }
1112  SCMutexUnlock(&log_file_list_mutex);
1113 }
1114 
1115 int LogFileWrite(LogFileCtx *file_ctx, MemBuffer *buffer)
1116 {
1117  if (file_ctx->type == LOGFILE_TYPE_FILE || file_ctx->type == LOGFILE_TYPE_UNIX_DGRAM ||
1118  file_ctx->type == LOGFILE_TYPE_UNIX_STREAM) {
1119  /* append \n for files only */
1120  MemBufferWriteString(buffer, "\n");
1121  file_ctx->Write((const char *)MEMBUFFER_BUFFER(buffer),
1122  MEMBUFFER_OFFSET(buffer), file_ctx);
1123  } else if (file_ctx->type == LOGFILE_TYPE_FILETYPE) {
1124  file_ctx->filetype.filetype->Write((const char *)MEMBUFFER_BUFFER(buffer),
1125  MEMBUFFER_OFFSET(buffer), file_ctx->filetype.init_data,
1126  file_ctx->filetype.thread_data);
1127  }
1128 #ifdef HAVE_LIBHIREDIS
1129  else if (file_ctx->type == LOGFILE_TYPE_REDIS) {
1130  SCMutexLock(&file_ctx->fp_mutex);
1131  LogFileWriteRedis(file_ctx, (const char *)MEMBUFFER_BUFFER(buffer),
1132  MEMBUFFER_OFFSET(buffer));
1133  SCMutexUnlock(&file_ctx->fp_mutex);
1134  }
1135 #endif
1136 
1137  return 0;
1138 }
SCParseTimeSizeString
uint64_t SCParseTimeSizeString(const char *str)
Parse string containing time size (1m, 1h, etc).
Definition: util-time.c:577
LogFileCtx_::rotation_flag
int rotation_flag
Definition: util-logopenfile.h:159
util-byte.h
LogThreadedFileCtx_::append
char * append
Definition: util-logopenfile.h:62
SCFerrorUnlocked
#define SCFerrorUnlocked
Definition: suricata-common.h:558
len
uint8_t len
Definition: app-layer-dnp3.h:2
SCConfValIsTrue
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:578
LogFileEntry_::ctx
struct LogFileCtx_ * ctx
Definition: util-logopenfile.h:72
LOGFILE_TYPE_REDIS
@ LOGFILE_TYPE_REDIS
Definition: util-logopenfile.h:42
LogFileCtx_::sensor_name
char * sensor_name
Definition: util-logopenfile.h:121
SCEveFileType_::Deinit
SCEveFileTypeDeinitFunc Deinit
Final call to deinitialize this filetype.
Definition: output-eve-bindgen.h:202
LogFileCtx_::reconn_timer
uint64_t reconn_timer
Definition: util-logopenfile.h:126
IsRunModeOffline
bool IsRunModeOffline(enum SCRunModes run_mode_to_check)
Definition: runmodes.c:558
SC_LOG_DEBUG
@ SC_LOG_DEBUG
Definition: util-debug.h:44
LogFileCtx_::fp_mutex
SCMutex fp_mutex
Definition: util-logopenfile.h:101
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_DECL_AND_INIT_WITH_VAL
#define SC_ATOMIC_DECL_AND_INIT_WITH_VAL(type, name, val)
wrapper for declaring an atomic variable and initializing it to a specific value
Definition: util-atomic.h:303
SCEveFileType_::Write
SCEveFileTypeWriteFunc Write
Called for each EVE log record.
Definition: output-eve-bindgen.h:180
LogFileNewCtx
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
Definition: util-logopenfile.c:735
SCGetSecondsUntil
uint64_t SCGetSecondsUntil(const char *str, time_t epoch)
Get seconds until a time unit changes.
Definition: util-time.c:628
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
LogFileRotateAll
void LogFileRotateAll(void)
Check rotation for all registered LogFileCtx instances.
Definition: util-logopenfile.c:1103
LogFileFlush
void LogFileFlush(LogFileCtx *file_ctx)
Definition: util-logopenfile.c:1022
LogFileCtx_::json_flags
size_t json_flags
Definition: util-logopenfile.h:162
ctx
struct Thresholds ctx
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
LogFileCtx_
Definition: util-logopenfile.h:77
LOGFILE_RECONN_MIN_TIME
#define LOGFILE_RECONN_MIN_TIME
Definition: util-logopenfile.h:178
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
DEFAULT_LOG_MODE_APPEND
#define DEFAULT_LOG_MODE_APPEND
Definition: output.h:30
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
HashTable_
Definition: util-hash.h:35
util-log-redis.h
SCConfValIsFalse
int SCConfValIsFalse(const char *val)
Check if a value is false.
Definition: conf.c:603
LogFileCtx_::Write
int(* Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp)
Definition: util-logopenfile.h:92
SCMUTEX_INITIALIZER
#define SCMUTEX_INITIALIZER
Definition: threads-debug.h:122
LogFileCtx_::filename
char * filename
Definition: util-logopenfile.h:112
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
LOGFILE_NAME_MAX
#define LOGFILE_NAME_MAX
Definition: util-logopenfile.c:49
JSON_ESCAPE_SLASH
#define JSON_ESCAPE_SLASH
Definition: suricata-common.h:298
m
SCMutex m
Definition: flow-hash.h:6
SCConfNodeLookupChildValue
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:878
ThreadLogFileHashEntry::ctx
struct LogFileCtx_ * ctx
Definition: util-logopenfile.h:49
LogFileCtx_::send_flags
uint8_t send_flags
Definition: util-logopenfile.h:149
SCConfLogReopen
int SCConfLogReopen(LogFileCtx *log_ctx)
Reopen a regular log file with the side-affect of truncating it.
Definition: util-logopenfile.c:704
ThreadLogFileHashEntry::slot_number
uint16_t slot_number
Definition: util-logopenfile.h:54
ThreadLogFileHashEntry::isopen
bool isopen
Definition: util-logopenfile.h:55
SCClearErrUnlocked
#define SCClearErrUnlocked
Definition: suricata-common.h:557
SC_ENOMEM
@ SC_ENOMEM
Definition: util-error.h:29
HashTableFree
void HashTableFree(HashTable *ht)
Free a HashTable and all its contents.
Definition: util-hash.c:112
t_thread_name
thread_local char t_thread_name[THREAD_NAME_LEN+1]
Definition: threads.c:33
HashTable_::array_size
uint32_t array_size
Definition: util-hash.h:37
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
LogFileCtx_::sock_type
int sock_type
Definition: util-logopenfile.h:125
OutputRegisterFileRotationFlag
void OutputRegisterFileRotationFlag(int *flag)
Register a flag for file rotation notification.
Definition: output.c:688
LogFileCtx_::filetype
LogFileTypeCtx filetype
Definition: util-logopenfile.h:97
log-maintenance.h
SCTimeToStringPattern
int SCTimeToStringPattern(time_t epoch, const char *pattern, char *str, size_t size)
Convert epoch time to string pattern.
Definition: util-time.c:548
TAILQ_HEAD_INITIALIZER
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:236
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
SCRunmodeGet
SCRunMode SCRunmodeGet(void)
Get the current run mode.
Definition: suricata.c:301
LogFileWrite
int LogFileWrite(LogFileCtx *file_ctx, MemBuffer *buffer)
Definition: util-logopenfile.c:1115
SCEveFileType_::ThreadDeinit
SCEveFileTypeThreadDeinitFunc ThreadDeinit
Called to deinitialize each thread.
Definition: output-eve-bindgen.h:192
LogFileCtx_::rotate_interval
uint64_t rotate_interval
Definition: util-logopenfile.h:133
DEFAULT_LOG_FILETYPE
#define DEFAULT_LOG_FILETYPE
Definition: output.h:31
LogFileRegister
void LogFileRegister(LogFileCtx *ctx)
Register a LogFileCtx for maintenance operations.
Definition: util-logopenfile.c:1036
LogFileCtx_::is_sock
int is_sock
Definition: util-logopenfile.h:124
SCFwriteUnlocked
#define SCFwriteUnlocked
Definition: suricata-common.h:555
LogFileCtx_::prefix_len
uint32_t prefix_len
Definition: util-logopenfile.h:138
SCEveFileType_::ThreadInit
SCEveFileTypeThreadInitFunc ThreadInit
Initialize thread specific data.
Definition: output-eve-bindgen.h:161
SCFflushUnlocked
#define SCFflushUnlocked
Definition: suricata-common.h:556
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:120
HashTableLookup
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:194
HashTableRemove
int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
Remove an item from the hash table.
Definition: util-hash.c:178
ThreadLogFileHashEntry::internal_thread_id
ThreadId internal_thread_id
Definition: util-logopenfile.h:52
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:269
util-time.h
LOGFILE_EVE_BUFFER_SIZE
#define LOGFILE_EVE_BUFFER_SIZE
Definition: util-logopenfile.h:184
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:262
HashTableAdd
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:132
LogFileCtx_::type
enum LogFileType type
Definition: util-logopenfile.h:109
LOGFILE_TYPE_FILE
@ LOGFILE_TYPE_FILE
Definition: util-logopenfile.h:39
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:325
LogFileCtx_::threads
LogThreadedFileCtx * threads
Definition: util-logopenfile.h:84
LogFileFlushAll
void LogFileFlushAll(void)
Flush all registered LogFileCtx instances.
Definition: util-logopenfile.c:1085
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:341
ThreadLogFileHashEntry::thread_id
uint64_t thread_id
Definition: util-logopenfile.h:51
LogFileUnregister
void LogFileUnregister(LogFileCtx *ctx)
Unregister a LogFileCtx from maintenance operations.
Definition: util-logopenfile.c:1062
SCConfigGetLogDirectory
const char * SCConfigGetLogDirectory(void)
Definition: util-conf.c:38
conf.h
SC_OK
@ SC_OK
Definition: util-error.h:27
SCBasename
const char * SCBasename(const char *path)
Definition: util-path.c:246
LOGFILE_TYPE_UNIX_DGRAM
@ LOGFILE_TYPE_UNIX_DGRAM
Definition: util-logopenfile.h:40
LogFileTypeCtx_::thread_data
void * thread_data
Definition: util-logopenfile.h:68
SCLogOpenThreadedFile
bool SCLogOpenThreadedFile(const char *log_path, const char *append, LogFileCtx *parent_ctx)
Definition: util-logopenfile.c:394
LogFileCtx_::compress_ipv6
bool compress_ipv6
Definition: util-logopenfile.h:156
LOGFILE_TYPE_UNIX_STREAM
@ LOGFILE_TYPE_UNIX_STREAM
Definition: util-logopenfile.h:41
MemBuffer_
Definition: util-buffer.h:27
LogFileCtx_::is_regular
uint8_t is_regular
Definition: util-logopenfile.h:153
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:232
TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:329
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
LogFileCtx_::buffer_size
uint32_t buffer_size
Definition: util-logopenfile.h:118
LogFileCtx_::bytes_since_last_flush
uint64_t bytes_since_last_flush
Definition: util-logopenfile.h:174
SCGetThreadIdLong
#define SCGetThreadIdLong(...)
Definition: threads.h:256
SCConfNodeLookupChild
SCConfNode * SCConfNodeLookupChild(const SCConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:850
LogFileCtx_::parent
struct LogFileCtx_ * parent
Definition: util-logopenfile.h:105
LogFileEntry_
Definition: util-logopenfile.h:71
util-conf.h
LogThreadedFileCtx_
Definition: util-logopenfile.h:59
LogFileEnsureExists
LogFileCtx * LogFileEnsureExists(ThreadId thread_id, LogFileCtx *parent_ctx)
LogFileEnsureExists() Ensure a log file context for the thread exists.
Definition: util-logopenfile.c:787
suricata-common.h
LogFileTypeCtx_::init_data
void * init_data
Definition: util-logopenfile.h:67
util-path.h
LogFileCtx_::output_errors
uint64_t output_errors
Definition: util-logopenfile.h:171
ThreadLogFileHashEntry
Definition: util-logopenfile.h:48
ThreadId
uint32_t ThreadId
Definition: output-eve-bindgen.h:31
LogFileCtx_::Flush
void(* Flush)(struct LogFileCtx_ *fp)
Definition: util-logopenfile.h:94
PathIsAbsolute
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:44
LogFileCtx_::entry
ThreadLogFileHashEntry * entry
Definition: util-logopenfile.h:106
LogFileFreeCtx
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
Definition: util-logopenfile.c:952
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
LogFileCtx_::rotate_time
time_t rotate_time
Definition: util-logopenfile.h:130
FatalError
#define FatalError(...)
Definition: util-debug.h:517
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:33
ParseSizeStringU32
int ParseSizeStringU32(const char *size, uint32_t *res)
Definition: util-misc.c:174
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
SCConfLogOpenGeneric
int SCConfLogOpenGeneric(SCConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
open a generic output "log file", which may be a regular file or a socket
Definition: util-logopenfile.c:499
SCFree
#define SCFree(p)
Definition: util-mem.h:61
util-logopenfile.h
LOGFILE_ROTATE_INTERVAL
#define LOGFILE_ROTATE_INTERVAL
Definition: util-logopenfile.h:181
LogThreadedFileCtx_::mutex
SCMutex mutex
Definition: util-logopenfile.h:60
sc_errno
thread_local SCError sc_errno
Definition: util-error.c:31
LogFileCtx_::prefix
char * prefix
Definition: util-logopenfile.h:137
TAILQ_HEAD
#define TAILQ_HEAD(name, type)
Definition: queue.h:230
HashTableInit
HashTable * HashTableInit(uint32_t size, uint32_t(*Hash)(struct HashTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
Definition: util-hash.c:35
sc_log_global_log_level
SCLogLevel sc_log_global_log_level
Holds the global log level. Is the same as sc_log_config->log_level.
Definition: util-debug.c:101
suricata.h
MemBufferWriteString
void MemBufferWriteString(MemBuffer *dst, const char *fmt,...)
Definition: util-buffer.c:130
SCConfNode_::name
char * name
Definition: conf.h:38
LogFileCtx_::flags
uint8_t flags
Definition: util-logopenfile.h:146
LogFileCtx_::Close
void(* Close)(struct LogFileCtx_ *fp)
Definition: util-logopenfile.h:93
MEMBUFFER_BUFFER
#define MEMBUFFER_BUFFER(mem_buffer)
Get the MemBuffers underlying buffer.
Definition: util-buffer.h:51
LogFileTypeCtx_::filetype
SCEveFileType * filetype
Definition: util-logopenfile.h:66
util-misc.h
LogThreadedFileCtx_::ht
HashTable * ht
Definition: util-logopenfile.h:61
OutputUnregisterFileRotationFlag
void OutputUnregisterFileRotationFlag(int *flag)
Unregister a file rotation flag.
Definition: output.c:711
MEMBUFFER_OFFSET
#define MEMBUFFER_OFFSET(mem_buffer)
Get the MemBuffers current offset.
Definition: util-buffer.h:56
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:288
SCMutexDestroy
#define SCMutexDestroy
Definition: threads-debug.h:121
SCConfNode_
Definition: conf.h:37
LogFileCtx_::Rotate
void(* Rotate)(struct LogFileCtx_ *fp)
Definition: util-logopenfile.h:95
LogFileCtx_::fp
FILE * fp
Definition: util-logopenfile.h:79
SCMutex
#define SCMutex
Definition: threads-debug.h:114
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
LOGFILE_TYPE_FILETYPE
@ LOGFILE_TYPE_FILETYPE
Definition: util-logopenfile.h:44
output.h
LogFileCtx_::threaded
bool threaded
Definition: util-logopenfile.h:104
SCCreateDirectoryTree
int SCCreateDirectoryTree(const char *path, const bool final)
Recursively create a directory.
Definition: util-path.c:137
LogFileCtx_::filemode
uint32_t filemode
Definition: util-logopenfile.h:115