mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
647 lines
20 KiB
C
647 lines
20 KiB
C
/* vi: set et ts=4: */
|
|
/* Copyright (C) 2007-2014 Open Information Security Foundation
|
|
*
|
|
* You can copy, redistribute or modify this Program under the terms of
|
|
* the GNU General Public License version 2 as published by the Free
|
|
* Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* version 2 along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
*
|
|
* \author Mike Pomraning <mpomraning@qualys.com>
|
|
*
|
|
* File-like output for logging: regular files and sockets.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
#include "suricata-common.h" /* errno.h, string.h, etc. */
|
|
#include "tm-modules.h" /* LogFileCtx */
|
|
#include "conf.h" /* ConfNode, etc. */
|
|
#include "output.h" /* DEFAULT_LOG_* */
|
|
#include "util-logopenfile.h"
|
|
#include "util-logopenfile-tile.h"
|
|
|
|
const char * redis_push_cmd = "LPUSH";
|
|
const char * redis_publish_cmd = "PUBLISH";
|
|
|
|
/** \brief connect to the indicated local stream socket, logging any errors
|
|
* \param path filesystem path to connect to
|
|
* \param log_err, non-zero if connect failure should be logged.
|
|
* \retval FILE* on success (fdopen'd wrapper of underlying socket)
|
|
* \retval NULL on error
|
|
*/
|
|
static FILE *
|
|
SCLogOpenUnixSocketFp(const char *path, int sock_type, int log_err)
|
|
{
|
|
struct sockaddr_un sun;
|
|
int s = -1;
|
|
FILE * ret = NULL;
|
|
|
|
memset(&sun, 0x00, sizeof(sun));
|
|
|
|
s = socket(PF_UNIX, sock_type, 0);
|
|
if (s < 0) goto err;
|
|
|
|
sun.sun_family = AF_UNIX;
|
|
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
|
|
|
|
if (connect(s, (const struct sockaddr *)&sun, sizeof(sun)) < 0)
|
|
goto err;
|
|
|
|
ret = fdopen(s, "w");
|
|
if (ret == NULL)
|
|
goto err;
|
|
|
|
return ret;
|
|
|
|
err:
|
|
if (log_err)
|
|
SCLogWarning(SC_ERR_SOCKET,
|
|
"Error connecting to socket \"%s\": %s (will keep trying)",
|
|
path, strerror(errno));
|
|
|
|
if (s >= 0)
|
|
close(s);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Attempt to reconnect a disconnected (or never-connected) Unix domain socket.
|
|
* \retval 1 if it is now connected; otherwise 0
|
|
*/
|
|
static int SCLogUnixSocketReconnect(LogFileCtx *log_ctx)
|
|
{
|
|
int disconnected = 0;
|
|
if (log_ctx->fp) {
|
|
SCLogWarning(SC_ERR_SOCKET,
|
|
"Write error on Unix socket \"%s\": %s; reconnecting...",
|
|
log_ctx->filename, strerror(errno));
|
|
fclose(log_ctx->fp);
|
|
log_ctx->fp = NULL;
|
|
log_ctx->reconn_timer = 0;
|
|
disconnected = 1;
|
|
}
|
|
|
|
struct timeval tv;
|
|
uint64_t now;
|
|
gettimeofday(&tv, NULL);
|
|
now = (uint64_t)tv.tv_sec * 1000;
|
|
now += tv.tv_usec / 1000; /* msec resolution */
|
|
if (log_ctx->reconn_timer != 0 &&
|
|
(now - log_ctx->reconn_timer) < LOGFILE_RECONN_MIN_TIME) {
|
|
/* Don't bother to try reconnecting too often. */
|
|
return 0;
|
|
}
|
|
log_ctx->reconn_timer = now;
|
|
|
|
log_ctx->fp = SCLogOpenUnixSocketFp(log_ctx->filename, log_ctx->sock_type, 0);
|
|
if (log_ctx->fp) {
|
|
/* Connected at last (or reconnected) */
|
|
SCLogNotice("Reconnected socket \"%s\"", log_ctx->filename);
|
|
} else if (disconnected) {
|
|
SCLogWarning(SC_ERR_SOCKET, "Reconnect failed: %s (will keep trying)",
|
|
strerror(errno));
|
|
}
|
|
|
|
return log_ctx->fp ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Write buffer to log file.
|
|
* \retval 0 on failure; otherwise, the return value of fwrite (number of
|
|
* characters successfully written).
|
|
*/
|
|
static int SCLogFileWrite(const char *buffer, int buffer_len, LogFileCtx *log_ctx)
|
|
{
|
|
/* Check for rotation. */
|
|
if (log_ctx->rotation_flag) {
|
|
log_ctx->rotation_flag = 0;
|
|
SCConfLogReopen(log_ctx);
|
|
}
|
|
|
|
int ret = 0;
|
|
|
|
if (log_ctx->fp == NULL && log_ctx->is_sock)
|
|
SCLogUnixSocketReconnect(log_ctx);
|
|
|
|
if (log_ctx->fp) {
|
|
clearerr(log_ctx->fp);
|
|
ret = fwrite(buffer, buffer_len, 1, log_ctx->fp);
|
|
fflush(log_ctx->fp);
|
|
|
|
if (ferror(log_ctx->fp) && log_ctx->is_sock) {
|
|
/* Error on Unix socket, maybe needs reconnect */
|
|
if (SCLogUnixSocketReconnect(log_ctx)) {
|
|
ret = fwrite(buffer, buffer_len, 1, log_ctx->fp);
|
|
fflush(log_ctx->fp);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void SCLogFileClose(LogFileCtx *log_ctx)
|
|
{
|
|
if (log_ctx->fp)
|
|
fclose(log_ctx->fp);
|
|
}
|
|
|
|
/** \brief open the indicated file, logging any errors
|
|
* \param path filesystem path to open
|
|
* \param append_setting open file with O_APPEND: "yes" or "no"
|
|
* \retval FILE* on success
|
|
* \retval NULL on error
|
|
*/
|
|
static FILE *
|
|
SCLogOpenFileFp(const char *path, const char *append_setting)
|
|
{
|
|
FILE *ret = NULL;
|
|
|
|
if (strcasecmp(append_setting, "yes") == 0) {
|
|
ret = fopen(path, "a");
|
|
} else {
|
|
ret = fopen(path, "w");
|
|
}
|
|
|
|
if (ret == NULL)
|
|
SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s",
|
|
path, strerror(errno));
|
|
return ret;
|
|
}
|
|
|
|
/** \brief open the indicated file remotely over PCIe to a host
|
|
* \param path filesystem path to open
|
|
* \param append_setting open file with O_APPEND: "yes" or "no"
|
|
* \retval FILE* on success
|
|
* \retval NULL on error
|
|
*/
|
|
static PcieFile *SCLogOpenPcieFp(LogFileCtx *log_ctx, const char *path,
|
|
const char *append_setting)
|
|
{
|
|
#ifndef __tile__
|
|
SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
|
|
"PCIe logging only supported on Tile-Gx Architecture.");
|
|
return NULL;
|
|
#else
|
|
return TileOpenPcieFp(log_ctx, path, append_setting);
|
|
#endif
|
|
}
|
|
|
|
/** \brief open a generic output "log file", which may be a regular file or a socket
|
|
* \param conf ConfNode structure for the output section in question
|
|
* \param log_ctx Log file context allocated by caller
|
|
* \param default_filename Default name of file to open, if not specified in ConfNode
|
|
* \param rotate Register the file for rotation in HUP.
|
|
* \retval 0 on success
|
|
* \retval -1 on error
|
|
*/
|
|
int
|
|
SCConfLogOpenGeneric(ConfNode *conf,
|
|
LogFileCtx *log_ctx,
|
|
const char *default_filename,
|
|
int rotate)
|
|
{
|
|
char log_path[PATH_MAX];
|
|
char *log_dir;
|
|
const char *filename, *filetype;
|
|
|
|
// Arg check
|
|
if (conf == NULL || log_ctx == NULL || default_filename == NULL) {
|
|
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
|
"SCConfLogOpenGeneric(conf %p, ctx %p, default %p) "
|
|
"missing an argument",
|
|
conf, log_ctx, default_filename);
|
|
return -1;
|
|
}
|
|
if (log_ctx->fp != NULL) {
|
|
SCLogError(SC_ERR_INVALID_ARGUMENT,
|
|
"SCConfLogOpenGeneric: previously initialized Log CTX "
|
|
"encountered");
|
|
return -1;
|
|
}
|
|
|
|
// Resolve the given config
|
|
filename = ConfNodeLookupChildValue(conf, "filename");
|
|
if (filename == NULL)
|
|
filename = default_filename;
|
|
|
|
log_dir = ConfigGetLogDirectory();
|
|
|
|
if (PathIsAbsolute(filename)) {
|
|
snprintf(log_path, PATH_MAX, "%s", filename);
|
|
} else {
|
|
snprintf(log_path, PATH_MAX, "%s/%s", log_dir, filename);
|
|
}
|
|
|
|
filetype = ConfNodeLookupChildValue(conf, "filetype");
|
|
if (filetype == NULL)
|
|
filetype = DEFAULT_LOG_FILETYPE;
|
|
|
|
const char *append = ConfNodeLookupChildValue(conf, "append");
|
|
if (append == NULL)
|
|
append = DEFAULT_LOG_MODE_APPEND;
|
|
|
|
// Now, what have we been asked to open?
|
|
if (strcasecmp(filetype, "unix_stream") == 0) {
|
|
/* Don't bail. May be able to connect later. */
|
|
log_ctx->is_sock = 1;
|
|
log_ctx->sock_type = SOCK_STREAM;
|
|
log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1);
|
|
} else if (strcasecmp(filetype, "unix_dgram") == 0) {
|
|
/* Don't bail. May be able to connect later. */
|
|
log_ctx->is_sock = 1;
|
|
log_ctx->sock_type = SOCK_DGRAM;
|
|
log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1);
|
|
} else if (strcasecmp(filetype, DEFAULT_LOG_FILETYPE) == 0 ||
|
|
strcasecmp(filetype, "file") == 0) {
|
|
log_ctx->fp = SCLogOpenFileFp(log_path, append);
|
|
if (log_ctx->fp == NULL)
|
|
return -1; // Error already logged by Open...Fp routine
|
|
log_ctx->is_regular = 1;
|
|
if (rotate) {
|
|
OutputRegisterFileRotationFlag(&log_ctx->rotation_flag);
|
|
}
|
|
} else if (strcasecmp(filetype, "pcie") == 0) {
|
|
log_ctx->pcie_fp = SCLogOpenPcieFp(log_ctx, log_path, append);
|
|
if (log_ctx->pcie_fp == NULL)
|
|
return -1; // Error already logged by Open...Fp routine
|
|
} else {
|
|
SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for "
|
|
"%s.filetype. Expected \"regular\" (default), \"unix_stream\", "
|
|
"\"pcie\" "
|
|
"or \"unix_dgram\"",
|
|
conf->name);
|
|
}
|
|
log_ctx->filename = SCStrdup(log_path);
|
|
if (unlikely(log_ctx->filename == NULL)) {
|
|
SCLogError(SC_ERR_MEM_ALLOC,
|
|
"Failed to allocate memory for filename");
|
|
return -1;
|
|
}
|
|
|
|
SCLogInfo("%s output device (%s) initialized: %s", conf->name, filetype,
|
|
filename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Reopen a regular log file with the side-affect of truncating it.
|
|
*
|
|
* This is useful to clear the log file and start a new one, or to
|
|
* re-open the file after its been moved by something external
|
|
* (eg. logrotate).
|
|
*/
|
|
int SCConfLogReopen(LogFileCtx *log_ctx)
|
|
{
|
|
if (!log_ctx->is_regular) {
|
|
/* Not supported and not needed on non-regular files. */
|
|
return 0;
|
|
}
|
|
|
|
if (log_ctx->filename == NULL) {
|
|
SCLogWarning(SC_ERR_INVALID_ARGUMENT,
|
|
"Can't re-open LogFileCtx without a filename.");
|
|
return -1;
|
|
}
|
|
|
|
fclose(log_ctx->fp);
|
|
|
|
/* Reopen the file. Append is forced in case the file was not
|
|
* moved as part of a rotation process. */
|
|
SCLogDebug("Reopening log file %s.", log_ctx->filename);
|
|
log_ctx->fp = SCLogOpenFileFp(log_ctx->filename, "yes");
|
|
if (log_ctx->fp == NULL) {
|
|
return -1; // Already logged by Open..Fp routine.
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_LIBHIREDIS
|
|
|
|
static void SCLogFileCloseRedis(LogFileCtx *log_ctx)
|
|
{
|
|
if (log_ctx->redis) {
|
|
redisReply *reply;
|
|
int i;
|
|
for (i = 0; i < log_ctx->redis_setup.batch_count; i++) {
|
|
redisGetReply(log_ctx->redis, (void **)&reply);
|
|
if (reply)
|
|
freeReplyObject(reply);
|
|
}
|
|
redisFree(log_ctx->redis);
|
|
log_ctx->redis = NULL;
|
|
}
|
|
log_ctx->redis_setup.tried = 0;
|
|
log_ctx->redis_setup.batch_count = 0;
|
|
}
|
|
|
|
int SCConfLogOpenRedis(ConfNode *redis_node, LogFileCtx *log_ctx)
|
|
{
|
|
const char *redis_server = NULL;
|
|
const char *redis_port = NULL;
|
|
const char *redis_mode = NULL;
|
|
const char *redis_key = NULL;
|
|
|
|
if (redis_node) {
|
|
redis_server = ConfNodeLookupChildValue(redis_node, "server");
|
|
redis_port = ConfNodeLookupChildValue(redis_node, "port");
|
|
redis_mode = ConfNodeLookupChildValue(redis_node, "mode");
|
|
redis_key = ConfNodeLookupChildValue(redis_node, "key");
|
|
}
|
|
if (!redis_server) {
|
|
redis_server = "127.0.0.1";
|
|
SCLogInfo("Using default redis server (127.0.0.1)");
|
|
}
|
|
if (!redis_port)
|
|
redis_port = "6379";
|
|
if (!redis_mode)
|
|
redis_mode = "list";
|
|
if (!redis_key)
|
|
redis_key = "suricata";
|
|
log_ctx->redis_setup.key = SCStrdup(redis_key);
|
|
|
|
if (!log_ctx->redis_setup.key) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key name");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
log_ctx->redis_setup.batch_size = 0;
|
|
|
|
ConfNode *pipelining = ConfNodeLookupChild(redis_node, "pipelining");
|
|
if (pipelining) {
|
|
int enabled = 0;
|
|
int ret;
|
|
intmax_t val;
|
|
ret = ConfGetChildValueBool(pipelining, "enabled", &enabled);
|
|
if (ret && enabled) {
|
|
ret = ConfGetChildValueInt(pipelining, "batch-size", &val);
|
|
if (ret) {
|
|
log_ctx->redis_setup.batch_size = val;
|
|
} else {
|
|
log_ctx->redis_setup.batch_size = 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!strcmp(redis_mode, "list")) {
|
|
log_ctx->redis_setup.command = redis_push_cmd;
|
|
if (!log_ctx->redis_setup.command) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key command");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
log_ctx->redis_setup.command = redis_publish_cmd;
|
|
if (!log_ctx->redis_setup.command) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key command");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
redisContext *c = redisConnect(redis_server, atoi(redis_port));
|
|
if (c != NULL && c->err) {
|
|
SCLogError(SC_ERR_SOCKET, "Error connecting to redis server: %s", c->errstr);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* store server params for reconnection */
|
|
log_ctx->redis_setup.server = SCStrdup(redis_server);
|
|
if (!log_ctx->redis_setup.server) {
|
|
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating redis server string");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
log_ctx->redis_setup.port = atoi(redis_port);
|
|
log_ctx->redis_setup.tried = 0;
|
|
|
|
log_ctx->redis = c;
|
|
|
|
log_ctx->Close = SCLogFileCloseRedis;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SCConfLogReopenRedis(LogFileCtx *log_ctx)
|
|
{
|
|
if (log_ctx->redis != NULL) {
|
|
redisFree(log_ctx->redis);
|
|
log_ctx->redis = NULL;
|
|
}
|
|
|
|
/* only try to reconnect once per second */
|
|
if (log_ctx->redis_setup.tried >= time(NULL)) {
|
|
return -1;
|
|
}
|
|
|
|
redisContext *c = redisConnect(log_ctx->redis_setup.server, log_ctx->redis_setup.port);
|
|
if (c != NULL && c->err) {
|
|
if (log_ctx->redis_setup.tried == 0) {
|
|
SCLogError(SC_ERR_SOCKET, "Error connecting to redis server: %s\n", c->errstr);
|
|
}
|
|
redisFree(c);
|
|
log_ctx->redis_setup.tried = time(NULL);
|
|
return -1;
|
|
}
|
|
log_ctx->redis = c;
|
|
log_ctx->redis_setup.tried = 0;
|
|
log_ctx->redis_setup.batch_count = 0;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/** \brief LogFileNewCtx() Get a new LogFileCtx
|
|
* \retval LogFileCtx * pointer if succesful, NULL if error
|
|
* */
|
|
LogFileCtx *LogFileNewCtx(void)
|
|
{
|
|
LogFileCtx* lf_ctx;
|
|
lf_ctx = (LogFileCtx*)SCMalloc(sizeof(LogFileCtx));
|
|
|
|
if (lf_ctx == NULL)
|
|
return NULL;
|
|
memset(lf_ctx, 0, sizeof(LogFileCtx));
|
|
|
|
SCMutexInit(&lf_ctx->fp_mutex,NULL);
|
|
|
|
// Default Write and Close functions
|
|
lf_ctx->Write = SCLogFileWrite;
|
|
lf_ctx->Close = SCLogFileClose;
|
|
|
|
#ifdef HAVE_LIBHIREDIS
|
|
lf_ctx->redis_setup.batch_count = 0;
|
|
#endif
|
|
|
|
return lf_ctx;
|
|
}
|
|
|
|
/** \brief LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
|
|
* \param motcx pointer to the OutputCtx
|
|
* \retval int 1 if succesful, 0 if error
|
|
* */
|
|
int LogFileFreeCtx(LogFileCtx *lf_ctx)
|
|
{
|
|
if (lf_ctx == NULL) {
|
|
SCReturnInt(0);
|
|
}
|
|
|
|
if (lf_ctx->fp != NULL) {
|
|
SCMutexLock(&lf_ctx->fp_mutex);
|
|
lf_ctx->Close(lf_ctx);
|
|
SCMutexUnlock(&lf_ctx->fp_mutex);
|
|
}
|
|
|
|
#ifdef HAVE_LIBHIREDIS
|
|
if (lf_ctx->type == LOGFILE_TYPE_REDIS) {
|
|
if (lf_ctx->redis)
|
|
redisFree(lf_ctx->redis);
|
|
if (lf_ctx->redis_setup.server)
|
|
SCFree(lf_ctx->redis_setup.server);
|
|
if (lf_ctx->redis_setup.key)
|
|
SCFree(lf_ctx->redis_setup.key);
|
|
}
|
|
#endif
|
|
|
|
SCMutexDestroy(&lf_ctx->fp_mutex);
|
|
|
|
if (lf_ctx->prefix != NULL) {
|
|
SCFree(lf_ctx->prefix);
|
|
lf_ctx->prefix_len = 0;
|
|
}
|
|
|
|
if(lf_ctx->filename != NULL)
|
|
SCFree(lf_ctx->filename);
|
|
|
|
if (lf_ctx->sensor_name)
|
|
SCFree(lf_ctx->sensor_name);
|
|
|
|
OutputUnregisterFileRotationFlag(&lf_ctx->rotation_flag);
|
|
|
|
SCFree(lf_ctx);
|
|
|
|
SCReturnInt(1);
|
|
}
|
|
|
|
#ifdef HAVE_LIBHIREDIS
|
|
static int LogFileWriteRedis(LogFileCtx *file_ctx, const char *string, size_t string_len)
|
|
{
|
|
if (file_ctx->redis == NULL) {
|
|
SCConfLogReopenRedis(file_ctx);
|
|
if (file_ctx->redis == NULL) {
|
|
return -1;
|
|
} else {
|
|
SCLogInfo("Reconnected to redis server");
|
|
}
|
|
}
|
|
/* TODO go async here ? */
|
|
if (file_ctx->redis_setup.batch_size) {
|
|
redisAppendCommand(file_ctx->redis, "%s %s %s",
|
|
file_ctx->redis_setup.command,
|
|
file_ctx->redis_setup.key,
|
|
string);
|
|
if (file_ctx->redis_setup.batch_count == file_ctx->redis_setup.batch_size) {
|
|
redisReply *reply;
|
|
int i;
|
|
file_ctx->redis_setup.batch_count = 0;
|
|
for (i = 0; i <= file_ctx->redis_setup.batch_size; i++) {
|
|
if (redisGetReply(file_ctx->redis, (void **)&reply) == REDIS_OK) {
|
|
freeReplyObject(reply);
|
|
} else {
|
|
if (file_ctx->redis->err) {
|
|
SCLogInfo("Error when fetching reply: %s (%d)",
|
|
file_ctx->redis->errstr,
|
|
file_ctx->redis->err);
|
|
}
|
|
switch (file_ctx->redis->err) {
|
|
case REDIS_ERR_EOF:
|
|
case REDIS_ERR_IO:
|
|
SCLogInfo("Reopening connection to redis server");
|
|
SCConfLogReopenRedis(file_ctx);
|
|
if (file_ctx->redis) {
|
|
SCLogInfo("Reconnected to redis server");
|
|
return 0;
|
|
} else {
|
|
SCLogInfo("Unable to reconnect to redis server");
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
SCLogWarning(SC_ERR_INVALID_VALUE,
|
|
"Unsupported error code %d",
|
|
file_ctx->redis->err);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
file_ctx->redis_setup.batch_count++;
|
|
}
|
|
} else {
|
|
redisReply *reply = redisCommand(file_ctx->redis, "%s %s %s",
|
|
file_ctx->redis_setup.command,
|
|
file_ctx->redis_setup.key,
|
|
string);
|
|
|
|
switch (reply->type) {
|
|
case REDIS_REPLY_ERROR:
|
|
SCLogWarning(SC_ERR_SOCKET, "Redis error: %s", reply->str);
|
|
SCConfLogReopenRedis(file_ctx);
|
|
break;
|
|
case REDIS_REPLY_INTEGER:
|
|
SCLogDebug("Redis integer %lld", reply->integer);
|
|
break;
|
|
default:
|
|
SCLogError(SC_ERR_INVALID_VALUE,
|
|
"Redis default triggered with %d", reply->type);
|
|
SCConfLogReopenRedis(file_ctx);
|
|
break;
|
|
}
|
|
freeReplyObject(reply);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int LogFileWrite(LogFileCtx *file_ctx, MemBuffer *buffer)
|
|
{
|
|
if (file_ctx->type == LOGFILE_TYPE_SYSLOG) {
|
|
syslog(file_ctx->syslog_setup.alert_syslog_level, "%s",
|
|
(const char *)MEMBUFFER_BUFFER(buffer));
|
|
} else if (file_ctx->type == LOGFILE_TYPE_FILE ||
|
|
file_ctx->type == LOGFILE_TYPE_UNIX_DGRAM ||
|
|
file_ctx->type == LOGFILE_TYPE_UNIX_STREAM)
|
|
{
|
|
/* append \n for files only */
|
|
MemBufferWriteString(buffer, "\n");
|
|
SCMutexLock(&file_ctx->fp_mutex);
|
|
file_ctx->Write((const char *)MEMBUFFER_BUFFER(buffer),
|
|
MEMBUFFER_OFFSET(buffer), file_ctx);
|
|
SCMutexUnlock(&file_ctx->fp_mutex);
|
|
}
|
|
#ifdef HAVE_LIBHIREDIS
|
|
else if (file_ctx->type == LOGFILE_TYPE_REDIS) {
|
|
SCMutexLock(&file_ctx->fp_mutex);
|
|
LogFileWriteRedis(file_ctx, (const char *)MEMBUFFER_BUFFER(buffer),
|
|
MEMBUFFER_OFFSET(buffer));
|
|
SCMutexUnlock(&file_ctx->fp_mutex);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|