#include "util-byte.h"
#include "util-misc.h"
#include "util-cpu.h"
+#include "util-atomic.h"
#include "source-pcap.h"
#define USE_STREAM_DEPTH_DISABLED 0
#define USE_STREAM_DEPTH_ENABLED 1
+SC_ATOMIC_DECLARE(uint32_t, thread_cnt);
+
typedef struct PcapFileName_ {
char *filename;
char *dirname;
uint64_t cnt;
} PcapLogProfileData;
+#define MAX_TOKS 9
+
/**
* PcapLog thread vars
*
TAILQ_HEAD(, PcapFileName_) pcap_file_list;
+ uint32_t thread_number; /**< thread number, first thread is 1, second 2, etc */
int use_ringbuffer; /**< ring buffer mode enabled or disabled */
int timestamp_format; /**< timestamp format sec or usec */
char *prefix; /**< filename prefix */
char dir[PATH_MAX]; /**< pcap log directory */
int reported;
int threads; /**< number of threads (only set in the global) */
+ char *filename_parts[MAX_TOKS];
+ int filename_part_cnt;
} PcapLogData;
typedef struct PcapLogThreadData_ {
OutputRegisterModule(MODULE_NAME, "pcap-log", PcapLogInitCtx);
+ SC_ATOMIC_INIT(thread_cnt);
return;
}
strlcpy(copy->dir, pl->dir, sizeof(copy->dir));
+ int i;
+ for (i = 0; i < pl->filename_part_cnt && i < MAX_TOKS; i++)
+ copy->filename_parts[i] = pl->filename_parts[i];
+ copy->filename_part_cnt = pl->filename_part_cnt;
+
+ /* set thread number, first thread is 1 */
+ copy->thread_number = SC_ATOMIC_ADD(thread_cnt, 1);
+
SCLogDebug("copied, returning %p", copy);
return copy;
}
return TM_ECODE_OK;
}
+static int ParseFilename(PcapLogData *pl, const char *filename)
+{
+ char *toks[MAX_TOKS] = { NULL };
+ int tok = 0;
+ char str[512] = "";
+ int s = 0;
+ int i, x;
+ char *p = NULL;
+
+ if (filename) {
+ for (i = 0; i < (int)strlen(filename); i++) {
+ if (tok >= MAX_TOKS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Max 2 %%-sign options");
+ goto error;
+ }
+
+ str[s++] = filename[i];
+
+ if (filename[i] == '%') {
+ str[s-1] = '\0';
+ SCLogDebug("filename with %%-sign: %s", str);
+
+ p = SCStrdup(str);
+ if (p == NULL)
+ goto error;
+ toks[tok++] = p;
+
+ s = 0;
+
+ if (i+1 < (int)strlen(filename)) {
+ if (tok >= MAX_TOKS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Max 2 %%-sign options");
+ goto error;
+ }
+
+ if (filename[i+1] != 'n' && filename[i+1] != 't' && filename[i+1] != 'i') {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Valid %%-sign options: %%n, %%i and %%t");
+ goto error;
+ }
+ str[0] = '%';
+ str[1] = filename[i+1];
+ str[2] = '\0';
+ p = SCStrdup(str);
+ if (p == NULL)
+ goto error;
+ toks[tok++] = p;
+ i++;
+ }
+ }
+ }
+ if (s) {
+ if (tok >= MAX_TOKS) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT,
+ "invalid filename option. Max 3 %%-sign options");
+ goto error;
+
+ }
+ str[s++] = '\0';
+ p = SCStrdup(str);
+ if (p == NULL)
+ goto error;
+ toks[tok++] = p;
+ }
+
+ /* finally, store tokens in the pl */
+ for (i = 0; i < tok; i++) {
+ if (toks[i] == NULL)
+ goto error;
+
+ SCLogDebug("toks[%d] %s", i, toks[i]);
+ pl->filename_parts[i] = toks[i];
+ }
+ pl->filename_part_cnt = tok;
+ }
+ return 0;
+error:
+ for (x = 0; x < MAX_TOKS; x++) {
+ if (toks[x] != NULL)
+ SCFree(toks[x]);
+ }
+ return -1;
+}
+
/** \brief Fill in pcap logging struct from the provided ConfNode.
* \param conf The configuration node for this output.
* \retval output_ctx
exit(EXIT_FAILURE);
}
+ if (filename) {
+ if (ParseFilename(pl, filename) != 0)
+ exit(EXIT_FAILURE);
+ }
+
pl->size_limit = DEFAULT_LIMIT;
if (conf != NULL) {
const char *s_limit = NULL;
pl->mode = LOGMODE_MULTI;
} else if (strcasecmp(s_mode, "normal") != 0) {
SCLogError(SC_ERR_INVALID_ARGUMENT,
- "log-pcap you must specify \"sguil\" or \"normal\" mode "
- "option to be set.");
+ "log-pcap: invalid mode \"%s\". Valid options: \"normal\", "
+ "\"sguil\", or \"multi\" mode ", s_mode);
exit(EXIT_FAILURE);
}
}
}
SCLogInfo("using %s logging", pl->mode == LOGMODE_SGUIL ?
- "Sguil compatible" : "normal");
+ "Sguil compatible" : (pl->mode == LOGMODE_MULTI ? "multi" : "normal"));
uint32_t max_file_limit = DEFAULT_FILE_LIMIT;
if (conf != NULL) {
pl->prefix, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
}
} else if (pl->mode == LOGMODE_MULTI) {
- long thread_id = SCGetThreadIdLong();
- uint64_t tid = (uint64_t)thread_id;
-
- /* create the filename to use */
- if (pl->timestamp_format == TS_FORMAT_SEC) {
- snprintf(filename, PATH_MAX, "%s/%s.%"PRIu64".%" PRIu32, pl->dir,
- pl->prefix, tid, (uint32_t)ts.tv_sec);
+ if (pl->filename_part_cnt > 0) {
+ /* assemble filename from stored tokens */
+
+ strlcpy(filename, pl->dir, PATH_MAX);
+ strlcat(filename, "/", PATH_MAX);
+
+ int i;
+ for (i = 0; i < pl->filename_part_cnt; i++) {
+ if (pl->filename_parts[i] == NULL ||strlen(pl->filename_parts[i]) == 0)
+ continue;
+
+ /* handle variables */
+ if (pl->filename_parts[i][0] == '%') {
+ char str[64] = "";
+ if (strlen(pl->filename_parts[i]) < 2)
+ continue;
+
+ switch(pl->filename_parts[i][1]) {
+ case 'n':
+ snprintf(str, sizeof(str), "%u", pl->thread_number);
+ break;
+ case 'i':
+ {
+ long thread_id = SCGetThreadIdLong();
+ snprintf(str, sizeof(str), "%"PRIu64, (uint64_t)thread_id);
+ break;
+ }
+ case 't':
+ /* create the filename to use */
+ if (pl->timestamp_format == TS_FORMAT_SEC) {
+ snprintf(str, sizeof(str), "%"PRIu32, (uint32_t)ts.tv_sec);
+ } else {
+ snprintf(str, sizeof(str), "%"PRIu32".%"PRIu32,
+ (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ }
+ }
+ strlcat(filename, str, PATH_MAX);
+
+ /* copy the rest over */
+ } else {
+ strlcat(filename, pl->filename_parts[i], PATH_MAX);
+ }
+ }
} else {
- snprintf(filename, PATH_MAX, "%s/%s.%"PRIu64".%" PRIu32 ".%" PRIu32, pl->dir,
- pl->prefix, tid, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ /* create the filename to use */
+ if (pl->timestamp_format == TS_FORMAT_SEC) {
+ snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32, pl->dir,
+ pl->prefix, pl->thread_number, (uint32_t)ts.tv_sec);
+ } else {
+ snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32 ".%" PRIu32, pl->dir,
+ pl->prefix, pl->thread_number, (uint32_t)ts.tv_sec, (uint32_t)ts.tv_usec);
+ }
}
SCLogDebug("multi-mode: filename %s", filename);
}