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.
suricata/src/util-logopenfile.c

313 lines
9.0 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"
/** \brief connect to the indicated local stream socket, logging any errors
* \param path filesystem path to connect to
* \retval FILE* on success (fdopen'd wrapper of underlying socket)
* \retval NULL on error
*/
static FILE *
SCLogOpenUnixSocketFp(const char *path, int sock_type)
{
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:
SCLogError(SC_ERR_SOCKET, "Error connecting to socket \"%s\": %s",
path, strerror(errno));
if (s >= 0)
close(s);
return NULL;
}
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) {
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
* \retval 0 on success
* \retval -1 on error
*/
int
SCConfLogOpenGeneric(ConfNode *conf,
LogFileCtx *log_ctx,
const char *default_filename)
{
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) {
log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM);
if (log_ctx->fp == NULL)
return -1; // Error already logged by Open...Fp routine
} else if (strcasecmp(filetype, "unix_dgram") == 0) {
log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM);
if (log_ctx->fp == NULL)
return -1; // Error already logged by Open...Fp routine
} 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;
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;
}
} 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);
}
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. In this case do not append like may have been
* done on the initial opening of the file. */
log_ctx->fp = SCLogOpenFileFp(log_ctx->filename, "no");
if (log_ctx->fp == NULL) {
return -1; // Already logged by Open..Fp routine.
}
return 0;
}
/** \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;
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);
}
SCMutexDestroy(&lf_ctx->fp_mutex);
if (lf_ctx->prefix != NULL)
SCFree(lf_ctx->prefix);
if(lf_ctx->filename != NULL)
SCFree(lf_ctx->filename);
SCFree(lf_ctx);
SCReturnInt(1);
}