summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--msg.c507
-rw-r--r--msg.h3
-rw-r--r--omfile.c437
-rw-r--r--omfile.h35
-rw-r--r--omfwd.c1
-rw-r--r--omshell.c2
-rw-r--r--omshell.h2
-rw-r--r--syslogd.c1098
-rw-r--r--syslogd.h9
-rw-r--r--template.c211
-rw-r--r--template.h5
12 files changed, 1219 insertions, 1093 deletions
diff --git a/Makefile.am b/Makefile.am
index d098191b..1bb3ca0a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,7 @@ rfc3195d_SOURCES=rfc3195d.c rsyslog.h
man_MANS = rfc3195d.8 rklogd.8 rsyslogd.8 rsyslog.conf.5
-rsyslogd_SOURCES=syslogd.c pidfile.c template.c outchannel.c stringbuf.c srUtils.c parse.c syslogd-types.h template.h outchannel.h syslogd.h stringbuf.h parse.h srUtils.h liblogging-stub.h net.c net.h msg.c msg.h omshell.c omshell.h omusrmsg.c omusrmsg.h ommysql.c ommysql.h omfwd.c omfwd.h tcpsyslog.c tcpsyslog.h
+rsyslogd_SOURCES=syslogd.c pidfile.c template.c outchannel.c stringbuf.c srUtils.c parse.c syslogd-types.h template.h outchannel.h syslogd.h stringbuf.h parse.h srUtils.h liblogging-stub.h net.c net.h msg.c msg.h omshell.c omshell.h omusrmsg.c omusrmsg.h ommysql.c ommysql.h omfwd.c omfwd.h tcpsyslog.c tcpsyslog.h omfile.h omfile.c
rsyslogd_CPPFLAGS=$(mysql_includes)
rsyslogd_LDADD=$(mysql_libs) $(zlib_libs) $(pthreads_libs)
diff --git a/msg.c b/msg.c
index 8a02b02e..733514e4 100644
--- a/msg.c
+++ b/msg.c
@@ -37,6 +37,7 @@
#include "rsyslog.h"
#include "syslogd.h"
#include "srUtils.h"
+#include "template.h"
#include "msg.h"
/* The following functions will support advanced output module
@@ -1131,6 +1132,512 @@ void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg)
}
+/* Decode a priority into textual information like auth.emerg.
+ * The variable pRes must point to a user-supplied buffer and
+ * pResLen must contain its size. The pointer to the buffer
+ * is also returned, what makes this functiona suitable for
+ * use in printf-like functions.
+ * Note: a buffer size of 20 characters is always sufficient.
+ * Interface to this function changed 2007-06-15 by RGerhards
+ */
+char *textpri(char *pRes, size_t pResLen, int pri)
+{
+ syslogCODE *c_pri, *c_fac;
+
+ assert(pRes != NULL);
+ assert(pResLen > 0);
+
+ for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++);
+ for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
+
+ snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri);
+
+ return pRes;
+}
+
+
+/* This function returns the current date in different
+ * variants. It is used to construct the $NOW series of
+ * system properties. The returned buffer must be freed
+ * by the caller when no longer needed. If the function
+ * can not allocate memory, it returns a NULL pointer.
+ * Added 2007-07-10 rgerhards
+ */
+typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_MINUTE } eNOWType;
+#define tmpBUFSIZE 16 /* size of formatting buffer */
+static uchar *getNOW(eNOWType eNow)
+{
+ uchar *pBuf;
+ struct syslogTime t;
+
+ if((pBuf = (uchar*) malloc(sizeof(uchar) * tmpBUFSIZE)) == NULL) {
+ glblHadMemShortage = 1;
+ return NULL;
+ }
+
+ getCurrTime(&t);
+ switch(eNow) {
+ case NOW_NOW:
+ snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day);
+ break;
+ case NOW_YEAR:
+ snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d", t.year);
+ break;
+ case NOW_MONTH:
+ snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.month);
+ break;
+ case NOW_DAY:
+ snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.day);
+ break;
+ case NOW_HOUR:
+ snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.hour);
+ break;
+ case NOW_MINUTE:
+ snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute);
+ break;
+ }
+
+ return(pBuf);
+}
+#undef tmpBUFSIZE /* clean up */
+
+
+/* This function returns a string-representation of the
+ * requested message property. This is a generic function used
+ * to abstract properties so that these can be easier
+ * queried. Returns NULL if property could not be found.
+ * Actually, this function is a big if..elseif. What it does
+ * is simply to map property names (from MonitorWare) to the
+ * message object data fields.
+ *
+ * In case we need string forms of propertis we do not
+ * yet have in string form, we do a memory allocation that
+ * is sufficiently large (in all cases). Once the string
+ * form has been obtained, it is saved until the Msg object
+ * is finally destroyed. This is so that we save the processing
+ * time in the (likely) case that this property is requested
+ * again. It also saves us a lot of dynamic memory management
+ * issues in the upper layers, because we so can guarantee that
+ * the buffer will remain static AND available during the lifetime
+ * of the object. Please note that both the max size allocation as
+ * well as keeping things in memory might like look like a
+ * waste of memory (some might say it actually is...) - we
+ * deliberately accept this because performance is more important
+ * to us ;)
+ * rgerhards 2004-11-18
+ * Parameter "bMustBeFreed" is set by this function. It tells the
+ * caller whether or not the string returned must be freed by the
+ * caller itself. It is is 0, the caller MUST NOT free it. If it is
+ * 1, the caller MUST free 1. Handling this wrongly leads to either
+ * a memory leak of a program abort (do to double-frees or frees on
+ * the constant memory pool). So be careful to do it right.
+ * rgerhards 2004-11-23
+ * regular expression support contributed by Andres Riancho merged
+ * on 2005-09-13
+ * changed so that it now an be called without a template entry (NULL).
+ * In this case, only the (unmodified) property is returned. This will
+ * be used in selector line processing.
+ * rgerhards 2005-09-15
+ */
+char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
+ rsCStrObj *pCSPropName, unsigned short *pbMustBeFreed)
+{
+ char *pName;
+ char *pRes; /* result pointer */
+ char *pBufStart;
+ char *pBuf;
+ int iLen;
+
+#ifdef FEATURE_REGEXP
+ /* Variables necessary for regular expression matching */
+ size_t nmatch = 2;
+ regmatch_t pmatch[2];
+#endif
+
+ assert(pMsg != NULL);
+ assert(pbMustBeFreed != NULL);
+
+ if(pCSPropName == NULL) {
+ assert(pTpe != NULL);
+ pName = pTpe->data.field.pPropRepl;
+ } else {
+ pName = (char*) rsCStrGetSzStr(pCSPropName);
+ }
+ *pbMustBeFreed = 0;
+
+ /* sometimes there are aliases to the original MonitoWare
+ * property names. These come after || in the ifs below. */
+ if(!strcmp(pName, "msg")) {
+ pRes = getMSG(pMsg);
+ } else if(!strcmp(pName, "rawmsg")) {
+ pRes = getRawMsg(pMsg);
+ } else if(!strcmp(pName, "UxTradMsg")) {
+ pRes = getUxTradMsg(pMsg);
+ } else if(!strcmp(pName, "FROMHOST")) {
+ pRes = getRcvFrom(pMsg);
+ } else if(!strcmp(pName, "source")
+ || !strcmp(pName, "HOSTNAME")) {
+ pRes = getHOSTNAME(pMsg);
+ } else if(!strcmp(pName, "syslogtag")) {
+ pRes = getTAG(pMsg);
+ } else if(!strcmp(pName, "PRI")) {
+ pRes = getPRI(pMsg);
+ } else if(!strcmp(pName, "PRI-text")) {
+ pBuf = malloc(20 * sizeof(char));
+ if(pBuf == NULL) {
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY**";
+ } else {
+ *pbMustBeFreed = 1;
+ pRes = textpri(pBuf, 20, getPRIi(pMsg));
+ }
+ } else if(!strcmp(pName, "iut")) {
+ pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */
+ } else if(!strcmp(pName, "syslogfacility")) {
+ pRes = getFacility(pMsg);
+ } else if(!strcmp(pName, "syslogfacility-text")) {
+ pRes = getFacilityStr(pMsg);
+ } else if(!strcmp(pName, "syslogseverity") || !strcmp(pName, "syslogpriority")) {
+ pRes = getSeverity(pMsg);
+ } else if(!strcmp(pName, "syslogseverity-text") || !strcmp(pName, "syslogpriority-text")) {
+ pRes = getSeverityStr(pMsg);
+ } else if(!strcmp(pName, "timegenerated")) {
+ pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
+ } else if(!strcmp(pName, "timereported")
+ || !strcmp(pName, "TIMESTAMP")) {
+ pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat);
+ } else if(!strcmp(pName, "programname")) {
+ pRes = getProgramName(pMsg);
+ } else if(!strcmp(pName, "PROTOCOL-VERSION")) {
+ pRes = getProtocolVersionString(pMsg);
+ } else if(!strcmp(pName, "STRUCTURED-DATA")) {
+ pRes = getStructuredData(pMsg);
+ } else if(!strcmp(pName, "APP-NAME")) {
+ pRes = getAPPNAME(pMsg);
+ } else if(!strcmp(pName, "PROCID")) {
+ pRes = getPROCID(pMsg);
+ } else if(!strcmp(pName, "MSGID")) {
+ pRes = getMSGID(pMsg);
+ /* here start system properties (those, that do not relate to the message itself */
+ } else if(!strcmp(pName, "$NOW")) {
+ if((pRes = (char*) getNOW(NOW_NOW)) == NULL) {
+ return "***OUT OF MEMORY***";
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ } else if(!strcmp(pName, "$YEAR")) {
+ if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) {
+ return "***OUT OF MEMORY***";
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ } else if(!strcmp(pName, "$MONTH")) {
+ if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) {
+ return "***OUT OF MEMORY***";
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ } else if(!strcmp(pName, "$DAY")) {
+ if((pRes = (char*) getNOW(NOW_DAY)) == NULL) {
+ return "***OUT OF MEMORY***";
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ } else if(!strcmp(pName, "$HOUR")) {
+ if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) {
+ return "***OUT OF MEMORY***";
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ } else if(!strcmp(pName, "$MINUTE")) {
+ if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) {
+ return "***OUT OF MEMORY***";
+ } else
+ *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
+ } else {
+ /* there is no point in continuing, we may even otherwise render the
+ * error message unreadable. rgerhards, 2007-07-10
+ */
+ return "**INVALID PROPERTY NAME**";
+ }
+
+ /* If we did not receive a template pointer, we are already done... */
+ if(pTpe == NULL) {
+ return pRes;
+ }
+
+ /* Now check if we need to make "temporary" transformations (these
+ * are transformations that do not go back into the message -
+ * memory must be allocated for them!).
+ */
+
+ /* substring extraction */
+ /* first we check if we need to extract by field number
+ * rgerhards, 2005-12-22
+ */
+ if(pTpe->data.field.has_fields == 1) {
+ size_t iCurrFld;
+ char *pFld;
+ char *pFldEnd;
+ /* first, skip to the field in question. The field separator
+ * is always one character and is stored in the template entry.
+ */
+ iCurrFld = 1;
+ pFld = pRes;
+ while(*pFld && iCurrFld < pTpe->data.field.iToPos) {
+ /* skip fields until the requested field or end of string is found */
+ while(*pFld && (uchar) *pFld != pTpe->data.field.field_delim)
+ ++pFld; /* skip to field terminator */
+ if(*pFld == pTpe->data.field.field_delim) {
+ ++pFld; /* eat it */
+ ++iCurrFld;
+ }
+ }
+ dprintf("field requested %d, field found %d\n", pTpe->data.field.iToPos, iCurrFld);
+
+ if(iCurrFld == pTpe->data.field.iToPos) {
+ /* field found, now extract it */
+ /* first of all, we need to find the end */
+ pFldEnd = pFld;
+ while(*pFldEnd && *pFldEnd != pTpe->data.field.field_delim)
+ ++pFldEnd;
+ --pFldEnd; /* we are already at the delimiter - so we need to
+ * step back a little not to copy it as part of the field. */
+ /* we got our end pointer, now do the copy */
+ /* TODO: code copied from below, this is a candidate for a separate function */
+ iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */
+ pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
+ if(pBuf == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY**";
+ }
+ /* now copy */
+ memcpy(pBuf, pFld, iLen);
+ pBuf[iLen] = '\0'; /* terminate it */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBufStart;
+ *pbMustBeFreed = 1;
+ if(*(pFldEnd+1) != '\0')
+ ++pFldEnd; /* OK, skip again over delimiter char */
+ } else {
+ /* field not found, return error */
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**FIELD NOT FOUND**";
+ }
+ } else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) {
+ /* we need to obtain a private copy */
+ int iFrom, iTo;
+ iFrom = pTpe->data.field.iFromPos;
+ iTo = pTpe->data.field.iToPos;
+ /* need to zero-base to and from (they are 1-based!) */
+ if(iFrom > 0)
+ --iFrom;
+ if(iTo > 0)
+ --iTo;
+ iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
+ pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
+ if(pBuf == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY**";
+ }
+ if(iFrom) {
+ /* skip to the start of the substring (can't do pointer arithmetic
+ * because the whole string might be smaller!!)
+ */
+ // ++iFrom; /* nbr of chars to skip! */
+ while(*pRes && iFrom) {
+ --iFrom;
+ ++pRes;
+ }
+ }
+ /* OK, we are at the begin - now let's copy... */
+ while(*pRes && iLen) {
+ *pBuf++ = *pRes;
+ ++pRes;
+ --iLen;
+ }
+ *pBuf = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBufStart;
+ *pbMustBeFreed = 1;
+#ifdef FEATURE_REGEXP
+ } else {
+ /* Check for regular expressions */
+ if (pTpe->data.field.has_regex != 0) {
+ if (pTpe->data.field.has_regex == 2)
+ /* Could not compile regex before! */
+ return
+ "**NO MATCH** **BAD REGULAR EXPRESSION**";
+
+ dprintf("debug: String to match for regex is: %s\n",
+ pRes);
+
+ if (0 != regexec(&pTpe->data.field.re, pRes, nmatch,
+ pmatch, 0)) {
+ /* we got no match! */
+ return "**NO MATCH**";
+ } else {
+ /* Match! */
+ /* I need to malloc pB */
+ int iLenBuf;
+ char *pB;
+
+ iLenBuf = pmatch[1].rm_eo - pmatch[1].rm_so;
+ pB = (char *) malloc((iLenBuf + 1) * sizeof(char));
+
+ if (pB == NULL) {
+ if (*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY ALLOCATING pBuf**";
+ }
+
+ /* Lets copy the matched substring to the buffer */
+ memcpy(pB, pRes + pmatch[1].rm_so, iLenBuf);
+ pB[iLenBuf] = '\0';/* terminate string, did not happen before */
+
+ if (*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pB;
+ *pbMustBeFreed = 1;
+ }
+ }
+#endif /* #ifdef FEATURE_REGEXP */
+ }
+
+ /* case conversations (should go after substring, because so we are able to
+ * work on the smallest possible buffer).
+ */
+ if(pTpe->data.field.eCaseConv != tplCaseConvNo) {
+ /* we need to obtain a private copy */
+ int iBufLen = strlen(pRes);
+ char *pBStart;
+ char *pB;
+ pBStart = pB = malloc((iBufLen + 1) * sizeof(char));
+ if(pB == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY**";
+ }
+ while(*pRes) {
+ *pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ?
+ toupper(*pRes) : tolower(*pRes);
+ /* currently only these two exist */
+ ++pRes;
+ }
+ *pB = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBStart;
+ *pbMustBeFreed = 1;
+ }
+
+ /* now do control character dropping/escaping/replacement
+ * Only one of these can be used. If multiple options are given, the
+ * result is random (though currently there obviously is an order of
+ * preferrence, see code below. But this is NOT guaranteed.
+ * RGerhards, 2006-11-17
+ */
+ if(pTpe->data.field.options.bDropCC) {
+ char *pSrc = pRes;
+ char *pDst = pRes;
+
+ while(*pSrc) {
+ if(!iscntrl((int) *pSrc))
+ *pDst++ = *pSrc;
+ ++pSrc;
+ }
+ *pDst = '\0';
+ } else if(pTpe->data.field.options.bSpaceCC) {
+ char *pB = pRes;
+ while(*pB) {
+ if(iscntrl((int) *pB))
+ *pB = ' ';
+ ++pB;
+ }
+ } else if(pTpe->data.field.options.bEscapeCC) {
+ /* we must first count how many control charactes are
+ * present, because we need this to compute the new string
+ * buffer length. While doing so, we also compute the string
+ * length.
+ */
+ int iNumCC = 0;
+ int iLenBuf = 0;
+ char *pB;
+
+ for(pB = pRes ; *pB ; ++pB) {
+ ++iLenBuf;
+ if(iscntrl((int) *pB))
+ ++iNumCC;
+ }
+
+ if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */
+ /* OK, let's do the escaping... */
+ char *pBStart;
+ char szCCEsc[8]; /* buffer for escape sequence */
+ int i;
+
+ iLenBuf += iNumCC * 4;
+ pBStart = pB = malloc((iLenBuf + 1) * sizeof(char));
+ if(pB == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY**";
+ }
+ while(*pRes) {
+ if(iscntrl((int) *pRes)) {
+ snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
+ for(i = 0 ; i < 4 ; ++i)
+ *pB++ = szCCEsc[i];
+ } else {
+ *pB++ = *pRes;
+ }
+ ++pRes;
+ }
+ *pB = '\0';
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ pRes = pBStart;
+ *pbMustBeFreed = 1;
+ }
+ }
+
+ /* Now drop last LF if present (pls note that this must not be done
+ * if bEscapeCC was set!
+ */
+ if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) {
+ int iLn = strlen(pRes);
+ char *pB;
+ if(*(pRes + iLn - 1) == '\n') {
+ /* we have a LF! */
+ /* check if we need to obtain a private copy */
+ if(pbMustBeFreed == 0) {
+ /* ok, original copy, need a private one */
+ pB = malloc((iLn + 1) * sizeof(char));
+ if(pB == NULL) {
+ if(*pbMustBeFreed == 1)
+ free(pRes);
+ *pbMustBeFreed = 0;
+ return "**OUT OF MEMORY**";
+ }
+ memcpy(pB, pRes, iLn - 1);
+ pRes = pB;
+ *pbMustBeFreed = 1;
+ }
+ *(pRes + iLn - 1) = '\0'; /* drop LF ;) */
+ }
+ }
+
+ /*dprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */
+ return(pRes);
+}
+
+
/*
* vi:set ai:
*/
diff --git a/msg.h b/msg.h
index 7b3cbd46..bf5ba07b 100644
--- a/msg.h
+++ b/msg.h
@@ -145,6 +145,9 @@ void MsgSetMSG(msg_t *pMsg, char* pszMSG);
void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg);
void moveHOSTNAMEtoTAG(msg_t *pM);
char *getMSGID(msg_t *pM);
+char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
+ rsCStrObj *pCSPropName, unsigned short *pbMustBeFreed);
+char *textpri(char *pRes, size_t pResLen, int pri);
#endif /* #ifndef MSG_H_INCLUDED */
/*
diff --git a/omfile.c b/omfile.c
new file mode 100644
index 00000000..f2d7814a
--- /dev/null
+++ b/omfile.c
@@ -0,0 +1,437 @@
+/* omfile.c
+ * This is the implementation of the build-in file output module.
+ *
+ * Handles: F_CONSOLE, F_TTY, F_FILE, F_PIPE
+ *
+ * File begun on 2007-07-21 by RGerhards (extracted from syslogd.c)
+ * This file is under development and has not yet arrived at being fully
+ * self-contained and a real object. So far, it is mostly an excerpt
+ * of the "old" message code without any modifications. However, it
+ * helps to have things at the right place one we go to the meat of it.
+ *
+ * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <sys/file.h>
+//#include <sys/param.h>
+
+#include "rsyslog.h"
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "omfile.h"
+
+
+/* rgerhards 2005-06-21: Try to resolve a size limit
+ * situation. This first runs the command, and then
+ * checks if we are still above the treshold.
+ * returns 0 if ok, 1 otherwise
+ * TODO: consider moving the initial check in here, too
+ */
+int resolveFileSizeLimit(selector_t *f)
+{
+ uchar *pParams;
+ uchar *pCmd;
+ uchar *p;
+ off_t actualFileSize;
+ assert(f != NULL);
+
+ if(f->f_un.f_file.f_sizeLimitCmd == NULL)
+ return 1; /* nothing we can do in this case... */
+
+ /* the execProg() below is probably not great, but at least is is
+ * fairly secure now. Once we change the way file size limits are
+ * handled, we should also revisit how this command is run (and
+ * with which parameters). rgerhards, 2007-07-20
+ */
+ /* we first check if we have command line parameters. We assume this,
+ * when we have a space in the program name. If we find it, everything after
+ * the space is treated as a single argument.
+ */
+ if((pCmd = (uchar*)strdup((char*)f->f_un.f_file.f_sizeLimitCmd)) == NULL) {
+ /* there is not much we can do - we make syslogd close the file in this case */
+ glblHadMemShortage = 1;
+ return 1;
+ }
+
+ for(p = pCmd ; *p && *p != ' ' ; ++p) {
+ /* JUST SKIP */
+ }
+
+ if(*p == ' ') {
+ *p = '\0'; /* pretend string-end */
+ pParams = p+1;
+ } else
+ pParams = NULL;
+
+ execProg(pCmd, 1, pParams);
+
+ f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
+ f->f_un.f_file.fCreateMode);
+
+ actualFileSize = lseek(f->f_file, 0, SEEK_END);
+ if(actualFileSize >= f->f_un.f_file.f_sizeLimit) {
+ /* OK, it didn't work out... */
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* This function deletes an entry from the dynamic file name
+ * cache. A pointer to the cache must be passed in as well
+ * as the index of the to-be-deleted entry. This index may
+ * point to an unallocated entry, in whcih case the
+ * function immediately returns. Parameter bFreeEntry is 1
+ * if the entry should be free()ed and 0 if not.
+ */
+static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
+{
+ assert(pCache != NULL);
+
+ if(pCache[iEntry] == NULL)
+ return;
+
+ dprintf("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
+ pCache[iEntry]->pName == NULL ? "[OPEN FAILED]" : (char*)pCache[iEntry]->pName);
+ /* if the name is NULL, this is an improperly initilized entry which
+ * needs to be discarded. In this case, neither the file is to be closed
+ * not the name to be freed.
+ */
+ if(pCache[iEntry]->pName != NULL) {
+ close(pCache[iEntry]->fd);
+ free(pCache[iEntry]->pName);
+ pCache[iEntry]->pName = NULL;
+ }
+
+ if(bFreeEntry) {
+ free(pCache[iEntry]);
+ pCache[iEntry] = NULL;
+ }
+}
+
+
+/* This function frees the dynamic file name cache.
+ */
+static void dynaFileFreeCache(selector_t *f)
+{
+ register int i;
+ assert(f != NULL);
+
+ for(i = 0 ; i < f->f_un.f_file.iCurrCacheSize ; ++i) {
+ dynaFileDelCacheEntry(f->f_un.f_file.dynCache, i, 1);
+ }
+
+ free(f->f_un.f_file.dynCache);
+}
+
+
+/* This function handles dynamic file names. It generates a new one
+ * based on the current message, checks if that file is already open
+ * and, if not, does everything needed to switch to the new one.
+ * Function returns 0 if all went well and non-zero otherwise.
+ * On successful return f->f_file must point to the correct file to
+ * be written.
+ * This is a helper to writeFile(). rgerhards, 2007-07-03
+ */
+static int prepareDynFile(selector_t *f)
+{
+ uchar *newFileName;
+ time_t ttOldest; /* timestamp of oldest element */
+ int iOldest;
+ int i;
+ int iFirstFree;
+ dynaFileCacheEntry **pCache;
+
+ assert(f != NULL);
+ if((newFileName = tplToString(f->f_un.f_file.pTpl, f->f_pMsg)) == NULL) {
+ /* memory shortage - there is nothing we can do to resolve it.
+ * We silently ignore it, this is probably the best we can do.
+ */
+ glblHadMemShortage = TRUE;
+ dprintf("prepareDynfile(): could not create file name, discarding this request\n");
+ return -1;
+ }
+
+ pCache = f->f_un.f_file.dynCache;
+
+ /* first check, if we still have the current file
+ * I *hope* this will be a performance enhancement.
+ */
+ if( (f->f_un.f_file.iCurrElt != -1)
+ && !strcmp((char*) newFileName,
+ (char*) pCache[f->f_un.f_file.iCurrElt])) {
+ /* great, we are all set */
+ free(newFileName);
+ pCache[f->f_un.f_file.iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */
+ return 0;
+ }
+
+ /* ok, no luck. Now let's search the table if we find a matching spot.
+ * While doing so, we also prepare for creation of a new one.
+ */
+ iFirstFree = -1; /* not yet found */
+ iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */
+ ttOldest = time(NULL) + 1; /* there must always be an older one */
+ for(i = 0 ; i < f->f_un.f_file.iCurrCacheSize ; ++i) {
+ if(pCache[i] == NULL) {
+ if(iFirstFree == -1)
+ iFirstFree = i;
+ } else { /* got an element, let's see if it matches */
+ if(!strcmp((char*) newFileName, (char*) pCache[i]->pName)) {
+ /* we found our element! */
+ f->f_file = pCache[i]->fd;
+ f->f_un.f_file.iCurrElt = i;
+ free(newFileName);
+ pCache[i]->lastUsed = time(NULL); /* update timestamp for LRU */
+ return 0;
+ }
+ /* did not find it - so lets keep track of the counters for LRU */
+ if(pCache[i]->lastUsed < ttOldest) {
+ ttOldest = pCache[i]->lastUsed;
+ iOldest = i;
+ }
+ }
+ }
+
+ /* we have not found an entry */
+ if(iFirstFree == -1 && (f->f_un.f_file.iCurrCacheSize < f->f_un.f_file.iDynaFileCacheSize)) {
+ /* there is space left, so set it to that index */
+ iFirstFree = f->f_un.f_file.iCurrCacheSize++;
+ }
+
+ if(iFirstFree == -1) {
+ dynaFileDelCacheEntry(pCache, iOldest, 0);
+ iFirstFree = iOldest; /* this one *is* now free ;) */
+ } else {
+ /* we need to allocate memory for the cache structure */
+ pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry));
+ if(pCache[iFirstFree] == NULL) {
+ glblHadMemShortage = TRUE;
+ dprintf("prepareDynfile(): could not alloc mem, discarding this request\n");
+ free(newFileName);
+ return -1;
+ }
+ }
+
+ /* Ok, we finally can open the file */
+ if(access((char*)newFileName, F_OK) == 0) {
+ /* file already exists */
+ f->f_file = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
+ f->f_un.f_file.fCreateMode);
+ } else {
+ /* file does not exist, create it (and eventually parent directories */
+ if(f->f_un.f_file.bCreateDirs) {
+ /* we fist need to create parent dirs if they are missing
+ * We do not report any errors here ourselfs but let the code
+ * fall through to error handler below.
+ */
+ if(makeFileParentDirs(newFileName, strlen((char*)newFileName),
+ f->f_un.f_file.fDirCreateMode, f->f_un.f_file.dirUID,
+ f->f_un.f_file.dirGID, f->f_un.f_file.bFailOnChown) == 0) {
+ f->f_file = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
+ f->f_un.f_file.fCreateMode);
+ if(f->f_file != -1) {
+ /* check and set uid/gid */
+ if(f->f_un.f_file.fileUID != (uid_t)-1 || f->f_un.f_file.fileGID != (gid_t) -1) {
+ /* we need to set owner/group */
+ if(fchown(f->f_file, f->f_un.f_file.fileUID,
+ f->f_un.f_file.fileGID) != 0) {
+ if(f->f_un.f_file.bFailOnChown) {
+ int eSave = errno;
+ close(f->f_file);
+ f->f_file = -1;
+ errno = eSave;
+ }
+ /* we will silently ignore the chown() failure
+ * if configured to do so.
+ */
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* file is either open now or an error state set */
+ if(f->f_file == -1) {
+ /* do not report anything if the message is an internally-generated
+ * message. Otherwise, we could run into a never-ending loop. The bad
+ * news is that we also lose errors on startup messages, but so it is.
+ */
+ if(f->f_pMsg->msgFlags & INTERNAL_MSG)
+ dprintf("Could not open dynaFile, discarding message\n");
+ else
+ logerrorSz("Could not open dynamic file '%s' - discarding message", (char*)newFileName);
+ free(newFileName);
+ dynaFileDelCacheEntry(pCache, iFirstFree, 1);
+ return -1;
+ }
+
+ pCache[iFirstFree]->fd = f->f_file;
+ pCache[iFirstFree]->pName = newFileName;
+ pCache[iFirstFree]->lastUsed = time(NULL);
+ f->f_un.f_file.iCurrElt = iFirstFree;
+ dprintf("Added new entry %d for file cache, file '%s'.\n",
+ iFirstFree, newFileName);
+
+ return 0;
+}
+
+
+/* rgerhards 2004-11-11: write to a file output. This
+ * will be called for all outputs using file semantics,
+ * for example also for pipes.
+ */
+void writeFile(selector_t *f)
+{
+ off_t actualFileSize;
+
+ assert(f != NULL);
+
+ /* first check if we have a dynamic file name and, if so,
+ * check if it still is ok or a new file needs to be created
+ */
+ if(f->f_un.f_file.bDynamicName) {
+ if(prepareDynFile(f) != 0)
+ return;
+ }
+
+ /* create the message based on format specified */
+ iovCreate(f);
+again:
+ /* check if we have a file size limit and, if so,
+ * obey to it.
+ */
+ if(f->f_un.f_file.f_sizeLimit != 0) {
+ actualFileSize = lseek(f->f_file, 0, SEEK_END);
+ if(actualFileSize >= f->f_un.f_file.f_sizeLimit) {
+ char errMsg[256];
+ /* for now, we simply disable a file once it is
+ * beyond the maximum size. This is better than having
+ * us aborted by the OS... rgerhards 2005-06-21
+ */
+ (void) close(f->f_file);
+ /* try to resolve the situation */
+ if(resolveFileSizeLimit(f) != 0) {
+ /* didn't work out, so disable... */
+ f->f_type = F_UNUSED;
+ snprintf(errMsg, sizeof(errMsg),
+ "no longer writing to file %s; grown beyond configured file size of %lld bytes, actual size %lld - configured command did not resolve situation",
+ f->f_un.f_file.f_fname, (long long) f->f_un.f_file.f_sizeLimit, (long long) actualFileSize);
+ errno = 0;
+ logerror(errMsg);
+ return;
+ } else {
+ snprintf(errMsg, sizeof(errMsg),
+ "file %s had grown beyond configured file size of %lld bytes, actual size was %lld - configured command resolved situation",
+ f->f_un.f_file.f_fname, (long long) f->f_un.f_file.f_sizeLimit, (long long) actualFileSize);
+ errno = 0;
+ logerror(errMsg);
+ }
+ }
+ }
+
+ if (writev(f->f_file, f->f_iov, f->f_iIovUsed) < 0) {
+ int e = errno;
+
+ /* If a named pipe is full, just ignore it for now
+ - mrn 24 May 96 */
+ if (f->f_type == F_PIPE && e == EAGAIN)
+ return;
+
+ /* If the filesystem is filled up, just ignore
+ * it for now and continue writing when possible
+ * based on patch for sysklogd by Martin Schulze on 2007-05-24
+ */
+ if (f->f_type == F_FILE && e == ENOSPC)
+ return;
+
+ (void) close(f->f_file);
+ /*
+ * Check for EBADF on TTY's due to vhangup()
+ * Linux uses EIO instead (mrn 12 May 96)
+ */
+ if ((f->f_type == F_TTY || f->f_type == F_CONSOLE)
+#ifdef linux
+ && e == EIO) {
+#else
+ && e == EBADF) {
+#endif
+ f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_NOCTTY);
+ if (f->f_file < 0) {
+ f->f_type = F_UNUSED;
+ logerror(f->f_un.f_file.f_fname);
+ } else {
+ untty();
+ goto again;
+ }
+ } else {
+ f->f_type = F_UNUSED;
+ errno = e;
+ logerror(f->f_un.f_file.f_fname);
+ }
+ } else if (f->f_flags & SYNC_FILE)
+ fsync(f->f_file);
+}
+
+
+/* free an instance
+ * returns 0 if it succeeds, something else otherwise
+ */
+int freeInstanceFile(selector_t *f)
+{
+ assert(f != NULL);
+ if(f->f_un.f_file.bDynamicName) {
+ dynaFileFreeCache(f);
+ } else
+ close(f->f_file);
+ return 0;
+}
+
+
+/* call the shell action
+ * returns 0 if it succeeds, something else otherwise
+ */
+int doActionFile(selector_t *f)
+{
+ assert(f != NULL);
+
+ /* f->f_file == -1 is an indicator that the we couldn't
+ * open the file at startup. For dynaFiles, this is ok,
+ * all others are doomed.
+ */
+ if(f->f_un.f_file.bDynamicName || (f->f_file != -1))
+ writeFile(f);
+ return 0;
+}
+/*
+ * vi:set ai:
+ */
diff --git a/omfile.h b/omfile.h
new file mode 100644
index 00000000..330ee4c1
--- /dev/null
+++ b/omfile.h
@@ -0,0 +1,35 @@
+/* omfile.h
+ * These are the definitions for the build-in file output module.
+ *
+ * File begun on 2007-07-21 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef OMFILE_H_INCLUDED
+#define OMFILE_H_INCLUDED 1
+
+/* prototypes */
+
+int doActionFile(selector_t *f);
+int freeInstanceFile(selector_t *f);
+
+#endif /* #ifndef OMFILE_H_INCLUDED */
+/*
+ * vi:set ai:
+ */
diff --git a/omfwd.c b/omfwd.c
index ab4dcc5b..04d5633d 100644
--- a/omfwd.c
+++ b/omfwd.c
@@ -43,6 +43,7 @@
#include "srUtils.h"
#include "net.h"
#include "omfwd.h"
+#include "template.h"
#include "msg.h"
#include "tcpsyslog.h"
diff --git a/omshell.c b/omshell.c
index 5f64e50d..001fbe2d 100644
--- a/omshell.c
+++ b/omshell.c
@@ -43,7 +43,7 @@
/* call the shell action
* returns 0 if it succeeds, something else otherwise
*/
-int doActionShell(selector_t *f, time_t now)
+int doActionShell(selector_t *f)
{
uchar *psz;
diff --git a/omshell.h b/omshell.h
index 1ffbec44..69558ab7 100644
--- a/omshell.h
+++ b/omshell.h
@@ -26,7 +26,7 @@
/* prototypes */
-int doActionShell(selector_t *f, time_t now);
+int doActionShell(selector_t *f);
#endif /* #ifndef ACTSHELL_H_INCLUDED */
/*
diff --git a/syslogd.c b/syslogd.c
index cf88c117..8c1459f4 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -205,20 +205,22 @@
#endif
#include "rsyslog.h"
+#include "srUtils.h"
+#include "stringbuf.h"
+#include "syslogd-types.h"
#include "template.h"
#include "outchannel.h"
#include "syslogd.h"
#include "net.h" /* struct NetAddr */
-#include "stringbuf.h"
#include "parse.h"
-#include "srUtils.h"
#include "msg.h"
#include "tcpsyslog.h"
#include "omshell.h"
#include "omusrmsg.h"
#include "ommysql.h"
#include "omfwd.h"
+#include "omfile.h"
/* We define our own set of syslog defintions so that we
* do not need to rely on (possibly different) implementations.
@@ -386,13 +388,6 @@ int funix[MAXFUNIX] = { -1, }; /* read-only after startup */
#define TABLE_ALLPRI 0xFF /* Value to indicate all priorities in f_pmask */
#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */
-/* Flags to logmsg().
- */
-#define INTERNAL_MSG 0x001 /* msg generated by logmsgInternal() --> special handling */
-#define SYNC_FILE 0x002 /* do fsync on file after printing */
-#define ADDDATE 0x004 /* add a date to the message */
-#define MARK 0x008 /* this message is a mark */
-
/* This table lists the directive lines:
*/
static const char *directive_name_list[] = {
@@ -1606,512 +1601,6 @@ void getCurrTime(struct syslogTime *t)
t->OffsetHour = lBias / 3600;
t->OffsetMinute = lBias % 3600;
}
-
-/* Decode a priority into textual information like auth.emerg.
- * The variable pRes must point to a user-supplied buffer and
- * pResLen must contain its size. The pointer to the buffer
- * is also returned, what makes this functiona suitable for
- * use in printf-like functions.
- * Note: a buffer size of 20 characters is always sufficient.
- * Interface to this function changed 2007-06-15 by RGerhards
- */
-char *textpri(char *pRes, size_t pResLen, int pri)
-{
- syslogCODE *c_pri, *c_fac;
-
- assert(pRes != NULL);
- assert(pResLen > 0);
-
- for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++);
- for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
-
- snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri);
-
- return pRes;
-}
-
-
-/* This function returns the current date in different
- * variants. It is used to construct the $NOW series of
- * system properties. The returned buffer must be freed
- * by the caller when no longer needed. If the function
- * can not allocate memory, it returns a NULL pointer.
- * Added 2007-07-10 rgerhards
- */
-typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_MINUTE } eNOWType;
-#define tmpBUFSIZE 16 /* size of formatting buffer */
-static uchar *getNOW(eNOWType eNow)
-{
- uchar *pBuf;
- struct syslogTime t;
-
- if((pBuf = (uchar*) malloc(sizeof(uchar) * tmpBUFSIZE)) == NULL) {
- glblHadMemShortage = 1;
- return NULL;
- }
-
- getCurrTime(&t);
- switch(eNow) {
- case NOW_NOW:
- snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day);
- break;
- case NOW_YEAR:
- snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d", t.year);
- break;
- case NOW_MONTH:
- snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.month);
- break;
- case NOW_DAY:
- snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.day);
- break;
- case NOW_HOUR:
- snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.hour);
- break;
- case NOW_MINUTE:
- snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute);
- break;
- }
-
- return(pBuf);
-}
-#undef tmpBUFSIZE /* clean up */
-
-
-/* This function returns a string-representation of the
- * requested message property. This is a generic function used
- * to abstract properties so that these can be easier
- * queried. Returns NULL if property could not be found.
- * Actually, this function is a big if..elseif. What it does
- * is simply to map property names (from MonitorWare) to the
- * message object data fields.
- *
- * In case we need string forms of propertis we do not
- * yet have in string form, we do a memory allocation that
- * is sufficiently large (in all cases). Once the string
- * form has been obtained, it is saved until the Msg object
- * is finally destroyed. This is so that we save the processing
- * time in the (likely) case that this property is requested
- * again. It also saves us a lot of dynamic memory management
- * issues in the upper layers, because we so can guarantee that
- * the buffer will remain static AND available during the lifetime
- * of the object. Please note that both the max size allocation as
- * well as keeping things in memory might like look like a
- * waste of memory (some might say it actually is...) - we
- * deliberately accept this because performance is more important
- * to us ;)
- * rgerhards 2004-11-18
- * Parameter "bMustBeFreed" is set by this function. It tells the
- * caller whether or not the string returned must be freed by the
- * caller itself. It is is 0, the caller MUST NOT free it. If it is
- * 1, the caller MUST free 1. Handling this wrongly leads to either
- * a memory leak of a program abort (do to double-frees or frees on
- * the constant memory pool). So be careful to do it right.
- * rgerhards 2004-11-23
- * regular expression support contributed by Andres Riancho merged
- * on 2005-09-13
- * changed so that it now an be called without a template entry (NULL).
- * In this case, only the (unmodified) property is returned. This will
- * be used in selector line processing.
- * rgerhards 2005-09-15
- */
-static char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe,
- rsCStrObj *pCSPropName, unsigned short *pbMustBeFreed)
-{
- char *pName;
- char *pRes; /* result pointer */
- char *pBufStart;
- char *pBuf;
- int iLen;
-
-#ifdef FEATURE_REGEXP
- /* Variables necessary for regular expression matching */
- size_t nmatch = 2;
- regmatch_t pmatch[2];
-#endif
-
- assert(pMsg != NULL);
- assert(pbMustBeFreed != NULL);
-
- if(pCSPropName == NULL) {
- assert(pTpe != NULL);
- pName = pTpe->data.field.pPropRepl;
- } else {
- pName = (char*) rsCStrGetSzStr(pCSPropName);
- }
- *pbMustBeFreed = 0;
-
- /* sometimes there are aliases to the original MonitoWare
- * property names. These come after || in the ifs below. */
- if(!strcmp(pName, "msg")) {
- pRes = getMSG(pMsg);
- } else if(!strcmp(pName, "rawmsg")) {
- pRes = getRawMsg(pMsg);
- } else if(!strcmp(pName, "UxTradMsg")) {
- pRes = getUxTradMsg(pMsg);
- } else if(!strcmp(pName, "FROMHOST")) {
- pRes = getRcvFrom(pMsg);
- } else if(!strcmp(pName, "source")
- || !strcmp(pName, "HOSTNAME")) {
- pRes = getHOSTNAME(pMsg);
- } else if(!strcmp(pName, "syslogtag")) {
- pRes = getTAG(pMsg);
- } else if(!strcmp(pName, "PRI")) {
- pRes = getPRI(pMsg);
- } else if(!strcmp(pName, "PRI-text")) {
- pBuf = malloc(20 * sizeof(char));
- if(pBuf == NULL) {
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- } else {
- *pbMustBeFreed = 1;
- pRes = textpri(pBuf, 20, getPRIi(pMsg));
- }
- } else if(!strcmp(pName, "iut")) {
- pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */
- } else if(!strcmp(pName, "syslogfacility")) {
- pRes = getFacility(pMsg);
- } else if(!strcmp(pName, "syslogfacility-text")) {
- pRes = getFacilityStr(pMsg);
- } else if(!strcmp(pName, "syslogseverity") || !strcmp(pName, "syslogpriority")) {
- pRes = getSeverity(pMsg);
- } else if(!strcmp(pName, "syslogseverity-text") || !strcmp(pName, "syslogpriority-text")) {
- pRes = getSeverityStr(pMsg);
- } else if(!strcmp(pName, "timegenerated")) {
- pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat);
- } else if(!strcmp(pName, "timereported")
- || !strcmp(pName, "TIMESTAMP")) {
- pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat);
- } else if(!strcmp(pName, "programname")) {
- pRes = getProgramName(pMsg);
- } else if(!strcmp(pName, "PROTOCOL-VERSION")) {
- pRes = getProtocolVersionString(pMsg);
- } else if(!strcmp(pName, "STRUCTURED-DATA")) {
- pRes = getStructuredData(pMsg);
- } else if(!strcmp(pName, "APP-NAME")) {
- pRes = getAPPNAME(pMsg);
- } else if(!strcmp(pName, "PROCID")) {
- pRes = getPROCID(pMsg);
- } else if(!strcmp(pName, "MSGID")) {
- pRes = getMSGID(pMsg);
- /* here start system properties (those, that do not relate to the message itself */
- } else if(!strcmp(pName, "$NOW")) {
- if((pRes = (char*) getNOW(NOW_NOW)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp(pName, "$YEAR")) {
- if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp(pName, "$MONTH")) {
- if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp(pName, "$DAY")) {
- if((pRes = (char*) getNOW(NOW_DAY)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp(pName, "$HOUR")) {
- if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else if(!strcmp(pName, "$MINUTE")) {
- if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) {
- return "***OUT OF MEMORY***";
- } else
- *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */
- } else {
- /* there is no point in continuing, we may even otherwise render the
- * error message unreadable. rgerhards, 2007-07-10
- */
- return "**INVALID PROPERTY NAME**";
- }
-
- /* If we did not receive a template pointer, we are already done... */
- if(pTpe == NULL) {
- return pRes;
- }
-
- /* Now check if we need to make "temporary" transformations (these
- * are transformations that do not go back into the message -
- * memory must be allocated for them!).
- */
-
- /* substring extraction */
- /* first we check if we need to extract by field number
- * rgerhards, 2005-12-22
- */
- if(pTpe->data.field.has_fields == 1) {
- size_t iCurrFld;
- char *pFld;
- char *pFldEnd;
- /* first, skip to the field in question. The field separator
- * is always one character and is stored in the template entry.
- */
- iCurrFld = 1;
- pFld = pRes;
- while(*pFld && iCurrFld < pTpe->data.field.iToPos) {
- /* skip fields until the requested field or end of string is found */
- while(*pFld && (uchar) *pFld != pTpe->data.field.field_delim)
- ++pFld; /* skip to field terminator */
- if(*pFld == pTpe->data.field.field_delim) {
- ++pFld; /* eat it */
- ++iCurrFld;
- }
- }
- dprintf("field requested %d, field found %d\n", pTpe->data.field.iToPos, iCurrFld);
-
- if(iCurrFld == pTpe->data.field.iToPos) {
- /* field found, now extract it */
- /* first of all, we need to find the end */
- pFldEnd = pFld;
- while(*pFldEnd && *pFldEnd != pTpe->data.field.field_delim)
- ++pFldEnd;
- --pFldEnd; /* we are already at the delimiter - so we need to
- * step back a little not to copy it as part of the field. */
- /* we got our end pointer, now do the copy */
- /* TODO: code copied from below, this is a candidate for a separate function */
- iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */
- pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
- if(pBuf == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- /* now copy */
- memcpy(pBuf, pFld, iLen);
- pBuf[iLen] = '\0'; /* terminate it */
- if(*pbMustBeFreed == 1)
- free(pRes);
- pRes = pBufStart;
- *pbMustBeFreed = 1;
- if(*(pFldEnd+1) != '\0')
- ++pFldEnd; /* OK, skip again over delimiter char */
- } else {
- /* field not found, return error */
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**FIELD NOT FOUND**";
- }
- } else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) {
- /* we need to obtain a private copy */
- int iFrom, iTo;
- iFrom = pTpe->data.field.iFromPos;
- iTo = pTpe->data.field.iToPos;
- /* need to zero-base to and from (they are 1-based!) */
- if(iFrom > 0)
- --iFrom;
- if(iTo > 0)
- --iTo;
- iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */
- pBufStart = pBuf = malloc((iLen + 1) * sizeof(char));
- if(pBuf == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- if(iFrom) {
- /* skip to the start of the substring (can't do pointer arithmetic
- * because the whole string might be smaller!!)
- */
- // ++iFrom; /* nbr of chars to skip! */
- while(*pRes && iFrom) {
- --iFrom;
- ++pRes;
- }
- }
- /* OK, we are at the begin - now let's copy... */
- while(*pRes && iLen) {
- *pBuf++ = *pRes;
- ++pRes;
- --iLen;
- }
- *pBuf = '\0';
- if(*pbMustBeFreed == 1)
- free(pRes);
- pRes = pBufStart;
- *pbMustBeFreed = 1;
-#ifdef FEATURE_REGEXP
- } else {
- /* Check for regular expressions */
- if (pTpe->data.field.has_regex != 0) {
- if (pTpe->data.field.has_regex == 2)
- /* Could not compile regex before! */
- return
- "**NO MATCH** **BAD REGULAR EXPRESSION**";
-
- dprintf("debug: String to match for regex is: %s\n",
- pRes);
-
- if (0 != regexec(&pTpe->data.field.re, pRes, nmatch,
- pmatch, 0)) {
- /* we got no match! */
- return "**NO MATCH**";
- } else {
- /* Match! */
- /* I need to malloc pB */
- int iLenBuf;
- char *pB;
-
- iLenBuf = pmatch[1].rm_eo - pmatch[1].rm_so;
- pB = (char *) malloc((iLenBuf + 1) * sizeof(char));
-
- if (pB == NULL) {
- if (*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY ALLOCATING pBuf**";
- }
-
- /* Lets copy the matched substring to the buffer */
- memcpy(pB, pRes + pmatch[1].rm_so, iLenBuf);
- pB[iLenBuf] = '\0';/* terminate string, did not happen before */
-
- if (*pbMustBeFreed == 1)
- free(pRes);
- pRes = pB;
- *pbMustBeFreed = 1;
- }
- }
-#endif /* #ifdef FEATURE_REGEXP */
- }
-
- /* case conversations (should go after substring, because so we are able to
- * work on the smallest possible buffer).
- */
- if(pTpe->data.field.eCaseConv != tplCaseConvNo) {
- /* we need to obtain a private copy */
- int iBufLen = strlen(pRes);
- char *pBStart;
- char *pB;
- pBStart = pB = malloc((iBufLen + 1) * sizeof(char));
- if(pB == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- while(*pRes) {
- *pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ?
- toupper(*pRes) : tolower(*pRes);
- /* currently only these two exist */
- ++pRes;
- }
- *pB = '\0';
- if(*pbMustBeFreed == 1)
- free(pRes);
- pRes = pBStart;
- *pbMustBeFreed = 1;
- }
-
- /* now do control character dropping/escaping/replacement
- * Only one of these can be used. If multiple options are given, the
- * result is random (though currently there obviously is an order of
- * preferrence, see code below. But this is NOT guaranteed.
- * RGerhards, 2006-11-17
- */
- if(pTpe->data.field.options.bDropCC) {
- char *pSrc = pRes;
- char *pDst = pRes;
-
- while(*pSrc) {
- if(!iscntrl((int) *pSrc))
- *pDst++ = *pSrc;
- ++pSrc;
- }
- *pDst = '\0';
- } else if(pTpe->data.field.options.bSpaceCC) {
- char *pB = pRes;
- while(*pB) {
- if(iscntrl((int) *pB))
- *pB = ' ';
- ++pB;
- }
- } else if(pTpe->data.field.options.bEscapeCC) {
- /* we must first count how many control charactes are
- * present, because we need this to compute the new string
- * buffer length. While doing so, we also compute the string
- * length.
- */
- int iNumCC = 0;
- int iLenBuf = 0;
- char *pB;
-
- for(pB = pRes ; *pB ; ++pB) {
- ++iLenBuf;
- if(iscntrl((int) *pB))
- ++iNumCC;
- }
-
- if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */
- /* OK, let's do the escaping... */
- char *pBStart;
- char szCCEsc[8]; /* buffer for escape sequence */
- int i;
-
- iLenBuf += iNumCC * 4;
- pBStart = pB = malloc((iLenBuf + 1) * sizeof(char));
- if(pB == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- while(*pRes) {
- if(iscntrl((int) *pRes)) {
- snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes);
- for(i = 0 ; i < 4 ; ++i)
- *pB++ = szCCEsc[i];
- } else {
- *pB++ = *pRes;
- }
- ++pRes;
- }
- *pB = '\0';
- if(*pbMustBeFreed == 1)
- free(pRes);
- pRes = pBStart;
- *pbMustBeFreed = 1;
- }
- }
-
- /* Now drop last LF if present (pls note that this must not be done
- * if bEscapeCC was set!
- */
- if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) {
- int iLn = strlen(pRes);
- char *pB;
- if(*(pRes + iLn - 1) == '\n') {
- /* we have a LF! */
- /* check if we need to obtain a private copy */
- if(pbMustBeFreed == 0) {
- /* ok, original copy, need a private one */
- pB = malloc((iLn + 1) * sizeof(char));
- if(pB == NULL) {
- if(*pbMustBeFreed == 1)
- free(pRes);
- *pbMustBeFreed = 0;
- return "**OUT OF MEMORY**";
- }
- memcpy(pB, pRes, iLn - 1);
- pRes = pB;
- *pbMustBeFreed = 1;
- }
- *(pRes + iLn - 1) = '\0'; /* drop LF ;) */
- }
- }
-
- /*dprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */
- return(pRes);
-}
-
/* rgerhards 2004-11-09: end of helper routines. On to the
* "real" code ;)
*/
@@ -2384,7 +1873,7 @@ static char **crunch_list(char *list)
}
-static void untty()
+void untty(void)
#ifdef HAVE_SETSID
{
if ( !Debug ) {
@@ -3582,129 +3071,6 @@ void logmsg(int pri, msg_t *pMsg, int flags)
}
-/* Helper to doSQLEscape. This is called if doSQLEscape
- * runs out of memory allocating the escaped string.
- * Then we are in trouble. We can
- * NOT simply return the unmodified string because this
- * may cause SQL injection. But we also can not simply
- * abort the run, this would be a DoS. I think an appropriate
- * measure is to remove the dangerous \' characters. We
- * replace them by \", which will break the message and
- * signatures eventually present - but this is the
- * best thing we can do now (or does anybody
- * have a better idea?). rgerhards 2004-11-23
- * added support for "escapeMode" (so doSQLEscape for details).
- * if mode = 1, then backslashes are changed to slashes.
- * rgerhards 2005-09-22
- */
-void doSQLEmergencyEscape(register char *p, int escapeMode)
-{
- while(*p) {
- if(*p == '\'')
- *p = '"';
- else if((escapeMode == 1) && (*p == '\\'))
- *p = '/';
- ++p;
- }
-}
-
-
-/* SQL-Escape a string. Single quotes are found and
- * replaced by two of them. A new buffer is allocated
- * for the provided string and the provided buffer is
- * freed. The length is updated. Parameter pbMustBeFreed
- * is set to 1 if a new buffer is allocated. Otherwise,
- * it is left untouched.
- * --
- * We just discovered a security issue. MySQL is so
- * "smart" to not only support the standard SQL mechanism
- * for escaping quotes, but to also provide its own (using
- * c-type syntax with backslashes). As such, it is actually
- * possible to do sql injection via rsyslogd. The cure is now
- * to escape backslashes, too. As we have found on the web, some
- * other databases seem to be similar "smart" (why do we have standards
- * at all if they are violated without any need???). Even better, MySQL's
- * smartness depends on config settings. So we add a new option to this
- * function that allows the caller to select if they want to standard or
- * "smart" encoding ;)
- * new parameter escapeMode is 0 - standard sql, 1 - "smart" engines
- * 2005-09-22 rgerhards
- */
-void doSQLEscape(char **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode)
-{
- char *p;
- int iLen;
- rsCStrObj *pStrB;
- uchar *pszGenerated;
-
- assert(pp != NULL);
- assert(*pp != NULL);
- assert(pLen != NULL);
- assert(pbMustBeFreed != NULL);
-
- /* first check if we need to do anything at all... */
- if(escapeMode == 0)
- for(p = *pp ; *p && *p != '\'' ; ++p)
- ;
- else
- for(p = *pp ; *p && *p != '\'' && *p != '\\' ; ++p)
- ;
- /* 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! */
-
- p = *pp;
- iLen = *pLen;
- if((pStrB = rsCStrConstruct()) == NULL) {
- /* oops - no mem ... Do emergency... */
- doSQLEmergencyEscape(p, escapeMode);
- return;
- }
-
- while(*p) {
- if(*p == '\'') {
- if(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\') != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- rsCStrFinish(pStrB);
- if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) != NULL)
- free(pszGenerated);
- return;
- }
- iLen++; /* reflect the extra character */
- } else if((escapeMode == 1) && (*p == '\\')) {
- if(rsCStrAppendChar(pStrB, '\\') != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- rsCStrFinish(pStrB);
- if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) != NULL)
- free(pszGenerated);
- return;
- }
- iLen++; /* reflect the extra character */
- }
- if(rsCStrAppendChar(pStrB, *p) != RS_RET_OK) {
- doSQLEmergencyEscape(*pp, escapeMode);
- rsCStrFinish(pStrB);
- if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) != NULL)
- free(pszGenerated);
- return;
- }
- ++p;
- }
- rsCStrFinish(pStrB);
- if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) == NULL) {
- doSQLEmergencyEscape(*pp, escapeMode);
- return;
- }
-
- if(*pbMustBeFreed)
- free(*pp); /* discard previous value */
-
- *pp = (char*) pszGenerated;
- *pLen = iLen;
- *pbMustBeFreed = 1;
-}
-
/* create a string from the provided iovec. This can
* be called by all functions who need the template
@@ -3860,446 +3226,6 @@ void iovCreate(selector_t *f)
return;
}
-/* This functions converts a template into a string. It should
- * actually be in template.c, but this requires larger re-structuring
- * of the code (because all the property-access functions are static
- * to this module). I have placed it next to the iov*() functions, as
- * it is somewhat similiar in what it does.
- *
- * The function takes a pointer to a template and a pointer to a msg object.
- * It the creates a string based on the template definition. A pointer
- * to that string is returned to the caller. The caller MUST FREE that
- * pointer when it is no longer needed. If the function fails, NULL
- * is returned.
- * If memory allocation fails in this function, we silently return
- * NULL. The reason is that we can not do anything against it. And
- * if we raise an alert, the memory situation might become even
- * worse. So we prefer to let the caller deal with it.
- * rgerhards, 2007-07-03
- */
-static uchar *tplToString(struct template *pTpl, msg_t *pMsg)
-{
- struct templateEntry *pTpe;
- rsCStrObj *pCStr;
- unsigned short bMustBeFreed;
- char *pVal;
- size_t iLenVal;
- rsRetVal iRet;
-
- assert(pTpl != NULL);
- assert(pMsg != NULL);
-
- /* loop through the template. We obtain one value
- * and copy it over to our dynamic string buffer. Then, we
- * free the obtained value (if requested). We continue this
- * loop until we got hold of all values.
- */
- if((pCStr = rsCStrConstruct()) == NULL) {
- dprintf("memory shortage, tplToString failed\n");
- return NULL;
- }
-
- pTpe = pTpl->pEntryRoot;
- while(pTpe != NULL) {
- if(pTpe->eEntryType == CONSTANT) {
- if((iRet = rsCStrAppendStrWithLen(pCStr,
- (uchar *) pTpe->data.constant.pConstant,
- pTpe->data.constant.iLenConstant)
- ) != RS_RET_OK) {
- dprintf("error %d during tplToString()\n", iRet);
- /* it does not make sense to continue now */
- rsCStrDestruct(pCStr);
- return NULL;
- }
- } else if(pTpe->eEntryType == FIELD) {
- pVal = (char*) MsgGetProp(pMsg, pTpe, NULL, &bMustBeFreed);
- iLenVal = strlen(pVal);
- /* we now need to check if we should use SQL option. In this case,
- * we must go over the generated string and escape '\'' characters.
- * rgerhards, 2005-09-22: the option values below look somewhat misplaced,
- * but they are handled in this way because of legacy (don't break any
- * existing thing).
- */
- if(pTpl->optFormatForSQL == 1)
- doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 1);
- else if(pTpl->optFormatForSQL == 2)
- doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 0);
- /* value extracted, so lets copy */
- if((iRet = rsCStrAppendStrWithLen(pCStr, (uchar*) pVal, iLenVal)) != RS_RET_OK) {
- dprintf("error %d during tplToString()\n", iRet);
- /* it does not make sense to continue now */
- rsCStrDestruct(pCStr);
- if(bMustBeFreed)
- free(pVal);
- return NULL;
- }
- if(bMustBeFreed)
- free(pVal);
- }
- pTpe = pTpe->pNext;
- }
-
- /* we are done with the template, now let's convert the result into a
- * "real" (usable) string and discard the helper structures.
- */
- rsCStrFinish(pCStr);
- return rsCStrConvSzStrAndDestruct(pCStr);
-}
-
-
-/* rgerhards 2005-06-21: Try to resolve a size limit
- * situation. This first runs the command, and then
- * checks if we are still above the treshold.
- * returns 0 if ok, 1 otherwise
- * TODO: consider moving the initial check in here, too
- */
-int resolveFileSizeLimit(selector_t *f)
-{
- uchar *pParams;
- uchar *pCmd;
- uchar *p;
- off_t actualFileSize;
- assert(f != NULL);
-
- if(f->f_un.f_file.f_sizeLimitCmd == NULL)
- return 1; /* nothing we can do in this case... */
-
- /* the execProg() below is probably not great, but at least is is
- * fairly secure now. Once we change the way file size limits are
- * handled, we should also revisit how this command is run (and
- * with which parameters). rgerhards, 2007-07-20
- */
- /* we first check if we have command line parameters. We assume this,
- * when we have a space in the program name. If we find it, everything after
- * the space is treated as a single argument.
- */
- if((pCmd = (uchar*)strdup((char*)f->f_un.f_file.f_sizeLimitCmd)) == NULL) {
- /* there is not much we can do - we make syslogd close the file in this case */
- glblHadMemShortage = 1;
- return 1;
- }
-
- for(p = pCmd ; *p && *p != ' ' ; ++p) {
- /* JUST SKIP */
- }
-
- if(*p == ' ') {
- *p = '\0'; /* pretend string-end */
- pParams = p+1;
- } else
- pParams = NULL;
-
- execProg(pCmd, 1, pParams);
-
- f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
- f->f_un.f_file.fCreateMode);
-
- actualFileSize = lseek(f->f_file, 0, SEEK_END);
- if(actualFileSize >= f->f_un.f_file.f_sizeLimit) {
- /* OK, it didn't work out... */
- return 1;
- }
-
- return 0;
-}
-
-
-/* This function deletes an entry from the dynamic file name
- * cache. A pointer to the cache must be passed in as well
- * as the index of the to-be-deleted entry. This index may
- * point to an unallocated entry, in whcih case the
- * function immediately returns. Parameter bFreeEntry is 1
- * if the entry should be free()ed and 0 if not.
- */
-static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
-{
- assert(pCache != NULL);
-
- if(pCache[iEntry] == NULL)
- return;
-
- dprintf("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
- pCache[iEntry]->pName == NULL ? "[OPEN FAILED]" : (char*)pCache[iEntry]->pName);
- /* if the name is NULL, this is an improperly initilized entry which
- * needs to be discarded. In this case, neither the file is to be closed
- * not the name to be freed.
- */
- if(pCache[iEntry]->pName != NULL) {
- close(pCache[iEntry]->fd);
- free(pCache[iEntry]->pName);
- pCache[iEntry]->pName = NULL;
- }
-
- if(bFreeEntry) {
- free(pCache[iEntry]);
- pCache[iEntry] = NULL;
- }
-}
-
-
-/* This function frees the dynamic file name cache.
- */
-static void dynaFileFreeCache(selector_t *f)
-{
- register int i;
- assert(f != NULL);
-
- for(i = 0 ; i < f->f_un.f_file.iCurrCacheSize ; ++i) {
- dynaFileDelCacheEntry(f->f_un.f_file.dynCache, i, 1);
- }
-
- free(f->f_un.f_file.dynCache);
-}
-
-
-/* This function handles dynamic file names. It generates a new one
- * based on the current message, checks if that file is already open
- * and, if not, does everything needed to switch to the new one.
- * Function returns 0 if all went well and non-zero otherwise.
- * On successful return f->f_file must point to the correct file to
- * be written.
- * This is a helper to writeFile(). rgerhards, 2007-07-03
- */
-static int prepareDynFile(selector_t *f)
-{
- uchar *newFileName;
- time_t ttOldest; /* timestamp of oldest element */
- int iOldest;
- int i;
- int iFirstFree;
- dynaFileCacheEntry **pCache;
-
- assert(f != NULL);
- if((newFileName = tplToString(f->f_un.f_file.pTpl, f->f_pMsg)) == NULL) {
- /* memory shortage - there is nothing we can do to resolve it.
- * We silently ignore it, this is probably the best we can do.
- */
- glblHadMemShortage = TRUE;
- dprintf("prepareDynfile(): could not create file name, discarding this request\n");
- return -1;
- }
-
- pCache = f->f_un.f_file.dynCache;
-
- /* first check, if we still have the current file
- * I *hope* this will be a performance enhancement.
- */
- if( (f->f_un.f_file.iCurrElt != -1)
- && !strcmp((char*) newFileName,
- (char*) pCache[f->f_un.f_file.iCurrElt])) {
- /* great, we are all set */
- free(newFileName);
- pCache[f->f_un.f_file.iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */
- return 0;
- }
-
- /* ok, no luck. Now let's search the table if we find a matching spot.
- * While doing so, we also prepare for creation of a new one.
- */
- iFirstFree = -1; /* not yet found */
- iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */
- ttOldest = time(NULL) + 1; /* there must always be an older one */
- for(i = 0 ; i < f->f_un.f_file.iCurrCacheSize ; ++i) {
- if(pCache[i] == NULL) {
- if(iFirstFree == -1)
- iFirstFree = i;
- } else { /* got an element, let's see if it matches */
- if(!strcmp((char*) newFileName, (char*) pCache[i]->pName)) {
- /* we found our element! */
- f->f_file = pCache[i]->fd;
- f->f_un.f_file.iCurrElt = i;
- free(newFileName);
- pCache[i]->lastUsed = time(NULL); /* update timestamp for LRU */
- return 0;
- }
- /* did not find it - so lets keep track of the counters for LRU */
- if(pCache[i]->lastUsed < ttOldest) {
- ttOldest = pCache[i]->lastUsed;
- iOldest = i;
- }
- }
- }
-
- /* we have not found an entry */
- if(iFirstFree == -1 && (f->f_un.f_file.iCurrCacheSize < f->f_un.f_file.iDynaFileCacheSize)) {
- /* there is space left, so set it to that index */
- iFirstFree = f->f_un.f_file.iCurrCacheSize++;
- }
-
- if(iFirstFree == -1) {
- dynaFileDelCacheEntry(pCache, iOldest, 0);
- iFirstFree = iOldest; /* this one *is* now free ;) */
- } else {
- /* we need to allocate memory for the cache structure */
- pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry));
- if(pCache[iFirstFree] == NULL) {
- glblHadMemShortage = TRUE;
- dprintf("prepareDynfile(): could not alloc mem, discarding this request\n");
- free(newFileName);
- return -1;
- }
- }
-
- /* Ok, we finally can open the file */
- if(access((char*)newFileName, F_OK) == 0) {
- /* file already exists */
- f->f_file = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
- f->f_un.f_file.fCreateMode);
- } else {
- /* file does not exist, create it (and eventually parent directories */
- if(f->f_un.f_file.bCreateDirs) {
- /* we fist need to create parent dirs if they are missing
- * We do not report any errors here ourselfs but let the code
- * fall through to error handler below.
- */
- if(makeFileParentDirs(newFileName, strlen((char*)newFileName),
- f->f_un.f_file.fDirCreateMode, f->f_un.f_file.dirUID,
- f->f_un.f_file.dirGID, f->f_un.f_file.bFailOnChown) == 0) {
- f->f_file = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
- f->f_un.f_file.fCreateMode);
- if(f->f_file != -1) {
- /* check and set uid/gid */
- if(f->f_un.f_file.fileUID != (uid_t)-1 || f->f_un.f_file.fileGID != (gid_t) -1) {
- /* we need to set owner/group */
- if(fchown(f->f_file, f->f_un.f_file.fileUID,
- f->f_un.f_file.fileGID) != 0) {
- if(f->f_un.f_file.bFailOnChown) {
- int eSave = errno;
- close(f->f_file);
- f->f_file = -1;
- errno = eSave;
- }
- /* we will silently ignore the chown() failure
- * if configured to do so.
- */
- }
- }
- }
- }
- }
- }
-
- /* file is either open now or an error state set */
- if(f->f_file == -1) {
- /* do not report anything if the message is an internally-generated
- * message. Otherwise, we could run into a never-ending loop. The bad
- * news is that we also lose errors on startup messages, but so it is.
- */
- if(f->f_pMsg->msgFlags & INTERNAL_MSG)
- dprintf("Could not open dynaFile, discarding message\n");
- else
- logerrorSz("Could not open dynamic file '%s' - discarding message", (char*)newFileName);
- free(newFileName);
- dynaFileDelCacheEntry(pCache, iFirstFree, 1);
- return -1;
- }
-
- pCache[iFirstFree]->fd = f->f_file;
- pCache[iFirstFree]->pName = newFileName;
- pCache[iFirstFree]->lastUsed = time(NULL);
- f->f_un.f_file.iCurrElt = iFirstFree;
- dprintf("Added new entry %d for file cache, file '%s'.\n",
- iFirstFree, newFileName);
-
- return 0;
-}
-
-
-/* rgerhards 2004-11-11: write to a file output. This
- * will be called for all outputs using file semantics,
- * for example also for pipes.
- */
-void writeFile(selector_t *f)
-{
- off_t actualFileSize;
-
- assert(f != NULL);
-
- /* first check if we have a dynamic file name and, if so,
- * check if it still is ok or a new file needs to be created
- */
- if(f->f_un.f_file.bDynamicName) {
- if(prepareDynFile(f) != 0)
- return;
- }
-
- /* create the message based on format specified */
- iovCreate(f);
-again:
- /* check if we have a file size limit and, if so,
- * obey to it.
- */
- if(f->f_un.f_file.f_sizeLimit != 0) {
- actualFileSize = lseek(f->f_file, 0, SEEK_END);
- if(actualFileSize >= f->f_un.f_file.f_sizeLimit) {
- char errMsg[256];
- /* for now, we simply disable a file once it is
- * beyond the maximum size. This is better than having
- * us aborted by the OS... rgerhards 2005-06-21
- */
- (void) close(f->f_file);
- /* try to resolve the situation */
- if(resolveFileSizeLimit(f) != 0) {
- /* didn't work out, so disable... */
- f->f_type = F_UNUSED;
- snprintf(errMsg, sizeof(errMsg),
- "no longer writing to file %s; grown beyond configured file size of %lld bytes, actual size %lld - configured command did not resolve situation",
- f->f_un.f_file.f_fname, (long long) f->f_un.f_file.f_sizeLimit, (long long) actualFileSize);
- errno = 0;
- logerror(errMsg);
- return;
- } else {
- snprintf(errMsg, sizeof(errMsg),
- "file %s had grown beyond configured file size of %lld bytes, actual size was %lld - configured command resolved situation",
- f->f_un.f_file.f_fname, (long long) f->f_un.f_file.f_sizeLimit, (long long) actualFileSize);
- errno = 0;
- logerror(errMsg);
- }
- }
- }
-
- if (writev(f->f_file, f->f_iov, f->f_iIovUsed) < 0) {
- int e = errno;
-
- /* If a named pipe is full, just ignore it for now
- - mrn 24 May 96 */
- if (f->f_type == F_PIPE && e == EAGAIN)
- return;
-
- /* If the filesystem is filled up, just ignore
- * it for now and continue writing when possible
- * based on patch for sysklogd by Martin Schulze on 2007-05-24
- */
- if (f->f_type == F_FILE && e == ENOSPC)
- return;
-
- (void) close(f->f_file);
- /*
- * Check for EBADF on TTY's due to vhangup()
- * Linux uses EIO instead (mrn 12 May 96)
- */
- if ((f->f_type == F_TTY || f->f_type == F_CONSOLE)
-#ifdef linux
- && e == EIO) {
-#else
- && e == EBADF) {
-#endif
- f->f_file = open(f->f_un.f_file.f_fname, O_WRONLY|O_APPEND|O_NOCTTY);
- if (f->f_file < 0) {
- f->f_type = F_UNUSED;
- logerror(f->f_un.f_file.f_fname);
- } else {
- untty();
- goto again;
- }
- } else {
- f->f_type = F_UNUSED;
- errno = e;
- logerror(f->f_un.f_file.f_fname);
- }
- } else if (f->f_flags & SYNC_FILE)
- fsync(f->f_file);
-}
/* rgerhards 2004-11-09: fprintlog() is the actual driver for
* the output channel. It receives the channel description (f) as
@@ -4373,12 +3299,7 @@ void fprintlog(register selector_t *f)
case F_PIPE:
printf(" (%s)\n", f->f_un.f_file.f_fname);
f->f_time = now; /* we need this for message repeation processing */
- /* f->f_file == -1 is an indicator that the we couldn't
- * open the file at startup. For dynaFiles, this is ok,
- * all others are doomed.
- */
- if(f->f_un.f_file.bDynamicName || (f->f_file != -1))
- writeFile(f);
+ doActionFile(f);
break;
case F_USERS:
@@ -4396,7 +3317,7 @@ void fprintlog(register selector_t *f)
case F_SHELL: /* shell support by bkalkbrenner 2005-09-20 */
f->f_time = now;
- doActionShell(f, now);
+ doActionShell(f);
break;
} /* switch */
@@ -5288,10 +4209,7 @@ static void init()
case F_PIPE:
case F_TTY:
case F_CONSOLE:
- if(f->f_un.f_file.bDynamicName) {
- dynaFileFreeCache(f);
- } else
- close(f->f_file);
+ freeInstanceFile(f);
break;
case F_FORW:
freeaddrinfo(f->f_un.f_forw.f_addr);
diff --git a/syslogd.h b/syslogd.h
index 38127c5f..05df1294 100644
--- a/syslogd.h
+++ b/syslogd.h
@@ -56,6 +56,14 @@
* rgerhards, 2005-07-26
*/
#endif
+
+/* Flags to logmsg().
+ */
+#define INTERNAL_MSG 0x001 /* msg generated by logmsgInternal() --> special handling */
+#define SYNC_FILE 0x002 /* do fsync on file after printing */
+#define ADDDATE 0x004 /* add a date to the message */
+#define MARK 0x008 /* this message is a mark */
+
#if defined(__GLIBC__)
#define dprintf mydprintf
#endif /* __GLIBC__ */
@@ -75,6 +83,7 @@ int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf);
void iovCreate(selector_t *f);
char *iovAsString(selector_t *f);
+void untty(void);
extern int glblHadMemShortage; /* indicates if we had memory shortage some time during the run */
extern syslogCODE rs_prioritynames[];
diff --git a/template.c b/template.c
index f8a0d0ca..38cdf384 100644
--- a/template.c
+++ b/template.c
@@ -16,13 +16,224 @@
#include <assert.h>
#include "rsyslog.h"
#include "stringbuf.h"
+#include "syslogd-types.h"
#include "template.h"
+#include "msg.h"
#include "syslogd.h"
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 */
+/* This functions converts a template into a string. It should
+ * actually be in template.c, but this requires larger re-structuring
+ * of the code (because all the property-access functions are static
+ * to this module). I have placed it next to the iov*() functions, as
+ * it is somewhat similiar in what it does.
+ *
+ * The function takes a pointer to a template and a pointer to a msg object.
+ * It the creates a string based on the template definition. A pointer
+ * to that string is returned to the caller. The caller MUST FREE that
+ * pointer when it is no longer needed. If the function fails, NULL
+ * is returned.
+ * If memory allocation fails in this function, we silently return
+ * NULL. The reason is that we can not do anything against it. And
+ * if we raise an alert, the memory situation might become even
+ * worse. So we prefer to let the caller deal with it.
+ * rgerhards, 2007-07-03
+ */
+uchar *tplToString(struct template *pTpl, msg_t *pMsg)
+{
+ struct templateEntry *pTpe;
+ rsCStrObj *pCStr;
+ unsigned short bMustBeFreed;
+ char *pVal;
+ size_t iLenVal;
+ rsRetVal iRet;
+
+ assert(pTpl != NULL);
+ assert(pMsg != NULL);
+
+ /* loop through the template. We obtain one value
+ * and copy it over to our dynamic string buffer. Then, we
+ * free the obtained value (if requested). We continue this
+ * loop until we got hold of all values.
+ */
+ if((pCStr = rsCStrConstruct()) == NULL) {
+ dprintf("memory shortage, tplToString failed\n");
+ return NULL;
+ }
+
+ pTpe = pTpl->pEntryRoot;
+ while(pTpe != NULL) {
+ if(pTpe->eEntryType == CONSTANT) {
+ if((iRet = rsCStrAppendStrWithLen(pCStr,
+ (uchar *) pTpe->data.constant.pConstant,
+ pTpe->data.constant.iLenConstant)
+ ) != RS_RET_OK) {
+ dprintf("error %d during tplToString()\n", iRet);
+ /* it does not make sense to continue now */
+ rsCStrDestruct(pCStr);
+ return NULL;
+ }
+ } else if(pTpe->eEntryType == FIELD) {
+ pVal = (char*) MsgGetProp(pMsg, pTpe, NULL, &bMustBeFreed);
+ iLenVal = strlen(pVal);
+ /* we now need to check if we should use SQL option. In this case,
+ * we must go over the generated string and escape '\'' characters.
+ * rgerhards, 2005-09-22: the option values below look somewhat misplaced,
+ * but they are handled in this way because of legacy (don't break any
+ * existing thing).
+ */
+ if(pTpl->optFormatForSQL == 1)
+ doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 1);
+ else if(pTpl->optFormatForSQL == 2)
+ doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 0);
+ /* value extracted, so lets copy */
+ if((iRet = rsCStrAppendStrWithLen(pCStr, (uchar*) pVal, iLenVal)) != RS_RET_OK) {
+ dprintf("error %d during tplToString()\n", iRet);
+ /* it does not make sense to continue now */
+ rsCStrDestruct(pCStr);
+ if(bMustBeFreed)
+ free(pVal);
+ return NULL;
+ }
+ if(bMustBeFreed)
+ free(pVal);
+ }
+ pTpe = pTpe->pNext;
+ }
+
+ /* we are done with the template, now let's convert the result into a
+ * "real" (usable) string and discard the helper structures.
+ */
+ rsCStrFinish(pCStr);
+ return rsCStrConvSzStrAndDestruct(pCStr);
+}
+
+/* Helper to doSQLEscape. This is called if doSQLEscape
+ * runs out of memory allocating the escaped string.
+ * Then we are in trouble. We can
+ * NOT simply return the unmodified string because this
+ * may cause SQL injection. But we also can not simply
+ * abort the run, this would be a DoS. I think an appropriate
+ * measure is to remove the dangerous \' characters. We
+ * replace them by \", which will break the message and
+ * signatures eventually present - but this is the
+ * best thing we can do now (or does anybody
+ * have a better idea?). rgerhards 2004-11-23
+ * added support for "escapeMode" (so doSQLEscape for details).
+ * if mode = 1, then backslashes are changed to slashes.
+ * rgerhards 2005-09-22
+ */
+static void doSQLEmergencyEscape(register char *p, int escapeMode)
+{
+ while(*p) {
+ if(*p == '\'')
+ *p = '"';
+ else if((escapeMode == 1) && (*p == '\\'))
+ *p = '/';
+ ++p;
+ }
+}
+
+
+/* SQL-Escape a string. Single quotes are found and
+ * replaced by two of them. A new buffer is allocated
+ * for the provided string and the provided buffer is
+ * freed. The length is updated. Parameter pbMustBeFreed
+ * is set to 1 if a new buffer is allocated. Otherwise,
+ * it is left untouched.
+ * --
+ * We just discovered a security issue. MySQL is so
+ * "smart" to not only support the standard SQL mechanism
+ * for escaping quotes, but to also provide its own (using
+ * c-type syntax with backslashes). As such, it is actually
+ * possible to do sql injection via rsyslogd. The cure is now
+ * to escape backslashes, too. As we have found on the web, some
+ * other databases seem to be similar "smart" (why do we have standards
+ * at all if they are violated without any need???). Even better, MySQL's
+ * smartness depends on config settings. So we add a new option to this
+ * function that allows the caller to select if they want to standard or
+ * "smart" encoding ;)
+ * new parameter escapeMode is 0 - standard sql, 1 - "smart" engines
+ * 2005-09-22 rgerhards
+ */
+void doSQLEscape(char **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode)
+{
+ char *p;
+ int iLen;
+ rsCStrObj *pStrB;
+ uchar *pszGenerated;
+
+ assert(pp != NULL);
+ assert(*pp != NULL);
+ assert(pLen != NULL);
+ assert(pbMustBeFreed != NULL);
+
+ /* first check if we need to do anything at all... */
+ if(escapeMode == 0)
+ for(p = *pp ; *p && *p != '\'' ; ++p)
+ ;
+ else
+ for(p = *pp ; *p && *p != '\'' && *p != '\\' ; ++p)
+ ;
+ /* 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! */
+
+ p = *pp;
+ iLen = *pLen;
+ if((pStrB = rsCStrConstruct()) == NULL) {
+ /* oops - no mem ... Do emergency... */
+ doSQLEmergencyEscape(p, escapeMode);
+ return;
+ }
+
+ while(*p) {
+ if(*p == '\'') {
+ if(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\') != RS_RET_OK) {
+ doSQLEmergencyEscape(*pp, escapeMode);
+ rsCStrFinish(pStrB);
+ if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) != NULL)
+ free(pszGenerated);
+ return;
+ }
+ iLen++; /* reflect the extra character */
+ } else if((escapeMode == 1) && (*p == '\\')) {
+ if(rsCStrAppendChar(pStrB, '\\') != RS_RET_OK) {
+ doSQLEmergencyEscape(*pp, escapeMode);
+ rsCStrFinish(pStrB);
+ if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) != NULL)
+ free(pszGenerated);
+ return;
+ }
+ iLen++; /* reflect the extra character */
+ }
+ if(rsCStrAppendChar(pStrB, *p) != RS_RET_OK) {
+ doSQLEmergencyEscape(*pp, escapeMode);
+ rsCStrFinish(pStrB);
+ if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) != NULL)
+ free(pszGenerated);
+ return;
+ }
+ ++p;
+ }
+ rsCStrFinish(pStrB);
+ if((pszGenerated = rsCStrConvSzStrAndDestruct(pStrB)) == NULL) {
+ doSQLEmergencyEscape(*pp, escapeMode);
+ return;
+ }
+
+ if(*pbMustBeFreed)
+ free(*pp); /* discard previous value */
+
+ *pp = (char*) pszGenerated;
+ *pLen = iLen;
+ *pbMustBeFreed = 1;
+}
+
/* Constructs a template entry object. Returns pointer to it
* or NULL (if it fails). Pointer to associated template list entry
* must be provided.
diff --git a/template.h b/template.h
index 3667173b..d749595a 100644
--- a/template.h
+++ b/template.h
@@ -7,6 +7,7 @@
#ifndef TEMPLATE_H_INCLUDED
#define TEMPLATE_H_INCLUDED 1
+
#ifdef FEATURE_REGEXP
/* Include regular expressions */
#include <regex.h>
@@ -34,6 +35,8 @@ enum tplFormatTypes { tplFmtDefault = 0, tplFmtMySQLDate = 1,
tplFmtRFC3164Date = 2, tplFmtRFC3339Date = 3 };
enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseConvLower = 2 };
+#include "msg.h"
+
/* a specific parse entry */
struct templateEntry {
struct templateEntry *pNext;
@@ -73,6 +76,8 @@ void tplDeleteAll(void);
void tplDeleteNew(void);
void tplPrintList(void);
void tplLastStaticInit(struct template *tpl);
+uchar *tplToString(struct template *pTpl, msg_t *pMsg);
+void doSQLEscape(char **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode);
#endif /* #ifndef TEMPLATE_H_INCLUDED */
/*