/*
**  OSSP l2 - Flexible Logging
**  Copyright (c) 2001-2003 Cable & Wireless Deutschland GmbH
**  Copyright (c) 2001-2003 The OSSP Project (http://www.ossp.org/)
**  Copyright (c) 2001-2003 Ralf S. Engelschall <rse@engelschall.com>
**
**  This file is part of OSSP l2, a flexible logging library which
**  can be found at http://www.ossp.org/pkg/lib/l2/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  l2_env.c: environment object
*/

#include "l2_p.h"

/* create environment object */
l2_result_t l2_env_create(l2_env_t **envp)
{
    l2_env_t *env;
    int i;

    /* argument sanity check */
    if (envp == NULL)
        return L2_ERR_ARG;

    /* allocate environment structure */
    if ((env = (l2_env_t *)malloc(sizeof(l2_env_t))) == NULL)
        return L2_ERR_SYS;

    /* initialize environment structure */
    env->rvErrorInfo = L2_OK;
    env->szErrorInfo[0] = '\0';
    env->szError[0] = '\0';
    env->levelmask = L2_LEVEL_ALL;
    env->flushmask = L2_LEVEL_NONE;
    env->interval  = 0;
    for (i = 0; i < L2_MAX_FORMATTERS; i++)
        env->formatters[i].cb = NULL;
    for (i = 0; i < L2_MAX_HANDLERS; i++)
        env->handlers[i] = NULL;

    /* pre-configure all handlers we ship */
    l2_env_handler(env, &l2_handler_null);
    l2_env_handler(env, &l2_handler_fd);
    l2_env_handler(env, &l2_handler_file);
    l2_env_handler(env, &l2_handler_pipe);
    l2_env_handler(env, &l2_handler_socket);
    l2_env_handler(env, &l2_handler_syslog);
    l2_env_handler(env, &l2_handler_smtp);
    l2_env_handler(env, &l2_handler_noop);
    l2_env_handler(env, &l2_handler_filter);
    l2_env_handler(env, &l2_handler_prefix);
    l2_env_handler(env, &l2_handler_buffer);

    /* pass new object to caller */
    (*envp) = env;

    return L2_OK;
}


/* destroy environment object */
l2_result_t l2_env_destroy(l2_env_t *env)
{
    /* argument sanity check */
    if (env == NULL)
        return L2_ERR_ARG;

    /* free env structure */
    free(env);

    return L2_OK;
}

/* set environment level masks */
l2_result_t l2_env_levels(l2_env_t *env, unsigned int levelmask, unsigned int flushmask)
{
    /* argument sanity check */
    if (env == NULL)
        return L2_ERR_ARG;

    /* override global level mask */
    env->levelmask = levelmask;
    env->flushmask = flushmask;

    return L2_OK;
}

/* attach formatter to environment */
l2_result_t l2_env_formatter(l2_env_t *env, char id, l2_formatter_t cb, l2_context_t *ctx)
{
    int i;

    /* argument sanity check */
    if (env == NULL || id == '\0' || cb == NULL)
        return L2_ERR_ARG;

    /* find next free formatter position in formatter array */
    for (i = 0; i < L2_MAX_FORMATTERS && env->formatters[i].cb != NULL; i++)
        ;
    if (i == L2_MAX_FORMATTERS)
        return L2_ERR_MEM;

    /* attach formatter to env */
    env->formatters[i].id  = id;
    env->formatters[i].ctx = ctx;
    env->formatters[i].cb  = cb;

    return L2_OK;
}

/* attach handler to environment */
l2_result_t l2_env_handler(l2_env_t *env, l2_handler_t *h)
{
    int i;

    /* argument sanity check */
    if (env == NULL || h == NULL)
        return L2_ERR_ARG;

    /* find next free handler position in handler array */
    for (i = 0; i < L2_MAX_HANDLERS && env->handlers[i] != NULL; i++)
        ;
    if (i == L2_MAX_HANDLERS)
        return L2_ERR_MEM;

    /* attach handler to env */
    env->handlers[i] = h;
    env->handlers[i] = h;

    return L2_OK;
}

/* remember additional error information */
l2_result_t l2_env_errorinfo(l2_env_t *env, l2_result_t rv, const char *fmt, ...)
{
    va_list ap;

    /* argument sanity check */
    if (env == NULL || rv == L2_OK || fmt == NULL)
        return L2_ERR_ARG;

    /* remember error information */
    va_start(ap, fmt);
    l2_util_vsprintf(env->szErrorInfo, sizeof(env->szErrorInfo), fmt, ap);
    env->rvErrorInfo = rv;
    va_end(ap);

    return L2_OK;
}

