/* Copyright (C) 2007-2010 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 Anoop Saldanha <anoopsaldanha@gmail.com>
 */

#ifndef __UTIL_DEBUG_H__
#define __UTIL_DEBUG_H__

#include <stdio.h>
#include <stdint.h>
#include <pcre.h>

#include "threads.h"
#include "util-enum.h"
#include "util-error.h"
#include "util-debug-filters.h"

/**
 * \brief ENV vars that can be used to set the properties for the logging module
 */
#define SC_LOG_ENV_LOG_LEVEL        "SC_LOG_LEVEL"
#define SC_LOG_ENV_LOG_OP_IFACE     "SC_LOG_OP_IFACE"
#define SC_LOG_ENV_LOG_FILE         "SC_LOG_FILE"
#define SC_LOG_ENV_LOG_FACILITY     "SC_LOG_FACILITY"
#define SC_LOG_ENV_LOG_FORMAT       "SC_LOG_FORMAT"
#define SC_LOG_ENV_LOG_OP_FILTER    "SC_LOG_OP_FILTER"

/**
 * \brief The various log levels
 * NOTE: when adding new level, don't forget to update SCLogMapLogLevelToSyslogLevel()
  *      or it may result in logging to syslog with LOG_EMERG priority.
 */
typedef enum {
    SC_LOG_NOTSET = -1,
    SC_LOG_NONE = 0,
    SC_LOG_EMERGENCY,
    SC_LOG_ALERT,
    SC_LOG_CRITICAL,
    SC_LOG_ERROR,
    SC_LOG_WARNING,
    SC_LOG_NOTICE,
    SC_LOG_INFO,
    SC_LOG_PERF,
    SC_LOG_CONFIG,
    SC_LOG_DEBUG,
    SC_LOG_LEVEL_MAX,
} SCLogLevel;

/**
 * \brief The various output interfaces supported
 */
typedef enum {
    SC_LOG_OP_IFACE_CONSOLE,
    SC_LOG_OP_IFACE_FILE,
    SC_LOG_OP_IFACE_SYSLOG,
    SC_LOG_OP_IFACE_MAX,
} SCLogOPIface;

typedef enum {
    SC_LOG_OP_TYPE_REGULAR = 0,
    SC_LOG_OP_TYPE_JSON,
} SCLogOPType;

/* The default log_format, if it is not supplied by the user */
#ifdef RELEASE
#define SC_LOG_DEF_LOG_FORMAT "%t - <%d> - "
#else
#define SC_LOG_DEF_LOG_FORMAT "[%i] %t - (%f:%l) <%d> (%n) -- "
#endif

/* The maximum length of the log message */
#define SC_LOG_MAX_LOG_MSG_LEN 2048

/* The maximum length of the log format */
#define SC_LOG_MAX_LOG_FORMAT_LEN 128

/* The default log level, if it is not supplied by the user */
#define SC_LOG_DEF_LOG_LEVEL SC_LOG_INFO

/* The default output interface to be used */
#define SC_LOG_DEF_LOG_OP_IFACE SC_LOG_OP_IFACE_CONSOLE

/* The default log file to be used */
#define SC_LOG_DEF_LOG_FILE "suricata.log"

/* The default syslog facility to be used */
#define SC_LOG_DEF_SYSLOG_FACILITY_STR "local0"
#define SC_LOG_DEF_SYSLOG_FACILITY LOG_LOCAL0

/**
 * \brief Structure to be used when log_level override support would be provided
 *        by the logging module
 */
typedef struct SCLogOPBuffer_ {
    char msg[SC_LOG_MAX_LOG_MSG_LEN];
    char *temp;
    const char *log_format;
} SCLogOPBuffer;

/**
 * \brief The output interface context for the logging module
 */
typedef struct SCLogOPIfaceCtx_ {
    SCLogOPIface iface;

    int16_t use_color;
    int16_t type;

    /* the output file to be used if the interface is SC_LOG_IFACE_FILE */
    const char *file;
    /* the output file descriptor for the above file */
    FILE * file_d;

    /* registered to be set on a file rotation signal */
    int rotation_flag;

    /* the facility code if the interface is SC_LOG_IFACE_SYSLOG */
    int facility;

    /* override for the global_log_level */
    SCLogLevel log_level;

    /* override for the global_log_format(currently not used) */
    const char *log_format;

    /* Mutex used for locking around rotate/write to a file. */
    SCMutex fp_mutex;

    struct SCLogOPIfaceCtx_ *next;
} SCLogOPIfaceCtx;

