diff options
Diffstat (limited to 'tools/omfile.c')
-rw-r--r-- | tools/omfile.c | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/tools/omfile.c b/tools/omfile.c new file mode 100644 index 00000000..8144386f --- /dev/null +++ b/tools/omfile.c @@ -0,0 +1,846 @@ +/* omfile.c + * This is the implementation of the build-in file output module. + * + * Handles: eTypeCONSOLE, eTypeTTY, eTypeFILE, eTypePIPE + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * 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, 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" +#include "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/file.h> + +#include "syslogd.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "template.h" +#include "outchannel.h" +#include "omfile.h" +#include "cfsysline.h" +#include "module-template.h" +#include "errmsg.h" + +MODULE_TYPE_OUTPUT + +/* internal structures + */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(errmsg) + +/* The following structure is a dynafile name cache entry. + */ +struct s_dynaFileCacheEntry { + uchar *pName; /* name currently open, if dynamic name */ + short fd; /* name associated with file name in cache */ + time_t lastUsed; /* for LRU - last access */ +}; +typedef struct s_dynaFileCacheEntry dynaFileCacheEntry; + + +/* globals for default values */ +static int iDynaFileCacheSize = 10; /* max cache for dynamic files */ +static int fCreateMode = 0644; /* mode to use when creating files */ +static int fDirCreateMode = 0644; /* mode to use when creating files */ +static int bFailOnChown; /* fail if chown fails? */ +static uid_t fileUID; /* UID to be used for newly created files */ +static uid_t fileGID; /* GID to be used for newly created files */ +static uid_t dirUID; /* UID to be used for newly created directories */ +static uid_t dirGID; /* GID to be used for newly created directories */ +static int bCreateDirs; /* auto-create directories for dynaFiles: 0 - no, 1 - yes */ +static int bEnableSync = 0;/* enable syncing of files (no dash in front of pathname in conf): 0 - no, 1 - yes */ +static uchar *pszTplName = NULL; /* name of the default template to use */ +/* end globals for default values */ + + +typedef struct _instanceData { + uchar f_fname[MAXFNAME];/* file or template name (display only) */ + short fd; /* file descriptor for (current) file */ + enum { + eTypeFILE, + eTypeTTY, + eTypeCONSOLE, + eTypePIPE + } fileType; + char bDynamicName; /* 0 - static name, 1 - dynamic name (with properties) */ + int fCreateMode; /* file creation mode for open() */ + int fDirCreateMode; /* creation mode for mkdir() */ + int bCreateDirs; /* auto-create directories? */ + int bSyncFile; /* should the file by sync()'ed? 1- yes, 0- no */ + uid_t fileUID; /* IDs for creation */ + uid_t dirUID; + gid_t fileGID; + gid_t dirGID; + int bFailOnChown; /* fail creation if chown fails? */ + int iCurrElt; /* currently active cache element (-1 = none) */ + int iCurrCacheSize; /* currently cache size (1-based) */ + int iDynaFileCacheSize; /* size of file handle cache */ + /* The cache is implemented as an array. An empty element is indicated + * by a NULL pointer. Memory is allocated as needed. The following + * pointer points to the overall structure. + */ + dynaFileCacheEntry **dynCache; + off_t f_sizeLimit; /* file size limit, 0 = no limit */ + char *f_sizeLimitCmd; /* command to carry out when size limit is reached */ +} instanceData; + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + if(pData->bDynamicName) { + printf("[dynamic]\n\ttemplate='%s'" + "\tfile cache size=%d\n" + "\tcreate directories: %s\n" + "\tfile owner %d, group %d\n" + "\tdirectory owner %d, group %d\n" + "\tfail if owner/group can not be set: %s\n", + pData->f_fname, + pData->iDynaFileCacheSize, + pData->bCreateDirs ? "yes" : "no", + pData->fileUID, pData->fileGID, + pData->dirUID, pData->dirGID, + pData->bFailOnChown ? "yes" : "no" + ); + } else { /* regular file */ + printf("%s", pData->f_fname); + if (pData->fd == -1) + printf(" (unused)"); + } +ENDdbgPrintInstInfo + + +/* set the dynaFile cache size. Does some limit checking. + * rgerhards, 2007-07-31 + */ +rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal) +{ + DEFiRet; + uchar errMsg[128]; /* for dynamic error messages */ + + if(iNewVal < 1) { + snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), + "DynaFileCacheSize must be greater 0 (%d given), changed to 1.", iNewVal); + errno = 0; + errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg); + iRet = RS_RET_VAL_OUT_OF_RANGE; + iNewVal = 1; + } else if(iNewVal > 10000) { + snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar), + "DynaFileCacheSize maximum is 10,000 (%d given), changed to 10,000.", iNewVal); + errno = 0; + errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg); + iRet = RS_RET_VAL_OUT_OF_RANGE; + iNewVal = 10000; + } + + iDynaFileCacheSize = iNewVal; + dbgprintf("DynaFileCacheSize changed to %d.\n", iNewVal); + + RETiRet; +} + + +/* Helper to cfline(). Parses a output channel name up until the first + * comma and then looks for the template specifier. Tries + * to find that template. Maps the output channel to the + * proper filed structure settings. Everything is stored in the + * filed struct. Over time, the dependency on filed might be + * removed. + * rgerhards 2005-06-21 + */ +static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts) +{ + DEFiRet; + size_t i; + struct outchannel *pOch; + char szBuf[128]; /* should be more than sufficient */ + + /* this must always be a file, because we can not set a size limit + * on a pipe... + * rgerhards 2005-06-21: later, this will be a separate type, but let's + * emulate things for the time being. When everything runs, we can + * extend it... + */ + pData->fileType = eTypeFILE; + + ++p; /* skip '$' */ + i = 0; + /* get outchannel name */ + while(*p && *p != ';' && *p != ' ' && + i < sizeof(szBuf) / sizeof(char)) { + szBuf[i++] = *p++; + } + szBuf[i] = '\0'; + + /* got the name, now look up the channel... */ + pOch = ochFind(szBuf, i); + + if(pOch == NULL) { + char errMsg[128]; + errno = 0; + snprintf(errMsg, sizeof(errMsg)/sizeof(char), + "outchannel '%s' not found - ignoring action line", + szBuf); + errmsg.LogError(0, RS_RET_NOT_FOUND, "%s", errMsg); + ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + + /* check if there is a file name in the outchannel... */ + if(pOch->pszFileTemplate == NULL) { + char errMsg[128]; + errno = 0; + snprintf(errMsg, sizeof(errMsg)/sizeof(char), + "outchannel '%s' has no file name template - ignoring action line", + szBuf); + errmsg.LogError(0, RS_RET_ERR, "%s", errMsg); + ABORT_FINALIZE(RS_RET_ERR); + } + + /* OK, we finally got a correct template. So let's use it... */ + strncpy((char*) pData->f_fname, (char*) pOch->pszFileTemplate, MAXFNAME); + pData->f_sizeLimit = pOch->uSizeLimit; + /* WARNING: It is dangerous "just" to pass the pointer. As we + * never rebuild the output channel description, this is acceptable here. + */ + pData->f_sizeLimitCmd = (char*) pOch->cmdOnSizeLimit; + +RUNLOG_VAR("%p", pszTplName); + iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, + (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName); + +finalize_it: + RETiRet; +} + + +/* 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(instanceData *pData) +{ + uchar *pParams; + uchar *pCmd; + uchar *p; + off_t actualFileSize; + ASSERT(pData != NULL); + + if(pData->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*)pData->f_sizeLimitCmd)) == NULL) { + /* there is not much we can do - we make syslogd close the file in this case */ + 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); + + free(pCmd); + + pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + pData->fCreateMode); + + actualFileSize = lseek(pData->fd, 0, SEEK_END); + if(actualFileSize >= pData->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 d_free()ed and 0 if not. + */ +static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry) +{ + ASSERT(pCache != NULL); + + BEGINfunc; + + if(pCache[iEntry] == NULL) + FINALIZE; + + dbgprintf("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); + d_free(pCache[iEntry]->pName); + pCache[iEntry]->pName = NULL; + } + + if(bFreeEntry) { + d_free(pCache[iEntry]); + pCache[iEntry] = NULL; + } + +finalize_it: + ENDfunc; +} + + +/* This function frees the dynamic file name cache. + */ +static void dynaFileFreeCache(instanceData *pData) +{ + register int i; + ASSERT(pData != NULL); + + BEGINfunc; + for(i = 0 ; i < pData->iCurrCacheSize ; ++i) { + dynaFileDelCacheEntry(pData->dynCache, i, 1); + } + + if(pData->dynCache != NULL) + d_free(pData->dynCache); + ENDfunc; +} + + +/* This is a shared code for both static and dynamic files. + */ +static void prepareFile(instanceData *pData, uchar *newFileName) +{ + if(access((char*)newFileName, F_OK) == 0) { + /* file already exists */ + pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + pData->fCreateMode); + } else { + pData->fd = -1; + /* file does not exist, create it (and eventually parent directories */ + if(pData->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), + pData->fDirCreateMode, pData->dirUID, + pData->dirGID, pData->bFailOnChown) == 0) { + pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + pData->fCreateMode); + if(pData->fd != -1) { + /* check and set uid/gid */ + if(pData->fileUID != (uid_t)-1 || pData->fileGID != (gid_t) -1) { + /* we need to set owner/group */ + if(fchown(pData->fd, pData->fileUID, + pData->fileGID) != 0) { + if(pData->bFailOnChown) { + int eSave = errno; + close(pData->fd); + pData->fd = -1; + errno = eSave; + } + /* we will silently ignore the chown() failure + * if configured to do so. + */ + } + } + } + } + } + } +} + + +/* This function handles dynamic file names. It checks if the + * requested file name is already open and, if not, does everything + * needed to switch to the it. + * Function returns 0 if all went well and non-zero otherwise. + * On successful return pData->fd must point to the correct file to + * be written. + * This is a helper to writeFile(). rgerhards, 2007-07-03 + */ +static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) +{ + time_t ttOldest; /* timestamp of oldest element */ + int iOldest; + int i; + int iFirstFree; + dynaFileCacheEntry **pCache; + + ASSERT(pData != NULL); + ASSERT(newFileName != NULL); + + pCache = pData->dynCache; + + /* first check, if we still have the current file + * I *hope* this will be a performance enhancement. + */ + if( (pData->iCurrElt != -1) + && !strcmp((char*) newFileName, (char*) pCache[pData->iCurrElt]->pName)) { + /* great, we are all set */ + pCache[pData->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 < pData->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! */ + pData->fd = pCache[i]->fd; + pData->iCurrElt = i; + 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 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) { + /* there is space left, so set it to that index */ + iFirstFree = pData->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) { + dbgprintf("prepareDynfile(): could not alloc mem, discarding this request\n"); + return -1; + } + } + + /* Ok, we finally can open the file */ + prepareFile(pData, newFileName); + + /* file is either open now or an error state set */ + if(pData->fd == -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(iMsgOpts & INTERNAL_MSG) + dbgprintf("Could not open dynaFile, discarding message\n"); + else + errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", (char*)newFileName); + dynaFileDelCacheEntry(pCache, iFirstFree, 1); + pData->iCurrElt = -1; + return -1; + } + + pCache[iFirstFree]->fd = pData->fd; + pCache[iFirstFree]->pName = (uchar*)strdup((char*)newFileName); /* TODO: check for NULL (very unlikely) */ + pCache[iFirstFree]->lastUsed = time(NULL); + pData->iCurrElt = iFirstFree; + dbgprintf("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. + */ +static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData) +{ + off_t actualFileSize; + DEFiRet; + + ASSERT(pData != 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(pData->bDynamicName) { + if(prepareDynFile(pData, ppString[1], iMsgOpts) != 0) + ABORT_FINALIZE(RS_RET_ERR); + } + + /* create the message based on format specified */ +again: + /* check if we have a file size limit and, if so, + * obey to it. + */ + if(pData->f_sizeLimit != 0) { + actualFileSize = lseek(pData->fd, 0, SEEK_END); + if(actualFileSize >= pData->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(pData->fd); + /* try to resolve the situation */ + if(resolveFileSizeLimit(pData) != 0) { + /* didn't work out, so disable... */ + 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", + pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize); + errno = 0; + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "%s", errMsg); + ABORT_FINALIZE(RS_RET_DISABLE_ACTION); + } else { + snprintf(errMsg, sizeof(errMsg), + "file %s had grown beyond configured file size of %lld bytes, actual size was %lld - configured command resolved situation", + pData->f_fname, (long long) pData->f_sizeLimit, (long long) actualFileSize); + errno = 0; + errmsg.LogError(0, NO_ERRCODE, "%s", errMsg); + } + } + } + + if (write(pData->fd, ppString[0], strlen((char*)ppString[0])) < 0) { + int e = errno; + + /* If a named pipe is full, just ignore it for now + - mrn 24 May 96 */ + if (pData->fileType == eTypePIPE && e == EAGAIN) + ABORT_FINALIZE(RS_RET_OK); + + /* 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 (pData->fileType == eTypeFILE && e == ENOSPC) + ABORT_FINALIZE(RS_RET_OK); + + (void) close(pData->fd); + /* + * Check for EBADF on TTY's due to vhangup() + * Linux uses EIO instead (mrn 12 May 96) + */ + if ((pData->fileType == eTypeTTY || pData->fileType == eTypeCONSOLE) +#ifdef linux + && e == EIO) { +#else + && e == EBADF) { +#endif + pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_NOCTTY); + if (pData->fd < 0) { + iRet = RS_RET_DISABLE_ACTION; + errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname); + } else { + untty(); + goto again; + } + } else { + iRet = RS_RET_DISABLE_ACTION; + errno = e; + errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname); + } + } else if (pData->bSyncFile) { + fsync(pData->fd); + } + +finalize_it: + RETiRet; +} + + +BEGINcreateInstance +CODESTARTcreateInstance + pData->fd = -1; +ENDcreateInstance + + +BEGINfreeInstance +CODESTARTfreeInstance + if(pData->bDynamicName) { + dynaFileFreeCache(pData); + } else if(pData->fd != -1) + close(pData->fd); +ENDfreeInstance + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +BEGINdoAction +CODESTARTdoAction + dbgprintf(" (%s)\n", pData->f_fname); + /* pData->fd == -1 is an indicator that the we couldn't + * open the file at startup. For dynaFiles, this is ok, + * all others are doomed. + */ + if(pData->bDynamicName || (pData->fd != -1)) + iRet = writeFile(ppString, iMsgOpts, pData); +ENDdoAction + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct + /* yes, the if below is redundant, but I need it now. Will go away as + * the code further changes. -- rgerhards, 2007-07-25 + */ + if(*p == '$' || *p == '?' || *p == '|' || *p == '/' || *p == '-') { + if((iRet = createInstance(&pData)) != RS_RET_OK) { + ENDfunc + return iRet; /* this can not use RET_iRet! */ + } + } else { + /* this is not clean, but we need it for the time being + * TODO: remove when cleaning up modularization + */ + ENDfunc + return RS_RET_CONFLINE_UNPROCESSED; + } + + if(*p == '-') { + pData->bSyncFile = 0; + p++; + } else { + pData->bSyncFile = bEnableSync ? 1 : 0; + } + + pData->f_sizeLimit = 0; /* default value, use outchannels to configure! */ + + switch (*p) + { + case '$': + CODE_STD_STRING_REQUESTparseSelectorAct(1) + /* rgerhards 2005-06-21: this is a special setting for output-channel + * definitions. In the long term, this setting will probably replace + * anything else, but for the time being we must co-exist with the + * traditional mode lines. + * rgerhards, 2007-07-24: output-channels will go away. We keep them + * for compatibility reasons, but seems to have been a bad idea. + */ + if((iRet = cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)) == RS_RET_OK) { + pData->bDynamicName = 0; + pData->fCreateMode = fCreateMode; /* preserve current setting */ + pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */ + pData->fd = open((char*) pData->f_fname, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, + pData->fCreateMode); + } + break; + + case '?': /* This is much like a regular file handle, but we need to obtain + * a template name. rgerhards, 2007-07-03 + */ + CODE_STD_STRING_REQUESTparseSelectorAct(2) + ++p; /* eat '?' */ + if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, + (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName)) + != RS_RET_OK) + break; + /* "filename" is actually a template name, we need this as string 1. So let's add it + * to the pOMSR. -- rgerhards, 2007-07-27 + */ + if((iRet = OMSRsetEntry(*ppOMSR, 1, (uchar*)strdup((char*) pData->f_fname), OMSR_NO_RQD_TPL_OPTS)) != RS_RET_OK) + break; + + pData->bDynamicName = 1; + pData->iCurrElt = -1; /* no current element */ + pData->fCreateMode = fCreateMode; /* freeze current setting */ + pData->fDirCreateMode = fDirCreateMode; /* preserve current setting */ + pData->bCreateDirs = bCreateDirs; + pData->bFailOnChown = bFailOnChown; + pData->fileUID = fileUID; + pData->fileGID = fileGID; + pData->dirUID = dirUID; + pData->dirGID = dirGID; + pData->iDynaFileCacheSize = iDynaFileCacheSize; /* freeze current setting */ + /* we now allocate the cache table. We use calloc() intentionally, as we + * need all pointers to be initialized to NULL pointers. + */ + if((pData->dynCache = (dynaFileCacheEntry**) + calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))) == NULL) { + iRet = RS_RET_OUT_OF_MEMORY; + dbgprintf("Could not allocate memory for dynaFileCache - selector disabled.\n"); + } + break; + + case '|': + case '/': + CODE_STD_STRING_REQUESTparseSelectorAct(1) + /* rgerhards, 2007-0726: first check if file or pipe */ + if(*p == '|') { + pData->fileType = eTypePIPE; + ++p; + } else { + pData->fileType = eTypeFILE; + } + /* rgerhards 2004-11-17: from now, we need to have different + * processing, because after the first comma, the template name + * to use is specified. So we need to scan for the first coma first + * and then look at the rest of the line. + */ + if((iRet = cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, + (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName)) + != RS_RET_OK) + break; + + pData->bDynamicName = 0; + pData->fCreateMode = fCreateMode; /* preserve current setting */ + pData->fDirCreateMode = fDirCreateMode; + pData->bCreateDirs = bCreateDirs; + pData->bFailOnChown = bFailOnChown; + pData->fileUID = fileUID; + pData->fileGID = fileGID; + pData->dirUID = dirUID; + pData->dirGID = dirGID; + + if(pData->fileType == eTypePIPE) { + pData->fd = open((char*) pData->f_fname, O_RDWR|O_NONBLOCK); + } else { + prepareFile(pData, pData->f_fname); + } + + if ( pData->fd < 0 ){ + pData->fd = -1; + dbgprintf("Error opening log file: %s\n", pData->f_fname); + errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname); + break; + } + if (isatty(pData->fd)) { + pData->fileType = eTypeTTY; + untty(); + } + if (strcmp((char*) p, _PATH_CONSOLE) == 0) + pData->fileType = eTypeCONSOLE; + break; + default: + iRet = RS_RET_CONFLINE_UNPROCESSED; + break; + } +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +/* Reset config variables for this module to default values. + * rgerhards, 2007-07-17 + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + fileUID = -1; + fileGID = -1; + dirUID = -1; + dirGID = -1; + bFailOnChown = 1; + iDynaFileCacheSize = 10; + fCreateMode = 0644; + fDirCreateMode = 0644; + bCreateDirs = 1; + bEnableSync = 0; + if(pszTplName != NULL) { + free(pszTplName); + pszTplName = NULL; + } + + return RS_RET_OK; +} + + +BEGINmodExit +CODESTARTmodExit + if(pszTplName != NULL) + free(pszTplName); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(File) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dynafilecachesize", 0, eCmdHdlrInt, (void*) setDynaFileCacheSize, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirowner", 0, eCmdHdlrUID, NULL, &dirUID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroup", 0, eCmdHdlrGID, NULL, &dirGID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileowner", 0, eCmdHdlrUID, NULL, &fileUID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"filegroup", 0, eCmdHdlrGID, NULL, &fileGID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dircreatemode", 0, eCmdHdlrFileCreateMode, NULL, &fDirCreateMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"filecreatemode", 0, eCmdHdlrFileCreateMode, NULL, &fCreateMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"createdirs", 0, eCmdHdlrBinary, NULL, &bCreateDirs, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"failonchownfailure", 0, eCmdHdlrBinary, NULL, &bFailOnChown, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfileenablesync", 0, eCmdHdlrBinary, NULL, &bEnableSync, STD_LOADABLE_MODULE_ID)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit +/* vi:set ai: + */ |