pcap-log: support dynamic file names in multi

When using multi mode, the filename can use a few variables:

%n -- thread number, where the 1st thread has 1, and it increments
%i -- thread id (system thread id, similar to pid)
%t -- timestamp, where seconds or seconds+usecs depends on
      the ts-format option.

Example:
filename: filename: pcaps/%n/pcap.%t
This will translate to: pcaps/3/pcap.1256792217 for the 3rd thread.

Note that while it's possible to use directories, they won't be
created. So make sure they exist.
pull/1045/head
Victor Julien 11 years ago
parent 6cebe7ef7b
commit be1979b2f9

@ -45,6 +45,7 @@
#include "util-byte.h"
#include "util-misc.h"
#include "util-cpu.h"
#include "util-atomic.h"
#include "source-pcap.h"
@ -71,6 +72,8 @@
#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;
@ -82,6 +85,8 @@ typedef struct PcapLogProfileData_ {
uint64_t cnt;
} PcapLogProfileData;
#define MAX_TOKS 9
/**
* PcapLog thread vars
*
@ -114,12 +119,15 @@ typedef struct PcapLogData_ {
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_ {
@ -148,6 +156,7 @@ void TmModulePcapLogRegister(void)
OutputRegisterModule(MODULE_NAME, "pcap-log", PcapLogInitCtx);
SC_ATOMIC_INIT(thread_cnt);
return;
}
@ -441,6 +450,14 @@ static PcapLogData *PcapLogDataCopy(const PcapLogData *pl)
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;
}
@ -552,6 +569,92 @@ static TmEcode PcapLogDataDeinit(ThreadVars *t, void *thread_data)
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
@ -598,6 +701,11 @@ static OutputCtx *PcapLogInitCtx(ConfNode *conf)
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;
@ -633,8 +741,8 @@ static OutputCtx *PcapLogInitCtx(ConfNode *conf)
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);
}
}
@ -682,7 +790,7 @@ static OutputCtx *PcapLogInitCtx(ConfNode *conf)
}
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) {
@ -845,16 +953,58 @@ static int PcapLogOpenFileCtx(PcapLogData *pl)
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);
}

Loading…
Cancel
Save