summaryrefslogtreecommitdiffstats
path: root/runtime/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/conf.c')
-rw-r--r--runtime/conf.c640
1 files changed, 640 insertions, 0 deletions
diff --git a/runtime/conf.c b/runtime/conf.c
new file mode 100644
index 0000000..5eee37e
--- /dev/null
+++ b/runtime/conf.c
@@ -0,0 +1,640 @@
+/* The config file handler (not yet a real object)
+ *
+ * This file is based on an excerpt from syslogd.c, which dates back
+ * much later. I began the file on 2008-02-19 as part of the modularization
+ * effort. Over time, a clean abstration will become even more important
+ * because the config file handler will by dynamically be loaded and be
+ * kept in memory only as long as the config file is actually being
+ * processed. Thereafter, it shall be unloaded. -- rgerhards
+ * Please note that the original syslogd.c source was under BSD license
+ * at the time of the rsyslog fork from sysklogd.
+ *
+ * Copyright 2008-2016 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define CFGLNSIZ 64*1024 /* the maximum size of a configuration file line, after re-combination */
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <dirent.h>
+#include <glob.h>
+#include <sys/types.h>
+#ifdef HAVE_LIBGEN_H
+# ifndef OS_SOLARIS
+# include <libgen.h>
+# endif
+#endif
+
+#include "rsyslog.h"
+#include "dirty.h"
+#include "parse.h"
+#include "action.h"
+#include "template.h"
+#include "cfsysline.h"
+#include "modules.h"
+#include "outchannel.h"
+#include "stringbuf.h"
+#include "conf.h"
+#include "stringbuf.h"
+#include "srUtils.h"
+#include "errmsg.h"
+#include "net.h"
+#include "ruleset.h"
+#include "rsconf.h"
+#include "unicode-helper.h"
+#include "rainerscript.h"
+
+#ifdef OS_SOLARIS
+# define NAME_MAX MAXNAMELEN
+#endif
+
+/* forward definitions */
+
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(module)
+DEFobjCurrIf(net)
+DEFobjCurrIf(ruleset)
+
+int bConfStrictScoping = 0; /* force strict scoping during config processing? */
+
+
+/* The following module-global variables are used for building
+ * tag and host selector lines during startup and config reload.
+ * This is stored as a global variable pool because of its ease. It is
+ * also fairly compatible with multi-threading as the stratup code must
+ * be run in a single thread anyways. So there can be no race conditions.
+ * rgerhards 2005-10-18
+ */
+EHostnameCmpMode eDfltHostnameCmpMode = HN_NO_COMP;
+cstr_t *pDfltHostnameCmp = NULL;
+cstr_t *pDfltProgNameCmp = NULL;
+
+
+/* process a $ModLoad config line. */
+static rsRetVal
+doModLoad(uchar **pp, __attribute__((unused)) void* pVal)
+{
+ DEFiRet;
+ uchar szName[512];
+ uchar *pModName;
+
+ assert(pp != NULL);
+ assert(*pp != NULL);
+
+ skipWhiteSpace(pp); /* skip over any whitespace */
+ if(getSubString(pp, (char*) szName, sizeof(szName), ' ') != 0) {
+ LogError(0, RS_RET_NOT_FOUND, "could not extract module name");
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+ skipWhiteSpace(pp); /* skip over any whitespace */
+
+ /* this below is a quick and dirty hack to provide compatibility with the
+ * $ModLoad MySQL forward compatibility statement. This needs to be supported
+ * for legacy format.
+ */
+ if(!strcmp((char*) szName, "MySQL"))
+ pModName = (uchar*) "ommysql.so";
+ else
+ pModName = szName;
+
+ CHKiRet(module.Load(pModName, 1, NULL));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* remove leading spaces from name; this "fixes" some anomalies in
+ * getSubString(), but I was not brave enough to fix the former as
+ * it has many other callers... -- rgerhards, 2013-05-27
+ */
+static void
+ltrim(char *src)
+{
+ char *dst = src;
+ while(isspace(*src))
+ ++src; /*SKIP*/;
+ if(dst != src) {
+ while(*src != '\0')
+ *dst++ = *src++;
+ *dst = '\0';
+ }
+}
+
+/* parse and interpret a $-config line that starts with
+ * a name (this is common code). It is parsed to the name
+ * and then the proper sub-function is called to handle
+ * the actual directive.
+ * rgerhards 2004-11-17
+ * rgerhards 2005-06-21: previously only for templates, now
+ * generalized.
+ */
+static rsRetVal
+doNameLine(uchar **pp, void* pVal)
+{
+ DEFiRet;
+ uchar *p;
+ enum eDirective eDir;
+ char szName[128];
+
+ assert(pp != NULL);
+ p = *pp;
+ assert(p != NULL);
+
+ PRAGMA_DIAGNOSTIC_PUSH
+ PRAGMA_IGNORE_Wvoid_pointer_to_enum_cast;
+ /* this time, pVal actually is NOT a pointer! It is save to case, as
+ * the enum was written to it, so there can be no loss of bits (ptr is larger).
+ */
+ eDir = (enum eDirective) pVal;
+ PRAGMA_DIAGNOSTIC_POP
+
+ if(getSubString(&p, szName, sizeof(szName), ',') != 0) {
+ LogError(0, RS_RET_NOT_FOUND, "Invalid config line: could not extract name - line ignored");
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+ ltrim(szName);
+ if(*p == ',')
+ ++p; /* comma was eaten */
+
+ /* we got the name - now we pass name & the rest of the string
+ * to the subfunction. It makes no sense to do further
+ * parsing here, as this is in close interaction with the
+ * respective subsystem. rgerhards 2004-11-17
+ */
+
+ switch(eDir) {
+ case DIR_TEMPLATE:
+ tplAddLine(loadConf, szName, &p);
+ break;
+ case DIR_OUTCHANNEL:
+ ochAddLine(szName, &p);
+ break;
+ case DIR_ALLOWEDSENDER:
+ net.addAllowedSenderLine(szName, &p);
+ break;
+ default:/* we do this to avoid compiler warning - not all
+ * enum values call this function, so an incomplete list
+ * is quite ok (but then we should not run into this code,
+ * so at least we log a debug warning).
+ */
+ dbgprintf("INTERNAL ERROR: doNameLine() called with invalid eDir %d.\n",
+ eDir);
+ break;
+ }
+
+ *pp = p;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Parse and interpret a system-directive in the config line
+ * A system directive is one that starts with a "$" sign. It offers
+ * extended configuration parameters.
+ * 2004-11-17 rgerhards
+ */
+static rsRetVal
+cfsysline(uchar *p)
+{
+ DEFiRet;
+ uchar szCmd[64];
+
+ assert(p != NULL);
+ errno = 0;
+ if(getSubString(&p, (char*) szCmd, sizeof(szCmd), ' ') != 0) {
+ LogError(0, RS_RET_NOT_FOUND, "Invalid $-configline "
+ "- could not extract command - line ignored\n");
+ ABORT_FINALIZE(RS_RET_NOT_FOUND);
+ }
+
+ /* we now try and see if we can find the command in the registered
+ * list of cfsysline handlers. -- rgerhards, 2007-07-31
+ */
+ CHKiRet(processCfSysLineCommand(szCmd, &p));
+
+ /* now check if we have some extra characters left on the line - that
+ * should not be the case. Whitespace is OK, but everything else should
+ * trigger a warning (that may be an indication of undesired behaviour).
+ * An exception, of course, are comments (starting with '#').
+ * rgerhards, 2007-07-04
+ */
+ skipWhiteSpace(&p);
+
+ if(*p && *p != '#') { /* we have a non-whitespace, so let's complain */
+ LogError(0, NO_ERRCODE,
+ "error: extra characters in config line ignored: '%s'", p);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Helper to cfline() and its helpers. Parses a template name
+ * from an "action" line. Must be called with the Line pointer
+ * pointing to the first character after the semicolon.
+ * rgerhards 2004-11-19
+ * changed function to work with OMSR. -- rgerhards, 2007-07-27
+ * the default template is to be used when no template is specified.
+ */
+rsRetVal cflineParseTemplateName(uchar** pp, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *dfltTplName)
+{
+ uchar *p;
+ uchar *tplName = NULL;
+ cstr_t *pStrB = NULL;
+ DEFiRet;
+
+ assert(pp != NULL);
+ assert(*pp != NULL);
+ assert(pOMSR != NULL);
+
+ p =*pp;
+ /* a template must follow - search it and complain, if not found */
+ skipWhiteSpace(&p);
+ if(*p == ';')
+ ++p; /* eat it */
+ else if(*p != '\0' && *p != '#') {
+ LogError(0, RS_RET_ERR, "invalid character in selector line - ';template' expected");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ skipWhiteSpace(&p); /* go to begin of template name */
+
+ if(*p == '\0' || *p == '#') {
+ /* no template specified, use the default */
+ /* TODO: check NULL ptr */
+ tplName = (uchar*) strdup((char*)dfltTplName);
+ } else {
+ /* template specified, pick it up */
+ CHKiRet(cstrConstruct(&pStrB));
+
+ /* now copy the string */
+ while(*p && *p != '#' && !isspace((int) *p)) {
+ CHKiRet(cstrAppendChar(pStrB, *p));
+ ++p;
+ }
+ cstrFinalize(pStrB);
+ CHKiRet(cstrConvSzStrAndDestruct(&pStrB, &tplName, 0));
+ }
+
+ CHKiRet(OMSRsetEntry(pOMSR, iEntry, tplName, iTplOpts));
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ free(tplName);
+ if(pStrB != NULL)
+ cstrDestruct(&pStrB);
+ }
+
+ *pp = p;
+
+ RETiRet;
+}
+
+/* Helper to cfline(). Parses a file name up until the first
+ * comma and then looks for the template specifier. Tries
+ * to find that template.
+ * rgerhards 2004-11-18
+ * parameter pFileName must point to a buffer large enough
+ * to hold the largest possible filename.
+ * rgerhards, 2007-07-25
+ * updated to include OMSR pointer -- rgerhards, 2007-07-27
+ * updated to include template name -- rgerhards, 2008-03-28
+ * rgerhards, 2010-01-19: file names end at the first space
+ */
+rsRetVal
+cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts, uchar *pszTpl)
+{
+ register uchar *pName;
+ int i;
+ DEFiRet;
+
+ assert(pOMSR != NULL);
+
+ pName = pFileName;
+ i = 1; /* we start at 1 so that we reseve space for the '\0'! */
+ while(*p && *p != ';' && *p != ' ' && i < MAXFNAME) {
+ *pName++ = *p++;
+ ++i;
+ }
+ *pName = '\0';
+
+ iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, pszTpl);
+
+ RETiRet;
+}
+
+
+/* Decode a traditional PRI filter */
+/* GPLv3 - stems back to sysklogd */
+rsRetVal DecodePRIFilter(uchar *pline, uchar pmask[])
+{
+ uchar *p;
+ register uchar *q;
+ register int i, i2;
+ uchar *bp;
+ int pri; /* this MUST be int, as -1 is used to convey an error state */
+ int singlpri = 0;
+ int ignorepri = 0;
+ uchar buf[2048]; /* buffer for facility and priority names */
+ uchar xbuf[200];
+ DEFiRet;
+
+ assert(pline != NULL);
+
+ dbgprintf("Decoding traditional PRI filter '%s'\n", pline);
+
+ for (i = 0; i <= LOG_NFACILITIES; i++) {
+ pmask[i] = TABLE_NOPRI;
+ }
+
+ /* scan through the list of selectors */
+ for (p = pline; *p && *p != '\t' && *p != ' ';) {
+ /* find the end of this facility name list */
+ for (q = p; *q && *q != '\t' && *q++ != '.'; )
+ continue;
+
+ /* collect priority name */
+ for (bp = buf; *q && !strchr("\t ,;", *q) && bp < buf+sizeof(buf)-1 ; )
+ *bp++ = *q++;
+ *bp = '\0';
+
+ /* skip cruft */
+ if(*q) {
+ while (strchr(",;", *q))
+ q++;
+ }
+
+ /* decode priority name */
+ if ( *buf == '!' ) {
+ ignorepri = 1;
+ /* copy below is ok, we can NOT go off the allocated area */
+ for (bp=buf; *(bp+1); bp++)
+ *bp=*(bp+1);
+ *bp='\0';
+ } else {
+ ignorepri = 0;
+ }
+ if ( *buf == '=' ) {
+ singlpri = 1;
+ pri = decodeSyslogName(&buf[1], syslogPriNames);
+ }
+ else { singlpri = 0;
+ pri = decodeSyslogName(buf, syslogPriNames);
+ }
+
+ if (pri < 0) {
+ snprintf((char*) xbuf, sizeof(xbuf), "unknown priority name \"%s\"", buf);
+ LogError(0, RS_RET_ERR, "%s", xbuf);
+ return RS_RET_ERR;
+ }
+
+ /* scan facilities */
+ while (*p && !strchr("\t .;", *p)) {
+ for (bp = buf; *p && !strchr("\t ,;.", *p) && bp < buf+sizeof(buf)-1 ; )
+ *bp++ = *p++;
+ *bp = '\0';
+ if (*buf == '*') {
+ for (i = 0; i <= LOG_NFACILITIES; i++) {
+ if ( pri == INTERNAL_NOPRI ) {
+ if ( ignorepri )
+ pmask[i] = TABLE_ALLPRI;
+ else
+ pmask[i] = TABLE_NOPRI;
+ }
+ else if ( singlpri ) {
+ if ( ignorepri )
+ pmask[i] &= ~(1<<pri);
+ else
+ pmask[i] |= (1<<pri);
+ } else {
+ if ( pri == TABLE_ALLPRI ) {
+ if ( ignorepri )
+ pmask[i] = TABLE_NOPRI;
+ else
+ pmask[i] = TABLE_ALLPRI;
+ } else {
+ if ( ignorepri )
+ for (i2= 0; i2 <= pri; ++i2)
+ pmask[i] &= ~(1<<i2);
+ else
+ for (i2= 0; i2 <= pri; ++i2)
+ pmask[i] |= (1<<i2);
+ }
+ }
+ }
+ } else {
+ i = decodeSyslogName(buf, syslogFacNames);
+ if (i < 0) {
+
+ snprintf((char*) xbuf, sizeof(xbuf), "unknown facility name \"%s\"", buf);
+ LogError(0, RS_RET_ERR, "%s", xbuf);
+ return RS_RET_ERR;
+ }
+
+ if ( pri == INTERNAL_NOPRI ) {
+ if ( ignorepri )
+ pmask[i >> 3] = TABLE_ALLPRI;
+ else
+ pmask[i >> 3] = TABLE_NOPRI;
+ } else if ( singlpri ) {
+ if ( ignorepri )
+ pmask[i >> 3] &= ~(1<<pri);
+ else
+ pmask[i >> 3] |= (1<<pri);
+ } else {
+ if ( pri == TABLE_ALLPRI ) {
+ if ( ignorepri )
+ pmask[i >> 3] = TABLE_NOPRI;
+ else
+ pmask[i >> 3] = TABLE_ALLPRI;
+ } else {
+ if ( ignorepri )
+ for (i2= 0; i2 <= pri; ++i2)
+ pmask[i >> 3] &= ~(1<<i2);
+ else
+ for (i2= 0; i2 <= pri; ++i2)
+ pmask[i >> 3] |= (1<<i2);
+ }
+ }
+ }
+ while (*p == ',' || *p == ' ')
+ p++;
+ }
+
+ p = q;
+ }
+
+ RETiRet;
+}
+
+
+/* process the action part of a selector line
+ * rgerhards, 2007-08-01
+ */
+rsRetVal cflineDoAction(rsconf_t *conf, uchar **p, action_t **ppAction)
+{
+ modInfo_t *pMod;
+ cfgmodules_etry_t *node;
+ omodStringRequest_t *pOMSR;
+ int bHadWarning = 0;
+ action_t *pAction = NULL;
+ void *pModData;
+ DEFiRet;
+
+ assert(p != NULL);
+ assert(ppAction != NULL);
+
+ /* loop through all modules and see if one picks up the line */
+ node = module.GetNxtCnfType(conf, NULL, eMOD_OUT);
+ /* Note: clang static analyzer reports that node maybe == NULL. However, this is
+ * not possible, because we have the built-in output modules which are always
+ * present. Anyhow, we guard this by an assert. -- rgerhards, 2010-12-16
+ */
+ assert(node != NULL);
+ while(node != NULL) {
+ pOMSR = NULL;
+ pMod = node->pMod;
+ iRet = pMod->mod.om.parseSelectorAct(p, &pModData, &pOMSR);
+ dbgprintf("tried selector action for %s: %d\n", module.GetName(pMod), iRet);
+ if(iRet == RS_RET_OK_WARN) {
+ bHadWarning = 1;
+ iRet = RS_RET_OK;
+ }
+ if(iRet == RS_RET_OK) {
+ if((iRet = addAction(&pAction, pMod, pModData, pOMSR, NULL, NULL)) == RS_RET_OK) {
+ /* here check if the module is compatible with select features
+ * (currently, we have no such features!) */
+ conf->actions.nbrActions++; /* one more active action! */
+ }
+ break;
+ } else if(iRet != RS_RET_CONFLINE_UNPROCESSED) {
+ /* In this case, the module would have handled the config
+ * line, but some error occurred while doing so. This error should
+ * already by reported by the module. We do not try any other
+ * modules on this line, because we found the right one.
+ * rgerhards, 2007-07-24
+ */
+ dbgprintf("error %d parsing config line\n", (int) iRet);
+ break;
+ }
+ node = module.GetNxtCnfType(conf, node, eMOD_OUT);
+ }
+
+ *ppAction = pAction;
+ if(iRet == RS_RET_OK && bHadWarning)
+ iRet = RS_RET_OK_WARN;
+ RETiRet;
+}
+
+
+/* return the current number of active actions
+ * rgerhards, 2008-07-28
+ */
+static rsRetVal
+GetNbrActActions(rsconf_t *conf, int *piNbrActions)
+{
+ DEFiRet;
+ assert(piNbrActions != NULL);
+ *piNbrActions = conf->actions.nbrActions;
+ RETiRet;
+}
+
+
+/* queryInterface function
+ * rgerhards, 2008-02-29
+ */
+BEGINobjQueryInterface(conf)
+CODESTARTobjQueryInterface(conf)
+ if(pIf->ifVersion != confCURR_IF_VERSION) { /* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->doNameLine = doNameLine;
+ pIf->cfsysline = cfsysline;
+ pIf->doModLoad = doModLoad;
+ pIf->GetNbrActActions = GetNbrActActions;
+
+finalize_it:
+ENDobjQueryInterface(conf)
+
+
+/* Reset config variables to default values.
+ * rgerhards, 2010-07-23
+ */
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ bConfStrictScoping = 0;
+ return RS_RET_OK;
+}
+
+
+/* exit our class
+ * rgerhards, 2008-03-11
+ */
+BEGINObjClassExit(conf, OBJ_IS_CORE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(conf)
+ /* free no-longer needed module-global variables */
+ if(pDfltHostnameCmp != NULL) {
+ rsCStrDestruct(&pDfltHostnameCmp);
+ }
+
+ if(pDfltProgNameCmp != NULL) {
+ rsCStrDestruct(&pDfltProgNameCmp);
+ }
+
+ /* release objects we no longer need */
+ objRelease(module, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(ruleset, CORE_COMPONENT);
+ENDObjClassExit(conf)
+
+
+/* Initialize our class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-29
+ */
+BEGINAbstractObjClassInit(conf, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */
+ /* request objects we use */
+ CHKiRet(objUse(module, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+
+ /* These commands will NOT be supported -- the new v6.3 config system provides
+ * far better methods. We will remove the related code soon. -- rgerhards, 2012-01-09
+ */
+ CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables,
+NULL, NULL));
+ENDObjClassInit(conf)