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