ossp-pkg/fsl/fsl.c
1.12
/*
** OSSP fsl - Faking Syslog Library
** Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
**
** This file is part of OSSP fsl, a syslog(3) API faking library which
** can be found at http://www.ossp.org/pkg/lib/fsl/.
**
** 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.
**
** fsl.c: OSSP fsl library
*/
/* standard includes we use */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
/* standard include we re-implement */
#include <syslog.h>
/* third party (linked in) */
#include "l2.h"
#include "cfg.h"
#include "pcre.h"
/* default for the dedicated logfile */
#ifndef LOGFILE
#define LOGFILE "/tmp/syslog"
#endif
#ifndef LOG_PRIMASK
#define LOG_PRIMASK (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG)
#endif
#ifndef LOG_PRI
#define LOG_PRI(p) ((p) & LOG_PRIMASK)
#endif
#include "config.h"
#define FSL_PREFIX "l2."
#define STMT(stuff) do { stuff } while (0)
#define CU(returncode) STMT( rc = returncode; goto CUS; )
#define VCU STMT( goto CUS; )
typedef struct {
char *base;
size_t used;
size_t size;
} buf_t;
/* general return codes */
typedef enum {
FSL_OK = 0, /* everything ok */
FSL_ERR_ARG, /* invalid argument */
FSL_ERR_USE, /* invalid use */
FSL_ERR_MEM, /* no more memory available */
FSL_ERR_SYS /* operating system error, see errno */
//FSL_ERR_FMT, /* formatting error */
//FSL_ERR_INT, /* internal error */
//FSL_ERR_SYN, /* syntax error */
} fsl_rc_t;
fsl_rc_t readfileorallfiles(buf_t *, const char *);
fsl_rc_t readfile (buf_t *, const char *);
fsl_rc_t readallfiles (buf_t *);
fsl_rc_t appendfiletobuffer(buf_t *, const char *);
static struct {
int facility;
char *string;
} syslogfacility2string[] = {
{ LOG_AUTH, "auth" },
{ LOG_AUTHPRIV, "authpriv" },
{ LOG_CONSOLE, "console" },
{ LOG_CRON, "cron" },
{ LOG_DAEMON, "daemon" },
{ LOG_FTP, "ftp" },
{ LOG_KERN, "kern" },
{ LOG_LPR, "lpr" },
{ LOG_MAIL, "mail" },
{ LOG_NEWS, "news" },
{ LOG_SECURITY, "security" },
{ LOG_SYSLOG, "syslog" },
{ LOG_USER, "user" },
{ LOG_UUCP, "uucp" },
{ LOG_LOCAL0, "local0" },
{ LOG_LOCAL1, "local1" },
{ LOG_LOCAL2, "local2" },
{ LOG_LOCAL3, "local3" },
{ LOG_LOCAL4, "local4" },
{ LOG_LOCAL5, "local5" },
{ LOG_LOCAL6, "local6" },
{ LOG_LOCAL7, "local7" },
{ 0, NULL }
};
static struct {
int level;
char *string;
l2_level_t deflevelmap;
} sysloglevel2string[] = {
{ LOG_EMERG, "emerg", L2_LEVEL_PANIC },
{ LOG_ALERT, "alert", L2_LEVEL_PANIC },
{ LOG_CRIT, "crit", L2_LEVEL_CRITICAL },
{ LOG_ERR, "err", L2_LEVEL_ERROR },
{ LOG_WARNING, "warning", L2_LEVEL_WARNING },
{ LOG_NOTICE, "notice", L2_LEVEL_NOTICE },
{ LOG_INFO, "info", L2_LEVEL_INFO },
{ LOG_DEBUG, "debug", L2_LEVEL_DEBUG },
{ 0, NULL, 0 }
};
typedef struct {
int syslog;
l2_level_t l2;
} levelmap_t;
/* internal context structure */
static struct {
l2_env_t *l2_env;
l2_channel_t *l2_nch;
levelmap_t *levelmap;
int maskpri;
} ctx = {
NULL,
NULL,
NULL,
LOG_PRIMASK
};
void traverse(cfg_t *cfg, cfg_node_t *cfgnode)
{
int rc;
cfg_node_t *cfgchld;
cfg_rc_t cfgrv;
cfg_node_type_t cfgtyp;
char *cp;
int cfgchilds;
/*fprintf(stderr, "DEBUG: diving\n");*/
while (cfgnode != NULL) {
if ((cfgrv = cfg_node_get(cfg, cfgnode, CFG_NODE_ATTR_TOKEN, &cp)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TOKEN) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgnode, CFG_NODE_ATTR_TYPE, &cfgtyp)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TYPE) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgnode, CFG_NODE_ATTR_CHILDS, &cfgchilds)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_CHILDS) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
/*fprintf(stderr, "DEBUG: cfgnode=0x%.8lx[%d], *cp=\"%s\", type=%d\n", (unsigned long)cfgnode, cfgchilds, cp, cfgtyp);*/
if ((cfgrv = cfg_node_get(cfg, cfgnode, CFG_NODE_ATTR_CHILD1, &cfgchld)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_CHILD1) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgchld != NULL)
traverse(cfg, cfgchld);
if ((cfgrv = cfg_node_get(cfg, cfgnode, CFG_NODE_ATTR_RBROTH, &cfgnode)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_RBROTH) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
}
/*fprintf(stderr, "DEBUG: climbing\n");*/
CU(0);
CUS:
return;
}
/* Substitutes $[0-9] in inbuf[inlen] with captured strings passed through capary[nary].
* Returns the number of characters (to be) written to output. Output can be suppressed
* by passing a NULL outpbuf. The terminating NUL is not counted but will be written!
*/
int substcapture(const char *inbuf, int inlen, const char **capary, int nary, char *outbuf)
{
int i; /* input cursor, current position */
int n; /* outbuf cursor, next possible write position */
int m; /* index to capary */
char c; /* scratch variable */
i = 0;
n = 0;
while (i < inlen - 1 /* last char cannot start a two-character sequence, skipping it allows safe look ahead */) {
c = inbuf[i];
if (c == '$') {
c = inbuf[i+1];
if (c == '$') {
if (outbuf != NULL)
outbuf[n] = '$';
i+=2;
n++;
}
else if ((c >= '0') && (c <= '9')) {
m = c - '0';
if (outbuf != NULL) {
outbuf[n] = '\0';
strcat(&outbuf[n], ((m < nary) && (capary[m] != NULL)) ? capary[m] : "");
}
i+=2;
n+= ((m < nary) && (capary[m] != NULL)) ? strlen(capary[m]) : 0;
}
else {
if (outbuf != NULL)
outbuf[n] = '$';
i++;
n++;
if (outbuf != NULL)
outbuf[n] = c;
i++;
n++;
}
}
else {
if (outbuf != NULL)
outbuf[n] = c;
i++;
n++;
}
}
if (outbuf != NULL)
outbuf[n] = '\0';
return n;
}
void openlog(const char *ident, int logopt, int facility)
{
int rc;
buf_t buf;
fsl_rc_t rv;
cfg_t *cfg;
cfg_rc_t cfgrv;
cfg_node_t *cfgseq;
cfg_node_t *cfgdir;
cfg_node_t *cfgarg;
cfg_node_type_t cfgtype;
int cfgnumc;
int cfgnume;
char *cfgargtoka[3]; //FIXME hardcoded maximum number of arguments
char *cfgargtok;
char *argident;
char *argmatch;
char *argl2spec;
char *cp; /* scratch variable */
int i; /* scratch variable */
int n; /* scratch variable */
char *cpIdent;
char *cpFacility;
char *cpISF;
#define OVECSIZE 30 //FIXME find a good place for this
int ovec[OVECSIZE];
const char **acpMatch;
l2_channel_t *ch; /* scratch variable */
l2_result_t l2rv;
cfg = NULL;
buf.base = NULL;
buf.used = 0;
buf.size = 0;
cpIdent = NULL;
cpISF = NULL;
/*FIXME this should be available to the user to help him finding out what the application passes along
* fprintf(stderr, "DEBUG: BEGIN ident=\"%s\", logopt=0x%.8lx, facility=0x%.8lx\n", ident, (unsigned long)logopt, (unsigned long)facility);
*/
/* check for repeated execution */
if (ctx.l2_nch != NULL) {
l2_channel_destroy(ctx.l2_nch);
ctx.l2_nch = NULL;
}
if (ctx.l2_env != NULL) {
l2_env_destroy(ctx.l2_env);
ctx.l2_env = NULL;
}
if (ctx.levelmap != NULL) {
free(ctx.levelmap);
ctx.levelmap = NULL;
}
/*FIXME this should be available to the user to help him finding out what the application passes along
* currently we completely ignore logopt LOG_CONS, LOG_NDELAY, LOG_PERROR, LOG_PID
*/
/* create default sysloglevel to l2_level mapping */
for (i = 0; sysloglevel2string[i].string != NULL; i++);
if ((ctx.levelmap = (levelmap_t *)malloc(i * sizeof(levelmap_t))) == NULL) {
fprintf(stderr, "Error: malloc() failed\n"); CU(1); }
for (i = 0; sysloglevel2string[i].string != NULL; i++) {
ctx.levelmap[i].syslog = sysloglevel2string[i].level;
ctx.levelmap[i].l2 = sysloglevel2string[i].deflevelmap;
/*fprintf(stderr, "DEBUG: ctx.levelmap[%d].syslog = 0x%.8lx, ctx.levelmap[%d].l2 = 0x%.8lx\n", i, (unsigned long)ctx.levelmap[i].syslog, i, (unsigned long)ctx.levelmap[i].l2);*/
}
/* create L2 environment */
if ((l2rv = l2_env_create(&ctx.l2_env)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: failed to create L2 environment (%d)\n", l2rv); CU(1); }
if ((l2rv = l2_env_levels(ctx.l2_env, L2_LEVEL_ALL, L2_LEVEL_NONE)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to set global logging level defaults %s(%d)\n", cp, l2rv); CU(1); }
if ((l2rv = l2_env_formatter(ctx.l2_env, 'D', l2_util_fmt_dump, NULL)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to register dump formatter %s(%d)\n", cp, l2rv); CU(1); }
if ((l2rv = l2_env_formatter(ctx.l2_env, 'S', l2_util_fmt_string, NULL)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to register string formatter %s(%d)\n", cp, l2rv); CU(1); }
if ((l2rv = l2_env_formatter(ctx.l2_env, 'm', l2_util_fmt_errno, NULL)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to register errno formatter %s(%d)\n", cp, l2rv); CU(1); }
if ((l2rv = l2_env_settimer(ctx.l2_env, 20)) != L2_OK) { //FIXME do we need this?
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: failed to configure timer %s(%d)\n", cp, l2rv); CU(1); }
/* create L2 noop channel */
if ((l2rv = l2_channel_create(&ctx.l2_nch, ctx.l2_env, "noop")) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to create noop channel; %s(%d)\n", cp, l2rv); CU(1); }
/* create IdentSlashFacility */
if ((cpIdent = strdup((ident != NULL) ? ident : "unknown")) == NULL) {
fprintf(stderr, "Error: strdup() failed\n"); CU(1); }
cpFacility = "unknown";
for (i = 0; syslogfacility2string[i].string != NULL; i++) {
if (facility == syslogfacility2string[i].facility) {
cpFacility = syslogfacility2string[i].string;
break;
}
}
if ((cpISF = (char *)malloc(strlen(cpIdent) + 1 + strlen(cpFacility) + 1)) == NULL) {
fprintf(stderr, "Error: malloc() failed\n"); CU(1); }
cpISF[0] = '\0';
strcat(cpISF, cpIdent);
strcat(cpISF, "/");
strcat(cpISF, cpFacility);
if ((rv = readfileorallfiles(&buf, ident)) != FSL_OK) {
fprintf(stderr, "DEBUG: error %d, system %s(%d)\n", rv, strerror(errno), errno); CU(1); }
if ((cfgrv = cfg_create(&cfg)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_create() failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if ((cfgrv = cfg_import(cfg, NULL, CFG_FMT_CFG, buf.base, buf.size)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_import() failed with error %s (%d)\n", cp, cfgrv); CU(1); }
/* find root and check if it is a sequence and has one or more directives below it */
if ((cfgrv = cfg_node_root(cfg, &cfgseq)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_root() failed with error %s (%d)\n", cp, cfgrv); CU(1); }
//FIXME traverse(cfg, cfgseq); /* DEBUG */
if ((cfgrv = cfg_node_get(cfg, cfgseq, CFG_NODE_ATTR_TYPE, &cfgtype)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TYPE) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgtype != CFG_NODE_TYPE_SEQ) {
fprintf(stderr, "Error: expected sequence\n"); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgseq, CFG_NODE_ATTR_CHILDS, &cfgnumc)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_CHILDS) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgnumc < 1) {
fprintf(stderr, "Error: sequence is missing directives, expected 1, got %d\n", cfgnumc); CU(1); }
/* get first directive below sequence */
if ((cfgrv = cfg_node_get(cfg, cfgseq, CFG_NODE_ATTR_CHILD1, &cfgdir)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_CHILD1) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
while (cfgdir != NULL) {
//FIXME fprintf(stderr, "DEBUG: cfgdir=0x%.8lx\n", (unsigned long)cfgdir);
/* check if operating on a directive */
if ((cfgrv = cfg_node_get(cfg, cfgdir, CFG_NODE_ATTR_TYPE, &cfgtype)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TYPE) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgtype != CFG_NODE_TYPE_DIR) {
fprintf(stderr, "Error: expected directive\n"); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgdir, CFG_NODE_ATTR_CHILDS, &cfgnumc)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_CHILDS) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
/* process first child of directive, check if it is an argument and has a valid token attached */
if ((cfgrv = cfg_node_get(cfg, cfgdir, CFG_NODE_ATTR_CHILD1, &cfgarg)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_CHILD1) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgarg == NULL) {
fprintf(stderr, "Error: first argument is NULL\n"); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgarg, CFG_NODE_ATTR_TYPE, &cfgtype)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TYPE) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgtype != CFG_NODE_TYPE_ARG) {
fprintf(stderr, "Error: expected first argument, got %d\n", cfgtype); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgarg, CFG_NODE_ATTR_TOKEN, &cfgargtok)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TOKEN) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgargtok == NULL) {
fprintf(stderr, "Error: first argument has NULL data\n"); CU(1); }
cfgargtoka[0] = cfgargtok;
cfgnume = 0;
if (strcmp(cfgargtoka[0], "ident") == 0)
cfgnume = 3;
if (strcmp(cfgargtoka[0], "default") == 0)
cfgnume = 3;
if (strcmp(cfgargtoka[0], "map") == 0)
cfgnume = 3;
if (cfgnume == 0) {
fprintf(stderr, "Error: syntax error, invalid argument \"%s\"\n", cfgargtoka[0]); CU(1); }
if (cfgnume != cfgnumc) {
fprintf(stderr, "Error: directive missing arguments, expected %d, got %d\n", cfgnume, cfgnumc); CU(1); }
for (i = 1; i < cfgnume; i++) {
/* process right brother of argument, check if it is an argument and has a valid token attached */
if ((cfgrv = cfg_node_get(cfg, cfgarg, CFG_NODE_ATTR_RBROTH, &cfgarg)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_RBROTH) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgarg == NULL) {
fprintf(stderr, "Error: argument %d is NULL\n", i); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgarg, CFG_NODE_ATTR_TYPE, &cfgtype)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TYPE) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgtype != CFG_NODE_TYPE_ARG) {
fprintf(stderr, "Error: expected argument %d\n", i); CU(1); }
if ((cfgrv = cfg_node_get(cfg, cfgarg, CFG_NODE_ATTR_TOKEN, &cfgargtok)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_TOKEN) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
if (cfgargtok == NULL) {
fprintf(stderr, "Error: argument %d has NULL data\n", i); CU(1); }
cfgargtoka[i] = cfgargtok;
}
if ((strcmp(cfgargtoka[0], "ident") == 0) || (strcmp(cfgargtoka[0], "default") == 0)) { //FIXME currently default and ident are identical
argident = cfgargtoka[0];
argmatch = cfgargtoka[1];
argl2spec = cfgargtoka[2];
/* process the directive using the three arguments found */
/*fprintf(stderr, "DEBUG: argident=%s, argmatch=%s, argl2spec=%s\n", argident, argmatch, argl2spec);*/
{
pcre *pcreRegex;
pcre_extra *pcreExtra;
const char *cpError;
int iError;
int nMatch;
/* compile regular expression into finite state machine and optimize */
if ((pcreRegex = pcre_compile(argmatch, PCRE_CASELESS, &cpError, &iError, NULL)) == NULL) {
fprintf(stderr, "Error: pcre_compile() failed with error %s (%d)\n", cpError, iError); CU(1); }
pcreExtra = pcre_study(pcreRegex, 0, &cpError);
if (cpError != NULL) {
fprintf(stderr, "Error: pcre_study() failed with error %s\n", cpError); CU(1); }
nMatch = pcre_exec(pcreRegex, pcreExtra, cpISF, strlen(cpISF), 0, 0, ovec, OVECSIZE);
/*fprintf(stderr, "DEBUG: nMatch=%d when \"%s\" is used against \"%s\"\n", nMatch, argmatch, cpISF);*/
if (nMatch >= 1) {
pcre_get_substring_list(cpISF, ovec, nMatch, &acpMatch);
/*
if (acpMatch != NULL)
for (i = 0; i < nMatch; i++)
fprintf(stderr, "DEBUG: regex reference[%d]=\'%s\'\n", i, acpMatch[i] == NULL ? "(UNDEFINED)" : acpMatch[i]);
*/
n = substcapture(argl2spec, strlen(argl2spec), acpMatch, nMatch, NULL);
if ((cp = (char *)malloc(n + 1)) == NULL) {
fprintf(stderr, "Error: malloc() failed\n"); CU(1); }
if (substcapture(argl2spec, strlen(argl2spec), acpMatch, nMatch, cp) != n) {
fprintf(stderr, "Error: substcapture() failed\n"); CU(1); }
argl2spec = cp;
/*fprintf(stderr, "DEBUG: argident=%s, argmatch=%s, argl2spec=%s\n", argident, argmatch, argl2spec);*/
/* create L2 channel throuh spec and link into root channel */
if ((l2rv = l2_spec(&ch, ctx.l2_env, argl2spec)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to create stream from spec %s(%d)\n", cp, l2rv); CU(1); }
if ((l2rv = l2_channel_link(ctx.l2_nch, L2_LINK_CHILD, ch, NULL)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to link child channel %s(%d)\n", cp, l2rv); CU(1); }
free(argl2spec);
}
}
}
else if (strcmp(cfgargtoka[0], "map") == 0) {
unsigned int levelmask;
for (i = 0; sysloglevel2string[i].string != NULL; i++) {
if (strcmp(sysloglevel2string[i].string, cfgargtoka[1]) == 0) {
if ((l2rv = l2_util_s2l(cfgargtoka[2], strlen(cfgargtoka[2]), ',', &levelmask)) != L2_OK) {;
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: illegal l2 mask \"%s\" %s(%d)\n", cfgargtoka[2], cp, l2rv); CU(1); }
ctx.levelmap[i].l2 = levelmask;
/*fprintf(stderr, "DEBUG: ctx.levelmap[%d].syslog = 0x%.8lx, ctx.levelmap[%d].l2 = 0x%.8lx\n", i, (unsigned long)ctx.levelmap[i].syslog, i, (unsigned long)ctx.levelmap[i].l2);*/
break;
}
}
if (sysloglevel2string[i].string == NULL) {
fprintf(stderr, "Error: trying to map unknown syslog level \"%s\"\n", cfgargtoka[1]); CU(1); }
}
else {
fprintf(stderr, "Error: internal, argument \"%s\" not implemented\n", cfgargtoka[0]); CU(1); }
/* get right brother of current directive */
if ((cfgrv = cfg_node_get(cfg, cfgdir, CFG_NODE_ATTR_RBROTH, &cfgdir)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_node_get(CFG_NODE_ATTR_RBROTH) failed with error %s (%d)\n", cp, cfgrv); CU(1); }
}
if ((l2rv = l2_channel_open(ctx.l2_nch)) != L2_OK) {
cp = l2_env_strerror(ctx.l2_env, l2rv); fprintf(stderr, "Error: logging failed to open channel stream %s(%d)\n", cp, l2rv); CU(1); }
ctx.maskpri = LOG_UPTO(LOG_DEBUG);
CU(0);
CUS:
if (cpISF != NULL)
free(cpISF);
if (cpIdent != NULL)
free(cpIdent);
#if 0
if (cfg != NULL)
if ((cfgrv = cfg_destroy(cfg)) != CFG_OK) {
(void)cfg_error(cfg, cfgrv, &cp); fprintf(stderr, "Error: cfg_destroy() failed with error %s (%d)\n", cp, cfgrv); CU(1); }
#endif
if (buf.base != NULL)
free(buf.base);
if (rc != 0)
exit(rc);
/*fprintf(stderr, "DEBUG: ------------------------------------------------------------\n");*/
return;
}
void closelog(void)
{
if (ctx.l2_nch != NULL) {
l2_channel_destroy(ctx.l2_nch);
ctx.l2_nch = NULL;
}
if (ctx.l2_env != NULL) {
l2_env_destroy(ctx.l2_env);
ctx.l2_env = NULL;
}
return;
}
int setlogmask(int maskpri)
{
int oldmask;
//FIXME this is currently a no-op. Should we care about maskpri, set l2 global mask or continue to ignore?
/* remember the logging mask */
oldmask = ctx.maskpri;
if (maskpri != 0)
ctx.maskpri = maskpri;
return oldmask;
}
void syslog(int priority, const char *message, ...)
{
va_list args;
/* wrap around vsyslog(3) */
va_start(args, message);
vsyslog(priority, message, args);
va_end(args);
return;
}
#ifdef HAVE_VSYSLOG_USVALIST
void vsyslog(int priority, const char *fmt, __va_list args)
#else
void vsyslog(int priority, const char *fmt, va_list args)
#endif
{
unsigned int levelmask;
int i;
if (ctx.l2_nch == NULL)
return;
priority &= LOG_PRIMASK; /* strip off facility */
levelmask = 0;
for (i = 0; sysloglevel2string[i].string != NULL; i++) {
/*fprintf(stderr, "DEBUG: ctx.levelmap[%d].syslog = 0x%.8lx, ctx.levelmap[%d].l2 = 0x%.8lx\n", i, (unsigned long)ctx.levelmap[i].syslog, i, (unsigned long)ctx.levelmap[i].l2);*/
if (ctx.levelmap[i].syslog == priority)
levelmask |= ctx.levelmap[i].l2;
}
/*fprintf(stderr, "DEBUG: prioriy=0x%.8lx > levelmask=0x%.8lx\n", (unsigned long)priority, (unsigned long)levelmask);*/
l2_channel_vlog(ctx.l2_nch, levelmask, fmt, args);
return;
}
fsl_rc_t readfileorallfiles(buf_t *buffer, const char *ident)
{
fsl_rc_t rv;
if ((rv = readfile(buffer, ident)) == FSL_OK)
return FSL_OK;
if (rv != FSL_ERR_SYS || errno != ENOENT)
return rv;
if ((rv = readallfiles(buffer)) != FSL_OK)
return rv;
return FSL_OK;
}
fsl_rc_t readfile(buf_t *buffer, const char *ident)
{
fsl_rc_t rc;
char *filename = NULL;
if (ident == NULL || buffer == NULL)
CU(FSL_ERR_ARG);
if ((filename = (char *)malloc(strlen(FSL_CFGDIR) + 1 + strlen(FSL_PREFIX) + strlen(ident) + 1)) == NULL)
CU(FSL_ERR_MEM);
filename[0] = '\0';
strcat(filename, FSL_CFGDIR);
strcat(filename, "/");
strcat(filename, FSL_PREFIX);
strcat(filename, ident);
CU(appendfiletobuffer(buffer, filename));
CUS:
if (filename != NULL)
free(filename);
return rc;
}
fsl_rc_t readallfiles(buf_t *buffer)
{
fsl_rc_t rc;
DIR *dp;
struct dirent *de;
char *filename = NULL;
if (buffer == NULL)
CU(FSL_ERR_ARG);
if ((dp = opendir(FSL_CFGDIR)) == NULL)
CU(FSL_ERR_SYS);
while ((de = readdir(dp)) != NULL) {
if ( (de->d_type == DT_REG)
&& (de->d_namlen >= strlen(FSL_PREFIX))
&& (strncmp(de->d_name, FSL_PREFIX, strlen(FSL_PREFIX)) == 0)
) {
if ((filename = (char *)malloc(strlen(FSL_CFGDIR) + 1 + de->d_namlen + 1)) == NULL)
CU(FSL_ERR_MEM);
filename[0] = '\0';
strcat(filename, FSL_CFGDIR);
strcat(filename, "/");
strncat(filename, de->d_name, de->d_namlen);
(void /*FIXME*/)appendfiletobuffer(buffer, filename);
free(filename);
filename = NULL;
}
}
(void)closedir(dp);
CU(FSL_OK);
CUS:
if (filename != NULL)
free(filename);
return rc;
}
fsl_rc_t appendfiletobuffer(buf_t *buffer, const char *filename)
{
fsl_rc_t rc;
int fd = -1;
int filesize;
int fileread;
int bufferneed;
if (filename == NULL || buffer == NULL)
CU(FSL_ERR_ARG);
/*fprintf(stderr, "DEBUG: appendfiletobuffer(..., %s)\n", filename);*/
if ((fd = open(filename, O_RDONLY)) == -1)
CU(FSL_ERR_SYS);
if ((filesize = (int)lseek(fd, 0, SEEK_END)) == -1)
CU(FSL_ERR_SYS);
bufferneed = buffer->used + filesize;
if (bufferneed > buffer->size) {
if (buffer->base == NULL) {
if ((buffer->base = (char *)malloc(bufferneed)) == NULL)
CU(FSL_ERR_MEM);
buffer->size = bufferneed;
buffer->used = 0;
}
else {
if ((buffer->base = (char *)realloc(buffer->base, bufferneed)) == NULL)
CU(FSL_ERR_MEM);
buffer->size = bufferneed;
}
}
if (lseek(fd, 0, SEEK_SET) == -1)
CU(FSL_ERR_SYS);
if ((fileread = (int)read(fd, buffer->base + buffer->used, (size_t)filesize)) == -1)
CU(FSL_ERR_SYS);
if (fileread != filesize)
CU(FSL_ERR_USE);
buffer->used += filesize;
CU(FSL_OK);
CUS:
if (fd != -1)
close(fd);
return rc;
}