/**
 * \brief Structure containing init data, that would be passed to
 *        SCInitDebugModule()
 */
typedef struct SCLogInitData_ {
    /* startup message */
    const char *startup_message;

    /* the log level */
    SCLogLevel global_log_level;

    /* the log format */
    const char *global_log_format;

    /* output filter */
    const char *op_filter;

    /* list of output interfaces to be used */
    SCLogOPIfaceCtx *op_ifaces;
    /* no of op ifaces */
    uint8_t op_ifaces_cnt;
} SCLogInitData;

/**
 * \brief Holds the config state used by the logging api
 */
typedef struct SCLogConfig_ {
    char *startup_message;
    SCLogLevel log_level;
    char *log_format;

    char *op_filter;
    /* compiled pcre filter expression */
    pcre *op_filter_regex;
    pcre_extra *op_filter_regex_study;

    /* op ifaces used */
    SCLogOPIfaceCtx *op_ifaces;
    /* no of op ifaces */
    uint8_t op_ifaces_cnt;
} SCLogConfig;

/* The different log format specifiers supported by the API */
#define SC_LOG_FMT_TIME             't' /* Timestamp in standard format */
#define SC_LOG_FMT_PID              'p' /* PID */
#define SC_LOG_FMT_TID              'i' /* Thread ID */
#define SC_LOG_FMT_TM               'm' /* Thread module name */
#define SC_LOG_FMT_LOG_LEVEL        'd' /* Log level */
#define SC_LOG_FMT_FILE_NAME        'f' /* File name */
#define SC_LOG_FMT_LINE             'l' /* Line number */
#define SC_LOG_FMT_FUNCTION         'n' /* Function */

/* The log format prefix for the format specifiers */
#define SC_LOG_FMT_PREFIX           '%'

extern SCLogLevel sc_log_global_log_level;

extern int sc_log_module_initialized;

extern int sc_log_module_cleaned;

#define SCLog(x, file, func, line, ...)                                         \
    do {                                                                        \
        if (sc_log_global_log_level >= x &&                                     \
               (sc_log_fg_filters_present == 0 ||                               \
                SCLogMatchFGFilterWL(file, func, line) == 1 ||                  \
                SCLogMatchFGFilterBL(file, func, line) == 1) &&                 \
               (sc_log_fd_filters_present == 0 ||                               \
                SCLogMatchFDFilter(func) == 1))                                 \
        {                                                                       \
            char _sc_log_msg[SC_LOG_MAX_LOG_MSG_LEN];                           \
                                                                                \
            int _sc_log_ret = snprintf(_sc_log_msg, SC_LOG_MAX_LOG_MSG_LEN, __VA_ARGS__);   \
            if (_sc_log_ret == SC_LOG_MAX_LOG_MSG_LEN)                          \
                _sc_log_msg[SC_LOG_MAX_LOG_MSG_LEN - 1] = '\0';                 \
                                                                                \
            SCLogMessage(x, file, line, func, SC_OK, _sc_log_msg);              \
        }                                                                       \
    } while(0)

#define SCLogErr(x, file, func, line, err, ...)                                 \
    do {                                                                        \
        if (sc_log_global_log_level >= x &&                                     \
               (sc_log_fg_filters_present == 0 ||                               \
                SCLogMatchFGFilterWL(file, func, line) == 1 ||                  \
                SCLogMatchFGFilterBL(file, func, line) == 1) &&                 \
               (sc_log_fd_filters_present == 0 ||                               \
                SCLogMatchFDFilter(func) == 1))                                 \
        {                                                                       \
            char _sc_log_msg[SC_LOG_MAX_LOG_MSG_LEN];                           \
                                                                                \
            int _sc_log_ret = snprintf(_sc_log_msg, SC_LOG_MAX_LOG_MSG_LEN, __VA_ARGS__);   \
            if (_sc_log_ret == SC_LOG_MAX_LOG_MSG_LEN)                          \
                _sc_log_msg[SC_LOG_MAX_LOG_MSG_LEN - 1] = '\0';                 \
                                                                                \
            SCLogMessage(x, file, line, func, err, _sc_log_msg);                \
        }                                                                       \
    } while(0)

/**
 * \brief Macro used to log INFORMATIONAL messages.
 *
 * \retval ... Takes as argument(s), a printf style format message
 */
#define SCLogInfo(...) SCLog(SC_LOG_INFO, \
        __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define SCLogInfoRaw(file, func, line, ...) SCLog(SC_LOG_INFO, \
        (file), (func), (line), __VA_ARGS__)

