/*
 *****************************************************************************
 *
 * File:    log_msg.c
 *
 * Purpose: General logging routine that can write to syslog and/or stderr
 *          and can take varibale number of args.
 *
 *  Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
 *  Copyright (C) 2009-2014 fwknop developers and contributors. For a full
 *  list of contributors, see the file 'CREDITS'.
 *
 *  License (GNU General Public License):
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  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
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 *  USA
 *
 *****************************************************************************
*/
#include "fwknopd_common.h"
#include "utils.h"
#include "log_msg.h"

/* The default log facility (can be overridden via config file directive).
*/
static int  syslog_fac      = LOG_DAEMON;

/* This value is or'ed with the log level on all logging calls. This allows
 * for force log to stderr instead of syslog simply be setting this to the
 * appropriate value (which is done at init_logging().
*/
static int  static_log_flag = LOG_STDERR_ONLY;

/* The name to use for ID in log messages.  This defaults to fwknopd.
*/
static char *log_name = NULL;

/* The value of the default verbosity used by the log module */
static int verbosity = LOG_DEFAULT_VERBOSITY;

/* Free resources allocated for logging.
*/
void
free_logging(void)
{
    if(log_name != NULL)
        free(log_name);
}

/* Initialize logging sets the name used for syslog.
*/
void
init_logging(fko_srv_options_t *opts) {
    char                   *my_name = NULL;
    int                     is_syslog = 0;

    /* In case this is a re-init.
    */
    free_logging();

    /* Allocate memory for the log_name and set the my_name to point to the
     * appropriate name. The name should already be set in the config struct
     * but if it is not, fallback to the default as defined by 'MY_NAME'.
    */
    if(opts->config[CONF_SYSLOG_IDENTITY] != NULL
      && opts->config[CONF_SYSLOG_IDENTITY][0] != '\0')
    {
        my_name  = opts->config[CONF_SYSLOG_IDENTITY];
        log_name = calloc(1, strlen(opts->config[CONF_SYSLOG_IDENTITY])+1);
        is_syslog = 1;
    }
    else
    {
        my_name  = (char*)&MY_NAME;
        log_name = calloc(1, strlen(MY_NAME)+1);
    }

    if(log_name == NULL)
    {
        fprintf(stderr, "Memory allocation error setting log_name!\n");
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* Set our name.
    */
    if (is_syslog)
        strlcpy(log_name, my_name, strlen(opts->config[CONF_SYSLOG_IDENTITY])+1);
    else
        strlcpy(log_name, my_name, strlen(MY_NAME)+1);

    static_log_flag = LOG_SYSLOG_ONLY;

    /* If we are running in the foreground or performing firewall operations,
     * all logging will go to stderr.
    */
    if(opts->foreground != 0
            || opts->fw_flush != 0
            || opts->fw_list != 0
            || opts->fw_list_all != 0)
        static_log_flag = LOG_STDERR_ONLY;

    /* If the user forces syslog using --syslog-enable, we remove the
     * LOG_WITHOUT_SYSLOG flag. It means all messages will go through syslog */
    if (opts->syslog_enable != 0)
        static_log_flag &= ~LOG_WITHOUT_SYSLOG;

    /* Parse the log facility as specified in the config struct. If, for some
     * reason, it is not, fac will already be set to LOG_DAEMON.
    */
    if(opts->config[CONF_SYSLOG_FACILITY] != NULL
      && opts->config[CONF_SYSLOG_FACILITY][0] != '\0')
    {
        if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_DAEMON"))
            syslog_fac = LOG_DAEMON;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL0"))
            syslog_fac = LOG_LOCAL0;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL1"))
            syslog_fac = LOG_LOCAL1;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL2"))
            syslog_fac = LOG_LOCAL2;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL3"))
            syslog_fac = LOG_LOCAL3;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL4"))
            syslog_fac = LOG_LOCAL4;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL5"))
            syslog_fac = LOG_LOCAL5;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL6"))
            syslog_fac = LOG_LOCAL6;
        else if(!strcasecmp(opts->config[CONF_SYSLOG_FACILITY], "LOG_LOCAL7"))
            syslog_fac = LOG_LOCAL7;
    }

    verbosity = LOG_DEFAULT_VERBOSITY + opts->verbose;
}

/* Set the log facility value.
*/
void
set_log_facility(int fac)
{
    syslog_fac = fac;
}

/* Syslog message function.  It uses default set at intialization, and also
 * takes variable args to accomodate printf-like formatting and expansion.
*/
void
log_msg(int level, char* msg, ...)
{
    va_list ap, apse;

    /* Make sure the level is in the right range */
    if ((level & LOG_VERBOSITY_MASK) > verbosity)
        return;

    va_start(ap, msg);

    level |= static_log_flag;

    /* Print msg to stderr if the level was or'ed with LOG_STDERR
    */
    if(LOG_STDERR & level)
    {
        /* Need to make a copy of our va_list so we don't screw
         * up the message going to syslog after we print it to stderr.
        */
        va_copy(apse, ap);

        vfprintf(stderr, msg, apse);
        fprintf(stderr, "\n");
        fflush(stderr);

        va_end(apse);
    }

    /* If the message has not to be printed to the syslog, we return */
    if (LOG_WITHOUT_SYSLOG & level)
    {
        va_end(ap);
        return;
    }

    /* Remove the static log flags from the level */
    level &= LOG_VERBOSITY_MASK;

    /* Send the message to syslog.
    */
    openlog(log_name, LOG_PID, syslog_fac);

    vsyslog(level, msg, ap);

    va_end(ap);
}

/**
 * Set the verbosity level for the current context of the log module.
 *
 * The verbosity levels used by the module are defined by the syslog module.
 *
 * @param level verbosity level to set (LOG_INFO, LOG_NOTICE ...)
 */
void
log_set_verbosity(int level)
{
    verbosity = level;
}

/***EOF***/
