ossp-pkg/l2/l2_ch_prefix.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_ch_prefix.c: prefixing channel implementation
*/
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/utsname.h>
#include "l2.h"
#include "l2_p.h"
/* declare private channel configuration */
typedef struct {
char *prefix;
char *timezone;
} l2_ch_prefix_t;
/* create channel */
static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch)
{
l2_ch_prefix_t *cfg;
/* allocate private channel configuration */
if ((cfg = (l2_ch_prefix_t *)malloc(sizeof(l2_ch_prefix_t))) == NULL)
return L2_ERR_MEM;
/* initialize configuration with reasonable defaults */
cfg->prefix = NULL;
cfg->timezone = strdup("local");
/* link private channel configuration into channel context */
ctx->vp = cfg;
return L2_OK;
}
/* configure channel */
static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list ap)
{
l2_ch_prefix_t *cfg = (l2_ch_prefix_t *)ctx->vp;
l2_param_t pa[3];
l2_result_t rv;
l2_env_t *env;
/* feed and call generic parameter parsing engine */
L2_PARAM_SET(pa[0], prefix, STR, &cfg->prefix);
L2_PARAM_SET(pa[1], timezone, STR, &cfg->timezone);
L2_PARAM_END(pa[2]);
l2_channel_env(ch, &env);
rv = l2_util_setparams(env, pa, fmt, ap);
/* argument consistency check */
if (rv == L2_OK) {
if (cfg->timezone == NULL)
return L2_ERR_ARG;
if (!( strcmp(cfg->timezone, "local") == 0
|| strcmp(cfg->timezone, "utc") == 0))
return L2_ERR_ARG;
}
return rv;
}
static l2_result_t expand_specials(char *buf, size_t bufsize, l2_level_t level)
{
char *cpSC;
char *cpSE;
char *cpBE;
char caBuf[128];
size_t nBuf;
l2_level_t rv;
int bSubst;
struct utsname uts;
cpSC = buf; /* string current pointer */
cpSE = buf+strlen(buf); /* string end pointer */
cpBE = buf+bufsize; /* buffer end pointer */
while (cpSC < cpSE) {
if ((cpSC+1) < cpSE && *cpSC == '%') {
bSubst = FALSE;
switch (*(cpSC+1)) {
case 'L': {
if ((rv = l2_util_l2s(caBuf, sizeof(caBuf), '\0', level)) != L2_OK)
return rv;
bSubst = TRUE;
break;
}
case 'N': {
if (uname(&uts) != -1)
l2_util_sprintf(caBuf, sizeof(caBuf), uts.nodename);
else
l2_util_sprintf(caBuf, sizeof(caBuf), "localhost");
bSubst = TRUE;
break;
}
case 'P': {
l2_util_sprintf(caBuf, sizeof(caBuf), "%lu", (unsigned long)getpid());
bSubst = TRUE;
break;
}
}
if (bSubst) {
nBuf = strlen(caBuf);
if ((cpBE-cpSE)+2 < nBuf)
return L2_ERR_MEM;
memmove(cpSC+nBuf, cpSC+2, cpSE-(cpSC+2)+1);
memmove(cpSC, caBuf, nBuf);
cpSC += nBuf;
cpSE += (-2+nBuf);
continue;
}
}
cpSC++;
}
return L2_OK;
}
/* write to channel */
static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch,
l2_level_t level, const char *buf, size_t buf_size)
{
l2_ch_prefix_t *cfg = (l2_ch_prefix_t *)ctx->vp;
l2_channel_t *downstream;
time_t t;
struct tm *tm;
size_t n;
l2_result_t rv;
char buf1[1024];
char buf2[1024];
/* optionally provide prefix */
if (cfg->prefix != NULL) {
if (strlen(cfg->prefix) >= sizeof(buf1))
return L2_ERR_MEM;
strcpy(buf1, cfg->prefix);
if ((rv = expand_specials(buf1, sizeof(buf1), level)) != L2_OK)
return rv;
t = time(NULL);
if (strcmp(cfg->timezone, "local") == 0)
tm = localtime(&t);
else if (strcmp(cfg->timezone, "utc") == 0)
tm = gmtime(&t);
else
return L2_ERR_ARG;
if ((n = strftime(buf2, sizeof(buf2), buf1, tm)) == 0)
return L2_ERR_SYS;
n += l2_util_sprintf(buf2+n, sizeof(buf2)-n, "%s", buf);
downstream = NULL;
while ((rv = l2_channel_downstream(ch, &downstream)) == L2_OK)
if ((rv = l2_channel_write(downstream, level, buf2, n)) != L2_OK)
return rv;
return L2_OK;
}
return L2_OK_PASS;
}
/* destroy channel */
static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch)
{
l2_ch_prefix_t *cfg = (l2_ch_prefix_t *)ctx->vp;
/* free prefix structure */
if (cfg->prefix != NULL)
free(cfg->prefix);
if (cfg->timezone != NULL)
free(cfg->timezone);
free(cfg);
return L2_OK_PASS;
}
/* exported channel handler structure */
l2_handler_t l2_handler_prefix = {
"prefix",
L2_CHANNEL_FILTER,
hook_create,
hook_configure,
NULL,
hook_write,
NULL,
NULL,
hook_destroy
};