aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/util-logopenfile.c
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:21:41 -0700
commit8879b125d26e8db1a5633de5a9c692eb2d1c4f83 (patch)
treec7259d85a991b83dfa85ab2e339360669fc1f58e /framework/src/suricata/src/util-logopenfile.c
parent13d05bc8458758ee39cb829098241e89616717ee (diff)
suricata checkin based on commit id a4bce14770beee46a537eda3c3f6e8e8565d5d0a
Change-Id: I9a214fa0ee95e58fc640e50bd604dac7f42db48f
Diffstat (limited to 'framework/src/suricata/src/util-logopenfile.c')
-rw-r--r--framework/src/suricata/src/util-logopenfile.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/framework/src/suricata/src/util-logopenfile.c b/framework/src/suricata/src/util-logopenfile.c
new file mode 100644
index 00000000..b25c4a82
--- /dev/null
+++ b/framework/src/suricata/src/util-logopenfile.c
@@ -0,0 +1,383 @@
+/* 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
+ * \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;
+}
+
+/** \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);
+
+ OutputUnregisterFileRotationFlag(&lf_ctx->rotation_flag);
+
+ SCFree(lf_ctx);
+
+ SCReturnInt(1);
+}