summaryrefslogtreecommitdiffstats
path: root/template.c
diff options
context:
space:
mode:
Diffstat (limited to 'template.c')
-rw-r--r--template.c275
1 files changed, 207 insertions, 68 deletions
diff --git a/template.c b/template.c
index 6057109a..936ccd07 100644
--- a/template.c
+++ b/template.c
@@ -1,14 +1,28 @@
/* This is the template processing code of rsyslog.
* Please see syslogd.c for license information.
- * This code is placed under the GPL.
* begun 2004-11-17 rgerhards
+ *
+ * Copyright 2004, 2007 Rainer Gerhards and Adiscon
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h"
-#ifdef __FreeBSD__
-#define BSD
-#endif
-
#include "rsyslog.h"
#include <stdio.h>
#include <stdlib.h>
@@ -19,8 +33,16 @@
#include "syslogd-types.h"
#include "template.h"
#include "msg.h"
-#include "syslogd.h"
+#include "dirty.h"
+#include "obj.h"
+#include "errmsg.h"
+/* static data */
+DEFobjCurrIf(obj)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(regexp)
+
+static int bFirstRegexpErrmsg = 1; /**< did we already do a "can't load regexp" error message? */
static struct template *tplRoot = NULL; /* the root of the template list */
static struct template *tplLast = NULL; /* points to the last element of the template list */
static struct template *tplLastStatic = NULL; /* last static element of the template list */
@@ -50,7 +72,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
{
DEFiRet;
struct templateEntry *pTpe;
- rsCStrObj *pCStr;
+ cstr_t *pCStr;
unsigned short bMustBeFreed;
uchar *pVal;
size_t iLenVal;
@@ -64,10 +86,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
* free the obtained value (if requested). We continue this
* loop until we got hold of all values.
*/
- if((pCStr = rsCStrConstruct()) == NULL) {
- dbgprintf("memory shortage, tplToString failed\n");
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKiRet(rsCStrConstruct(&pCStr));
pTpe = pTpl->pEntryRoot;
while(pTpe != NULL) {
@@ -78,7 +97,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
) {
dbgprintf("error %d during tplToString()\n", iRet);
/* it does not make sense to continue now */
- rsCStrDestruct(pCStr);
+ rsCStrDestruct(&pCStr);
FINALIZE;
}
} else if(pTpe->eEntryType == FIELD) {
@@ -98,7 +117,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr, (uchar*) pVal, iLenVal)) {
dbgprintf("error %d during tplToString()\n", iRet);
/* it does not make sense to continue now */
- rsCStrDestruct(pCStr);
+ rsCStrDestruct(&pCStr);
if(bMustBeFreed)
free(pVal);
FINALIZE;
@@ -118,7 +137,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
finalize_it:
*ppSz = (iRet == RS_RET_OK) ? pVal : NULL;
- return iRet;
+ RETiRet;
}
/* Helper to doSQLEscape. This is called if doSQLEscape
@@ -169,11 +188,13 @@ static void doSQLEmergencyEscape(register uchar *p, int escapeMode)
* new parameter escapeMode is 0 - standard sql, 1 - "smart" engines
* 2005-09-22 rgerhards
*/
-void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode)
+rsRetVal
+doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode)
{
+ DEFiRet;
uchar *p;
int iLen;
- rsCStrObj *pStrB;
+ cstr_t *pStrB = NULL;
uchar *pszGenerated;
assert(pp != NULL);
@@ -191,44 +212,25 @@ void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int es
/* when we get out of the loop, we are either at the
* string terminator or the first \'. */
if(*p == '\0')
- return; /* nothing to do in this case! */
+ FINALIZE; /* nothing to do in this case! */
p = *pp;
iLen = *pLen;
- if((pStrB = rsCStrConstruct()) == NULL) {
- /* oops - no mem ... Do emergency... */
- doSQLEmergencyEscape(p, escapeMode);
- return;
- }
+ CHKiRet(rsCStrConstruct(&pStrB));
while(*p) {
if(*p == '\'') {
- if(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\') != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- rsCStrDestruct(pStrB);
- return;
- }
+ CHKiRet(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\'));
iLen++; /* reflect the extra character */
} else if((escapeMode == 1) && (*p == '\\')) {
- if(rsCStrAppendChar(pStrB, '\\') != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- rsCStrDestruct(pStrB);
- return;
- }
+ CHKiRet(rsCStrAppendChar(pStrB, '\\'));
iLen++; /* reflect the extra character */
}
- if(rsCStrAppendChar(pStrB, *p) != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- rsCStrDestruct(pStrB);
- return;
- }
+ CHKiRet(rsCStrAppendChar(pStrB, *p));
++p;
}
- rsCStrFinish(pStrB);
- if(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0) != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- return;
- }
+ CHKiRet(rsCStrFinish(pStrB));
+ CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0));
if(*pbMustBeFreed)
free(*pp); /* discard previous value */
@@ -236,6 +238,15 @@ void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int es
*pp = pszGenerated;
*pLen = iLen;
*pbMustBeFreed = 1;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ doSQLEmergencyEscape(*pp, escapeMode);
+ if(pStrB != NULL)
+ rsCStrDestruct(&pStrB);
+ }
+
+ RETiRet;
}
@@ -299,7 +310,7 @@ struct template* tplConstruct(void)
static int do_Constant(unsigned char **pp, struct template *pTpl)
{
register unsigned char *p;
- rsCStrObj *pStrB;
+ cstr_t *pStrB;
struct templateEntry *pTpe;
int i;
@@ -309,7 +320,7 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
p = *pp;
- if((pStrB = rsCStrConstruct()) == NULL)
+ if(rsCStrConstruct(&pStrB) != RS_RET_OK)
return 1;
rsCStrSetAllocIncrement(pStrB, 32);
/* process the message and expand escapes
@@ -366,7 +377,6 @@ static int do_Constant(unsigned char **pp, struct template *pTpl)
if((pTpe = tpeConstruct(pTpl)) == NULL) {
/* OK, we are out of luck. Let's invalidate the
* entry and that's it.
- * TODO: add panic message once we have a mechanism for this
*/
pTpe->eEntryType = UNDEFINED;
return 1;
@@ -430,10 +440,14 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
pTpe->data.field.eDateFormat = tplFmtRFC3164Date;
} else if(!strcmp((char*)Buf, "date-rfc3339")) {
pTpe->data.field.eDateFormat = tplFmtRFC3339Date;
+ } else if(!strcmp((char*)Buf, "date-subseconds")) {
+ pTpe->data.field.eDateFormat = tplFmtSecFrac;
} else if(!strcmp((char*)Buf, "lowercase")) {
pTpe->data.field.eCaseConv = tplCaseConvLower;
} else if(!strcmp((char*)Buf, "uppercase")) {
pTpe->data.field.eCaseConv = tplCaseConvUpper;
+ } else if(!strcmp((char*)Buf, "sp-if-no-1st-sp")) {
+ pTpe->data.field.options.bSPIffNo1stSP = 1;
} else if(!strcmp((char*)Buf, "escape-cc")) {
pTpe->data.field.options.bEscapeCC = 1;
} else if(!strcmp((char*)Buf, "drop-cc")) {
@@ -462,14 +476,15 @@ static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
static int do_Parameter(unsigned char **pp, struct template *pTpl)
{
unsigned char *p;
- rsCStrObj *pStrB;
+ cstr_t *pStrB;
struct templateEntry *pTpe;
int iNum; /* to compute numbers */
+ rsRetVal iRetLocal;
#ifdef FEATURE_REGEXP
- /* APR: variables for regex */
- int longitud;
- unsigned char *regex_char;
+ /* APR: variables for regex */
+ int longitud;
+ unsigned char *regex_char;
unsigned char *regex_end;
#endif
@@ -479,7 +494,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
p = (unsigned char*) *pp;
- if((pStrB = rsCStrConstruct()) == NULL)
+ if(rsCStrConstruct(&pStrB) != RS_RET_OK)
return 1;
if((pTpe = tpeConstruct(pTpl)) == NULL) {
@@ -490,7 +505,8 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
pTpe->eEntryType = FIELD;
while(*p && *p != '%' && *p != ':') {
- rsCStrAppendChar(pStrB, *p++);
+ rsCStrAppendChar(pStrB, tolower(*p));
+ ++p; /* do NOT do this in tolower()! */
}
/* got the name*/
@@ -502,18 +518,90 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
if(*p == ':') {
++p; /* eat ':' */
#ifdef FEATURE_REGEXP
- if (*p == 'R') {
+ if(*p == 'R') {
/* APR: R found! regex alarm ! :) */
++p; /* eat ':' */
- if (*p != ':') {
+ /* first come the regex type */
+ if(*p == ',') {
+ ++p; /* eat ',' */
+ if(p[0] == 'B' && p[1] == 'R' && p[2] == 'E' && (p[3] == ',' || p[3] == ':')) {
+ pTpe->data.field.typeRegex = TPL_REGEX_BRE;
+ p += 3; /* eat indicator sequence */
+ } else if(p[0] == 'E' && p[1] == 'R' && p[2] == 'E' && (p[3] == ',' || p[3] == ':')) {
+ pTpe->data.field.typeRegex = TPL_REGEX_ERE;
+ p += 3; /* eat indicator sequence */
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "error: invalid regular expression type, rest of line %s",
+ (char*) p);
+ }
+ }
+
+ /* now check for submatch ID */
+ pTpe->data.field.iSubMatchToUse = 0;
+ if(*p == ',') {
+ /* in this case a number follows, which indicates which match
+ * shall be used. This must be a single digit.
+ */
+ ++p; /* eat ',' */
+ if(isdigit((int) *p)) {
+ pTpe->data.field.iSubMatchToUse = *p - '0';
+ ++p; /* eat digit */
+ }
+ }
+
+ /* now pull what to do if we do not find a match */
+ if(*p == ',') {
+ ++p; /* eat ',' */
+ if(p[0] == 'D' && p[1] == 'F' && p[2] == 'L' && p[3] == 'T'
+ && (p[4] == ',' || p[4] == ':')) {
+ pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_DFLTSTR;
+ p += 4; /* eat indicator sequence */
+ } else if(p[0] == 'B' && p[1] == 'L' && p[2] == 'A' && p[3] == 'N' && p[4] == 'K'
+ && (p[5] == ',' || p[5] == ':')) {
+ pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_BLANK;
+ p += 5; /* eat indicator sequence */
+ } else if(p[0] == 'F' && p[1] == 'I' && p[2] == 'E' && p[3] == 'L' && p[4] == 'D'
+ && (p[5] == ',' || p[5] == ':')) {
+ pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_WHOLE_FIELD;
+ p += 5; /* eat indicator sequence */
+ } else if(p[0] == 'Z' && p[1] == 'E' && p[2] == 'R' && p[3] == 'O'
+ && (p[4] == ',' || p[4] == ':')) {
+ pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_ZERO;
+ p += 4; /* eat indicator sequence */
+ } else if(p[0] == ',') { /* empty, use default */
+ pTpe->data.field.nomatchAction = TPL_REGEX_NOMATCH_USE_DFLTSTR;
+ /* do NOT eat indicator sequence, as this was already eaten - the
+ * comma itself is already part of the next field.
+ */
+ } else {
+ errmsg.LogError(0, NO_ERRCODE, "error: invalid regular expression type, rest of line %s",
+ (char*) p);
+ }
+ }
+
+ /* now check for match ID */
+ pTpe->data.field.iMatchToUse = 0;
+ if(*p == ',') {
+ /* in this case a number follows, which indicates which match
+ * shall be used. This must be a single digit.
+ */
+ ++p; /* eat ',' */
+ if(isdigit((int) *p)) {
+ pTpe->data.field.iMatchToUse = *p - '0';
+ ++p; /* eat digit */
+ }
+ }
+
+ if(*p != ':') {
/* There is something more than an R , this is invalid ! */
/* Complain on extra characters */
- logerrorSz
- ("error: invalid character in frompos after \"R\", property: '%%%s'",
+ errmsg.LogError(0, NO_ERRCODE, "error: invalid character in frompos after \"R\", property: '%%%s'",
(char*) *pp);
} else {
pTpe->data.field.has_regex = 1;
+ dbgprintf("we have a regexp and use match #%d, submatch #%d\n",
+ pTpe->data.field.iMatchToUse, pTpe->data.field.iSubMatchToUse);
}
} else {
/* now we fall through the "regular" FromPos code */
@@ -534,8 +622,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
pTpe->data.field.has_fields = 1;
if(!isdigit((int)*p)) {
/* complain and use default */
- logerrorSz
- ("error: invalid character in frompos after \"F,\", property: '%%%s' - using 9 (HT) as field delimiter",
+ errmsg.LogError(0, NO_ERRCODE, "error: invalid character in frompos after \"F,\", property: '%%%s' - using 9 (HT) as field delimiter",
(char*) *pp);
pTpe->data.field.field_delim = 9;
} else {
@@ -543,8 +630,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
while(isdigit((int)*p))
iNum = iNum * 10 + *p++ - '0';
if(iNum < 0 || iNum > 255) {
- logerrorInt
- ("error: non-USASCII delimiter character value in template - using 9 (HT) as substitute", iNum);
+ errmsg.LogError(0, NO_ERRCODE, "error: non-USASCII delimiter character value %d in template - using 9 (HT) as substitute", iNum);
pTpe->data.field.field_delim = 9;
} else {
pTpe->data.field.field_delim = iNum;
@@ -554,8 +640,7 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
/* invalid character after F, so we need to reject
* this.
*/
- logerrorSz
- ("error: invalid character in frompos after \"F\", property: '%%%s'",
+ errmsg.LogError(0, NO_ERRCODE, "error: invalid character in frompos after \"F\", property: '%%%s'",
(char*) *pp);
}
} else {
@@ -596,9 +681,8 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
longitud = regex_end - p;
/* Malloc for the regex string */
regex_char = (unsigned char *) malloc(longitud + 1);
- if (regex_char == NULL) {
- dbgprintf
- ("Could not allocate memory for template parameter!\n");
+ if(regex_char == NULL) {
+ dbgprintf("Could not allocate memory for template parameter!\n");
pTpe->data.field.has_regex = 0;
return 1;
/* TODO: RGer: check if we can recover better... (probably not) */
@@ -612,8 +696,22 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
/* Now i compile the regex */
/* Remember that the re is an attribute of the Template entry */
- if(regcomp(&(pTpe->data.field.re), (char*) regex_char, 0) != 0) {
- dbgprintf("error: can not compile regex: '%s'\n", regex_char);
+ if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ int iOptions;
+ iOptions = (pTpe->data.field.typeRegex == TPL_REGEX_ERE) ? REG_EXTENDED : 0;
+ if(regexp.regcomp(&(pTpe->data.field.re), (char*) regex_char, iOptions) != 0) {
+ dbgprintf("error: can not compile regex: '%s'\n", regex_char);
+ pTpe->data.field.has_regex = 2;
+ }
+ } else {
+ /* regexp object could not be loaded */
+ dbgprintf("error %d trying to load regexp library - this may be desired and thus OK",
+ iRetLocal);
+ if(bFirstRegexpErrmsg) { /* prevent flood of messages, maybe even an endless loop! */
+ bFirstRegexpErrmsg = 0;
+ errmsg.LogError(0, NO_ERRCODE, "regexp library could not be loaded (error %d), "
+ "regexp ignored", iRetLocal);
+ }
pTpe->data.field.has_regex = 2;
}
@@ -651,7 +749,6 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl)
#endif /* #ifdef FEATURE_REGEXP */
}
- /* TODO: add more sanity checks. For now, we do the bare minimum */
if((pTpe->data.field.has_fields == 0) && (pTpe->data.field.iToPos < pTpe->data.field.iFromPos)) {
iNum = pTpe->data.field.iToPos;
pTpe->data.field.iToPos = pTpe->data.field.iFromPos;
@@ -822,6 +919,8 @@ void tplDeleteAll(void)
{
struct template *pTpl, *pTplDel;
struct templateEntry *pTpe, *pTpeDel;
+ rsRetVal iRetLocal;
+ BEGINfunc
pTpl = tplRoot;
while(pTpl != NULL) {
@@ -841,6 +940,12 @@ void tplDeleteAll(void)
free(pTpeDel->data.constant.pConstant);
break;
case FIELD:
+ /* check if we have a regexp and, if so, delete it */
+ if(pTpeDel->data.field.has_regex != 0) {
+ if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ regexp.regfree(&(pTpeDel->data.field.re));
+ }
+ }
/*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
free(pTpeDel->data.field.pPropRepl);
break;
@@ -854,15 +959,20 @@ void tplDeleteAll(void)
free(pTplDel->pszName);
free(pTplDel);
}
+ ENDfunc
}
+
/* Destroy all templates obtained from conf file
- * preserving hadcoded ones. This is called from init().
+ * preserving hardcoded ones. This is called from init().
*/
void tplDeleteNew(void)
{
struct template *pTpl, *pTplDel;
struct templateEntry *pTpe, *pTpeDel;
+ rsRetVal iRetLocal;
+
+ BEGINfunc
if(tplRoot == NULL || tplLastStatic == NULL)
return;
@@ -887,6 +997,12 @@ void tplDeleteNew(void)
free(pTpeDel->data.constant.pConstant);
break;
case FIELD:
+ /* check if we have a regexp and, if so, delete it */
+ if(pTpeDel->data.field.has_regex != 0) {
+ if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) {
+ regexp.regfree(&(pTpeDel->data.field.re));
+ }
+ }
/*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
free(pTpeDel->data.field.pPropRepl);
break;
@@ -900,6 +1016,7 @@ void tplDeleteNew(void)
free(pTplDel->pszName);
free(pTplDel);
}
+ ENDfunc
}
/* Store the pointer to the last hardcoded teplate */
@@ -974,6 +1091,15 @@ void tplPrintList(void)
if(pTpe->data.field.options.bSpaceCC) {
dbgprintf("[replace control-characters with space] ");
}
+ if(pTpe->data.field.options.bSecPathDrop) {
+ dbgprintf("[slashes are dropped] ");
+ }
+ if(pTpe->data.field.options.bSecPathReplace) {
+ dbgprintf("[slashes are replaced by '_'] ");
+ }
+ if(pTpe->data.field.options.bSPIffNo1stSP) {
+ dbgprintf("[SP iff no first SP] ");
+ }
if(pTpe->data.field.options.bDropLastLF) {
dbgprintf("[drop last LF in msg] ");
}
@@ -1000,6 +1126,19 @@ int tplGetEntryCount(struct template *pTpl)
assert(pTpl != NULL);
return(pTpl->tpenElements);
}
+
+/* our init function. TODO: remove once converted to a class
+ */
+rsRetVal templateInit()
+{
+ DEFiRet;
+ CHKiRet(objGetObjInterface(&obj));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+finalize_it:
+ RETiRet;
+}
+
/*
* vi:set ai:
*/