ossp-pkg/l2/l2_env.c
/*
** OSSP l2 - Flexible Logging
** Copyright (c) 2001-2005 Cable & Wireless <http://www.cw.com/>
** Copyright (c) 2001-2005 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2001-2005 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;
}