#define SCLogConfig(...) SCLog(SC_LOG_CONFIG, \
        __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define SCLogPerf(...) SCLog(SC_LOG_PERF, \
        __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

/**
 * \brief Macro used to log NOTICE messages.
 *
 * \retval ... Takes as argument(s), a printf style format message
 */
#define SCLogNotice(...) SCLog(SC_LOG_NOTICE, \
        __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define SCLogNoticeRaw(file, func, line, ... ) SCLog(SC_LOG_NOTICE, \
        (file), (func), (line), __VA_ARGS__)

/**
 * \brief Macro used to log WARNING messages.
 *
 * \retval err_code Error code that has to be logged along with the
 *                  warning message
 * \retval ...      Takes as argument(s), a printf style format message
 */
#define SCLogWarning(err_code, ...) SCLogErr(SC_LOG_WARNING, \
        __FILE__, __FUNCTION__, __LINE__, \
        err_code, __VA_ARGS__)
#define SCLogWarningRaw(err_code, file, func, line, ...) \
    SCLogErr(SC_LOG_WARNING, (file), (func), (line), err_code, __VA_ARGS__)

/**
 * \brief Macro used to log ERROR messages.
 *
 * \retval err_code Error code that has to be logged along with the
 *                  error message
 * \retval ...      Takes as argument(s), a printf style format message
 */
#define SCLogError(err_code, ...) SCLogErr(SC_LOG_ERROR, \
        __FILE__, __FUNCTION__, __LINE__, \
        err_code, __VA_ARGS__)
#define SCLogErrorRaw(err_code, file, func, line, ...) SCLogErr(SC_LOG_ERROR, \
        (file), (func), (line), err_code, __VA_ARGS__)

/**
 * \brief Macro used to log CRITICAL messages.
 *
 * \retval err_code Error code that has to be logged along with the
 *                  critical message
 * \retval ...      Takes as argument(s), a printf style format message
 */
#define SCLogCritical(err_code, ...) SCLogErr(SC_LOG_CRITICAL, \
        __FILE__, __FUNCTION__, __LINE__, \
        err_code, __VA_ARGS__)
/**
 * \brief Macro used to log ALERT messages.
 *
 * \retval err_code Error code that has to be logged along with the
 *                  alert message
 * \retval ...      Takes as argument(s), a printf style format message
 */
#define SCLogAlert(err_code, ...) SCLogErr(SC_LOG_ALERT, \
        __FILE__, __FUNCTION__, __LINE__, \
        err_code, __VA_ARGS__)
/**
 * \brief Macro used to log EMERGENCY messages.
 *
 * \retval err_code Error code that has to be logged along with the
 *                  emergency message
 * \retval ...      Takes as argument(s), a printf style format message
 */
#define SCLogEmerg(err_code, ...) SCLogErr(SC_LOG_EMERGENCY, \
        __FILE__, __FUNCTION__, __LINE__, \
        err_code, __VA_ARGS__)


/* Avoid the overhead of using the debugging subsystem, in production mode */
#ifndef DEBUG

#define SCLogDebug(...)                 do { } while (0)

#define SCEnter(...)

#define SCReturn                        return

#define SCReturnInt(x)                  return x

#define SCReturnUInt(x)                 return x

#define SCReturnDbl(x)                  return x

#define SCReturnChar(x)                 return x

#define SCReturnCharPtr(x)              return x

#define SCReturnCT(x, type)             return x

#define SCReturnPtr(x, type)            return x

/* Please use it only for debugging purposes */
#else


/**
 * \brief Macro used to log DEBUG messages. Comes under the debugging subsystem,
 *        and hence will be enabled only in the presence of the DEBUG macro.
 *
 * \retval ... Takes as argument(s), a printf style format message
 */
#define SCLogDebug(...)       SCLog(SC_LOG_DEBUG, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

/**
 * \brief Macro used to log debug messages on function entry.  Comes under the
 *        debugging subsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_entry logs, it also
 *        processes the FD filters, if any FD filters are registered.
 *
 * \retval f An argument can be supplied, although it is not used
 */
#define SCEnter(f)            do {                                              \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG &&\
                                      SCLogCheckFDFilterEntry(__FUNCTION__))    \
                                  {                                             \
                                     SCLogDebug("Entering ... >>");             \
                                  }                                             \
                              } while(0)


/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that don't return
 *        a value.
 */
#define SCReturn              do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning ... <<" );       \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return;                                    \
                              } while(0)

