summaryrefslogtreecommitdiff
path: root/plugins/imfile/imfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/imfile/imfile.c')
-rw-r--r--plugins/imfile/imfile.c192
1 files changed, 132 insertions, 60 deletions
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index 9c824c1..9fde058 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -5,7 +5,7 @@
*
* Work originally begun on 2008-02-01 by Rainer Gerhards
*
- * Copyright 2008-2012 Adiscon GmbH.
+ * Copyright 2008-2013 Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -31,6 +31,8 @@
#include <errno.h>
#include <fcntl.h>
#include <pthread.h> /* do NOT remove: will soon be done by the module generation macros */
+#include <sys/types.h>
+#include <unistd.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
@@ -70,8 +72,14 @@ static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config para
#define NUM_MULTISUB 1024 /* default max number of submits */
#define DFLT_PollInterval 10
+#define INIT_FILE_TAB_SIZE 4 /* default file table size - is extended as needed, use 2^x value */
+
+/* this structure is used in pure polling mode as well one of the support
+ * structures for inotify.
+ */
typedef struct fileInfo_s {
uchar *pszFileName;
+ uchar *pszBasename; /* our files basename part */
uchar *pszTag;
size_t lenTag;
uchar *pszStateFile; /* file in which state between runs is to be stored */
@@ -81,7 +89,8 @@ typedef struct fileInfo_s {
int nRecords; /**< How many records did we process before persisting the stream? */
int iPersistStateInterval; /**< how often should state be persisted? (0=on close only) */
strm_t *pStrm; /* its stream (NULL if not assigned) */
- int readMode; /* which mode to use in ReadMulteLine call? */
+ uint8_t readMode; /* which mode to use in ReadMulteLine call? */
+ sbool escapeLF; /* escape LF inside the MSG content? */
ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */
ratelimit_t *ratelimiter;
multi_submit_t multiSub;
@@ -103,6 +112,8 @@ static struct configSettings_s {
struct instanceConf_s {
uchar *pszFileName;
+ uchar *pszDirName;
+ uchar *pszFileBaseName;
uchar *pszTag;
uchar *pszStateFile;
uchar *pszBindRuleset;
@@ -110,7 +121,8 @@ struct instanceConf_s {
int iPersistStateInterval;
int iFacility;
int iSeverity;
- int readMode;
+ uint8_t readMode;
+ sbool escapeLF;
int maxLinesAtOnce;
ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */
struct instanceConf_s *next;
@@ -132,8 +144,8 @@ static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current l
static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */
static int iFilPtr = 0; /* number of files to be monitored; pointer to next free spot during config */
-#define MAX_INPUT_FILES 100
-static fileInfo_t files[MAX_INPUT_FILES];
+static fileInfo_t *files = NULL;
+static int currMaxFiles;
static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this input */
@@ -156,6 +168,7 @@ static struct cnfparamdescr inppdescr[] = {
{ "facility", eCmdHdlrFacility, 0 },
{ "ruleset", eCmdHdlrString, 0 },
{ "readmode", eCmdHdlrInt, 0 },
+ { "escapelf", eCmdHdlrBinary, 0 },
{ "maxlinesatonce", eCmdHdlrInt, 0 },
{ "maxsubmitatonce", eCmdHdlrInt, 0 },
{ "persiststateinterval", eCmdHdlrInt, 0 }
@@ -168,6 +181,7 @@ static struct cnfparamblk inppblk =
#include "im-helper.h" /* must be included AFTER the type definitions! */
+
/* enqueue the read file line as a message. The provided string is
* not freed - thuis must be done by the caller.
*/
@@ -295,7 +309,7 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
while(glbl.GetGlobalInputTermState() == 0) {
if(pThis->maxLinesAtOnce != 0 && nProcessed >= pThis->maxLinesAtOnce)
break;
- CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode));
+ CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode, pThis->escapeLF));
++nProcessed;
*pbHadFileData = 1; /* this is just a flag, so set it and forget it */
CHKiRet(enqLine(pThis, pCStr)); /* process line */
@@ -341,6 +355,7 @@ createInstance(instanceConf_t **pinst)
inst->maxLinesAtOnce = 10240;
inst->iPersistStateInterval = 0;
inst->readMode = 0;
+ inst->escapeLF = 1;
/* node created, let's add to config */
if(loadModConf->tail == NULL) {
@@ -356,6 +371,65 @@ finalize_it:
}
+/* this function checks instance parameters and does some required pre-processing
+ * (e.g. split filename in path and actual name)
+ * Note: we do NOT use dirname()/basename() as they have portability problems.
+ */
+static rsRetVal
+checkInstance(instanceConf_t *inst)
+{
+ char dirn[MAXFNAME];
+ char basen[MAXFNAME];
+ int i;
+ int lenName;
+ struct stat sb;
+ int r;
+ int eno;
+ char errStr[512];
+ DEFiRet;
+
+ lenName = ustrlen(inst->pszFileName);
+ for(i = lenName ; i >= 0 ; --i) {
+ if(inst->pszFileName[i] == '/') {
+ /* found basename component */
+ if(i == lenName)
+ basen[0] = '\0';
+ else {
+ memcpy(basen, inst->pszFileName+i+1, lenName-i);
+ /* Note \0 is copied above! */
+ //basen[(lenName-i+1)+1] = '\0';
+ }
+ break;
+ }
+ }
+ memcpy(dirn, inst->pszFileName, i); /* do not copy slash */
+ dirn[i] = '\0';
+ CHKmalloc(inst->pszFileBaseName = ustrdup(basen));
+ CHKmalloc(inst->pszDirName = ustrdup(dirn));
+
+ if(dirn[0] == '\0') {
+ dirn[0] = '/';
+ dirn[1] = '\0';
+ }
+ r = stat(dirn, &sb);
+ if(r != 0) {
+ eno = errno;
+ rs_strerror_r(eno, errStr, sizeof(errStr));
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile warning: directory '%s': %s",
+ dirn, errStr);
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+ if(!S_ISDIR(sb.st_mode)) {
+ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile warning: configured directory "
+ "'%s' is NOT a directory", dirn);
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
/* add a new monitor */
static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
@@ -389,6 +463,9 @@ static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
inst->maxLinesAtOnce = cs.maxLinesAtOnce;
inst->iPersistStateInterval = cs.iPersistStateInterval;
inst->readMode = cs.readMode;
+ inst->escapeLF = 0;
+
+ CHKiRet(checkInstance(inst));
/* reset legacy system */
cs.iPersistStateInterval = 0;
@@ -405,33 +482,44 @@ static inline rsRetVal
addListner(instanceConf_t *inst)
{
DEFiRet;
+ int newMax;
+ fileInfo_t *newFileTab;
fileInfo_t *pThis;
- if(iFilPtr < MAX_INPUT_FILES) {
- pThis = &files[iFilPtr];
- //TODO: optimize, save strdup?
- pThis->pszFileName = (uchar*) strdup((char*) inst->pszFileName);
- pThis->pszTag = (uchar*) strdup((char*) inst->pszTag);
- pThis->lenTag = ustrlen(pThis->pszTag);
- pThis->pszStateFile = (uchar*) strdup((char*) inst->pszStateFile);
-
- CHKiRet(ratelimitNew(&pThis->ratelimiter, "imfile", (char*)inst->pszFileName));
- CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(inst->nMultiSub * sizeof(msg_t*)));
- pThis->multiSub.maxElem = inst->nMultiSub;
- pThis->multiSub.nElem = 0;
- pThis->iSeverity = inst->iSeverity;
- pThis->iFacility = inst->iFacility;
- pThis->maxLinesAtOnce = inst->maxLinesAtOnce;
- pThis->iPersistStateInterval = inst->iPersistStateInterval;
- pThis->readMode = inst->readMode;
- pThis->pRuleset = inst->pBindRuleset;
- pThis->nRecords = 0;
- } else {
- errmsg.LogError(0, RS_RET_OUT_OF_DESRIPTORS,
- "Too many file monitors configured - ignoring %s",
- inst->pszFileName);
- ABORT_FINALIZE(RS_RET_OUT_OF_DESRIPTORS);
+ if(iFilPtr == currMaxFiles) {
+ newMax = 2 * currMaxFiles;
+ newFileTab = realloc(files, newMax * sizeof(fileInfo_t));
+ if(newFileTab == NULL) {
+ errmsg.LogError(0, RS_RET_OUT_OF_MEMORY,
+ "cannot alloc memory to monitor file '%s' - ignoring",
+ inst->pszFileName);
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ files = newFileTab;
+ currMaxFiles = newMax;
+ DBGPRINTF("imfile: increased file table to %d entries\n", currMaxFiles);
}
+
+ /* if we reach this point, there is space in the file table for the new entry */
+ pThis = &files[iFilPtr];
+ pThis->pszFileName = (uchar*) strdup((char*) inst->pszFileName);
+ pThis->pszTag = (uchar*) strdup((char*) inst->pszTag);
+ pThis->lenTag = ustrlen(pThis->pszTag);
+ pThis->pszStateFile = (uchar*) strdup((char*) inst->pszStateFile);
+
+ CHKiRet(ratelimitNew(&pThis->ratelimiter, "imfile", (char*)inst->pszFileName));
+ CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(inst->nMultiSub * sizeof(msg_t*)));
+ pThis->multiSub.maxElem = inst->nMultiSub;
+ pThis->multiSub.nElem = 0;
+ pThis->iSeverity = inst->iSeverity;
+ pThis->iFacility = inst->iFacility;
+ pThis->maxLinesAtOnce = inst->maxLinesAtOnce;
+ pThis->iPersistStateInterval = inst->iPersistStateInterval;
+ pThis->readMode = inst->readMode;
+ pThis->escapeLF = inst->escapeLF;
+ pThis->pRuleset = inst->pBindRuleset;
+ pThis->nRecords = 0;
+ pThis->pStrm = NULL;
++iFilPtr; /* we got a new file to monitor */
resetConfigVariables(NULL, NULL); /* values are both dummies */
@@ -475,7 +563,9 @@ CODESTARTnewInpInst
} else if(!strcmp(inppblk.descr[i].name, "facility")) {
inst->iFacility = pvals[i].val.d.n;
} else if(!strcmp(inppblk.descr[i].name, "readmode")) {
- inst->readMode = pvals[i].val.d.n;
+ inst->readMode = (uint8_t) pvals[i].val.d.n;
+ } else if(!strcmp(inppblk.descr[i].name, "escapelf")) {
+ inst->escapeLF = (sbool) pvals[i].val.d.n;
} else if(!strcmp(inppblk.descr[i].name, "maxlinesatonce")) {
inst->maxLinesAtOnce = pvals[i].val.d.n;
} else if(!strcmp(inppblk.descr[i].name, "persiststateinterval")) {
@@ -487,6 +577,7 @@ CODESTARTnewInpInst
"param '%s'\n", inppblk.descr[i].name);
}
}
+ CHKiRet(checkInstance(inst));
finalize_it:
CODE_STD_FINALIZERnewInpInst
cnfparamvalsDestruct(pvals, &inppblk);
@@ -591,9 +682,15 @@ BEGINactivateCnf
instanceConf_t *inst;
CODESTARTactivateCnf
runModConf = pModConf;
+ free(files); /* clear any previous instance */
+ CHKmalloc(files = (fileInfo_t*) malloc(sizeof(fileInfo_t) * INIT_FILE_TAB_SIZE));
+ currMaxFiles = INIT_FILE_TAB_SIZE;
+ iFilPtr = 0;
+
for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
addListner(inst);
}
+
/* if we could not set up any listeners, there is no point in running... */
if(iFilPtr == 0) {
errmsg.LogError(0, NO_ERRCODE, "imfile: no file monitors could be started, "
@@ -610,35 +707,18 @@ CODESTARTfreeCnf
for(inst = pModConf->root ; inst != NULL ; ) {
free(inst->pszBindRuleset);
free(inst->pszFileName);
+ free(inst->pszDirName);
+ free(inst->pszFileBaseName);
free(inst->pszTag);
free(inst->pszStateFile);
del = inst;
inst = inst->next;
free(del);
}
+ free(files);
ENDfreeCnf
-
-/* This function is the cancel cleanup handler. It is called when rsyslog decides the
- * module must be stopped, what most probably happens during shutdown of rsyslogd. When
- * this function is called, the runInput() function (below) is already terminated - somewhere
- * in the middle of what it was doing. The cancel cleanup handler below should take
- * care of any locked mutexes and such, things that really need to be cleaned up
- * before processing continues. In general, many plugins do not need to provide
- * any code at all here.
- *
- * IMPORTANT: the calling interface of this function can NOT be modified. It actually is
- * called by pthreads. The provided argument is currently not being used.
- */
-static void
-inputModuleCleanup(void __attribute__((unused)) *arg)
-{
- BEGINfunc
- ENDfunc
-}
-
-
/* This function is called by the framework to gather the input. The module stays
* most of its lifetime inside this function. It MUST NEVER exit this function. Doing
* so would end module processing and rsyslog would NOT reschedule the module. If
@@ -659,12 +739,10 @@ inputModuleCleanup(void __attribute__((unused)) *arg)
* On spamming the main queue: keep in mind that it will automatically rate-limit
* ourselfes if we begin to overrun it. So we really do not need to care here.
*/
-#pragma GCC diagnostic ignored "-Wempty-body"
BEGINrunInput
int i;
int bHadFileData; /* were there at least one file with data during this run? */
CODESTARTrunInput
- pthread_cleanup_push(inputModuleCleanup, NULL);
while(glbl.GetGlobalInputTermState() == 0) {
do {
bHadFileData = 0;
@@ -684,13 +762,8 @@ CODESTARTrunInput
}
DBGPRINTF("imfile: terminating upon request of rsyslog core\n");
- pthread_cleanup_pop(0); /* just for completeness, but never called... */
RETiRet; /* use it to make sure the housekeeping is done! */
ENDrunInput
-#pragma GCC diagnostic warning "-Wempty-body"
- /* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
/* The function is called by rsyslog before runInput() is called. It is a last chance
@@ -860,8 +933,7 @@ std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, insta
* complexity of processing is depending on the actual module. However, only
* thing absolutely necessary should be done here. Actual app-level processing
* is to be performed in runInput(). A good sample of what to do here may be to
- * set some variable defaults. The most important thing probably is registration
- * of config command handlers.
+ * set some variable defaults.
*/
BEGINmodInit()
CODESTARTmodInit