/* retrieve string description of error result code */
char *l2_env_strerror(l2_env_t *env, l2_result_t rv)
{
    char *sz;
    char *cpBuf;
    int nBuf;
    int n;

    /* argument sanity check */
    if (env == NULL)
        return NULL;

    /* start at begin of buffer */
    cpBuf = env->szError;
    nBuf  = sizeof(env->szError);

    /* translate result value into corresponding string */
    if      (rv == L2_OK)      sz = "everything ok";
    else if (rv == L2_OK_PASS) sz = "everything ok - pass downstream";
    else if (rv == L2_ERR_ARG) sz = "invalid argument";
    else if (rv == L2_ERR_USE) sz = "invalid use";
    else if (rv == L2_ERR_MEM) sz = "no more memory available";
    else if (rv == L2_ERR_SYS) sz = "operating system error";
    else if (rv == L2_ERR_IO)  sz = "input/output error";
    else if (rv == L2_ERR_FMT) sz = "formatting error";
    else if (rv == L2_ERR_INT) sz = "internal error";
    else if (rv == L2_ERR_SYN) sz = "syntax error";
    else if (rv == L2_ERR_CH)  sz = "no (more) channel found";
    else                       sz = "unknown error";
    n = l2_util_sprintf(cpBuf, nBuf, "%s", sz);
    cpBuf += n;
    nBuf  -= n;

    /* optionally annotate with error information */
    if (rv == env->rvErrorInfo && env->szErrorInfo[0] != '\0') {
        n = l2_util_sprintf(cpBuf, nBuf, "; %s", env->szErrorInfo);
        cpBuf += n;
        nBuf  -= n;
    }

    /* optionally annotate with operating system error information */
    if (rv == L2_ERR_SYS) {
        n = l2_util_sprintf(cpBuf, nBuf, "; %s (%d)", strerror(errno), errno);
        cpBuf += n;
        nBuf  -= n;
    }

    /* return pointer to internal buffer */
    return env->szError;
}

/* sets the virtual timer to the interval value in env */
static int set_alarm(l2_env_t *env)
{
#if defined(HAVE_SETITIMER) && defined(HAVE_SYS_TIME_H)
    struct itimerval valtest, valnew;

    /* initialize auto vars before using them */
    memset(&valnew, 0, sizeof(valnew));

    valnew.it_interval.tv_sec = (long)env->interval;
    valnew.it_interval.tv_usec = 0L;       /* no microsecond granularity */
    valnew.it_value.tv_sec = (long)env->interval;
    valnew.it_value.tv_usec = 0L;          /* no microsecond granularity */
    if ((getitimer(L2_BUFFER_TIMER, &valtest) == 0) &&
       ((valtest.it_value.tv_sec | valtest.it_value.tv_usec |
       valtest.it_interval.tv_sec | valtest.it_interval.tv_usec) == 0L))
        return (setitimer(L2_BUFFER_TIMER, &valnew, 0) ? L2_ERR_INT : L2_OK);
    else {
        env->interval = L2_BROKEN_TIMER;    /* mark this timer as broken */
        assert(FALSE);     /* throw the switch right away when debugging */
        return L2_ERR_ARG;
    }
#else
    unsigned int uiAlarmed = 0;

    assert(env->interval >= 0);          /* guard against a broken timer */
    assert(!(uiAlarmed = alarm((unsigned int)iInterval)));
    if (uiAlarmed) {             /* check if SIGALRM is occupied         */
        alarm(uiAlarmed);        /* ...if so, then hack in the old value */
        env->interval = L2_BROKEN_TIMER; /* ...mark this timer as broken */
        return L2_ERR_INT;
    }
    else
        return L2_OK;
#endif
}

/* l2_env_settimer will change or disappear with  */
/* the arrival of the multiplexed L2 timer object */
/* set the L2 timer */
l2_result_t l2_env_settimer(l2_env_t *env, int iInterval)
{
    if ((env == NULL) || (iInterval < 0))  /* argument sanity check */
        return L2_ERR_ARG;

    /* short circuit if setting again with identical interval value */
    if (env->interval == iInterval)
        return L2_OK;

                    /* one value only, no multiplexed timer support */
    if (env->interval != 0)      /* && (env->interval != iInterval) */
        return L2_ERR_ARG;

    env->interval = iInterval;
    if (set_alarm(env) != L2_OK) {
        env->interval = L2_BROKEN_TIMER;      /* L2 timer is broken */
        return L2_ERR_INT;
    }
    else
        return L2_OK;
}