/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns an
 *        integer value.
 *
 * \retval x Variable of type 'integer' that has to be returned
 */
#define SCReturnInt(x)        do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning: %"PRIdMAX" ... <<", (intmax_t)x); \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return x;                                  \
                              } while(0)

/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns an
 *        unsigned integer value.
 *
 * \retval x Variable of type 'unsigned integer' that has to be returned
 */
#define SCReturnUInt(x)       do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning: %"PRIuMAX" ... <<", (uintmax_t)x); \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return x;                                  \
                              } while(0)

/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns a
 *        float/double value.
 *
 * \retval x Variable of type 'float/double' that has to be returned
 */
#define SCReturnDbl(x)        do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning: %f ... <<", x); \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return x;                                  \
                              } while(0)

/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns a var
 *        of character type.
 *
 * \retval x Variable of type 'char' that has to be returned
 */
#define SCReturnChar(x)       do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning: %c ... <<", x); \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return x;                                  \
                              } while(0)

/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns a
 *        character string.
 *
 * \retval x Pointer to the char string that has to be returned
 */
#define SCReturnCharPtr(x)    do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      if ((x) != NULL) {                    \
                                          SCLogDebug("Returning: %s ... <<", x); \
                                      } else {                          \
                                          SCLogDebug("Returning: NULL ... <<"); \
                                      } SCLogCheckFDFilterExit(__FUNCTION__); \
                                  }                                     \
                                 return x;                                   \
                              } while(0)


/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns a var
 *        of custom type
 *
 * \retval x    Variable instance of a custom type that has to be returned
 * \retval type Pointer to a character string holding the name of the custom
 *              type(the argument x) that has to be returned
 */
#define SCReturnCT(x, type)   do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning var of "         \
                                              "type %s ... <<", type);       \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return x;                                  \
                              } while(0)

/**
 * \brief Macro used to log debug messages on function exit.  Comes under the
 *        debugging sybsystem, and hence will be enabled only in the presence
 *        of the DEBUG macro.  Apart from logging function_exit logs, it also
 *        processes the FD filters, if any FD filters are registered.  This
 *        function_exit macro should be used for functions that returns a
 *        pointer to a custom type
 *
 * \retval x    Pointer to a variable instance of a custom type that has to be
 *              returned
 * \retval type Pointer to a character string holding the name of the custom
 *              type(the argument x) that has to be returned
 */
#define SCReturnPtr(x, type)  do {                                           \
                                  if (sc_log_global_log_level >= SC_LOG_DEBUG) { \
                                      SCLogDebug("Returning pointer %p of "  \
                                              "type %s ... <<", x, type);    \
                                      SCLogCheckFDFilterExit(__FUNCTION__);  \
                                  }                                          \
                                  return x;                                  \
                              } while(0)

#endif /* DEBUG */

#define FatalError(x, ...) do {                                             \
    SCLogError(x, __VA_ARGS__);                                             \
    exit(EXIT_FAILURE);                                                     \
} while(0)

/** \brief Fatal error IF we're starting up, and configured to consider
 *         errors to be fatal errors */
#if !defined(__clang_analyzer__)
#define FatalErrorOnInit(x, ...) do {                                       \
    int init_errors_fatal = 0;                                              \
    ConfGetBool("engine.init-failure-fatal", &init_errors_fatal);           \
    if (init_errors_fatal && (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT))\
    {                                                                       \
        SCLogError(x, __VA_ARGS__);                                         \
        exit(EXIT_FAILURE);                                                 \
    }                                                                       \
    SCLogWarning(x, __VA_ARGS__);                                           \
} while(0)
/* make it simpler for scan-build */
#else
#define FatalErrorOnInit(x, ...) FatalError(x, __VA_ARGS__)
#endif

SCLogInitData *SCLogAllocLogInitData(void);

SCLogOPIfaceCtx *SCLogInitOPIfaceCtx(const char *, const char *, int,
                                     const char *);

void SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx *, SCLogInitData *);

void SCLogInitLogModule(SCLogInitData *);

void SCLogDeInitLogModule(void);

SCError SCLogMessage(const SCLogLevel, const char *, const unsigned int,
                     const char *, const SCError, const char *message);

SCLogOPBuffer *SCLogAllocLogOPBuffer(void);

int SCLogDebugEnabled(void);

void SCLogRegisterTests(void);

void SCLogLoadConfig(int daemon, int verbose);

#endif /* __UTIL_DEBUG_H__ */
