summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am11
-rw-r--r--tools/gethostn.c47
-rw-r--r--tools/iminternal.c3
-rw-r--r--tools/msggen.c38
-rw-r--r--tools/omfile.c178
-rw-r--r--tools/omfwd.c18
-rw-r--r--tools/rsyslogd.833
-rw-r--r--tools/syslogd.c737
-rw-r--r--tools/syslogd.h3
9 files changed, 761 insertions, 307 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index b2b7a8ca..e523b854 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -21,9 +21,14 @@ rsyslogd_SOURCES = \
pidfile.h \
\
../dirty.h
-
-rsyslogd_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
-rsyslogd_LDADD = $(zlib_libs) $(pthreads_libs) $(rsrt_libs)
+rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS)
rsyslogd_LDFLAGS = -export-dynamic
+if ENABLE_DIAGTOOLS
+sbin_PROGRAMS += rsyslog_diag_hostname msggen
+rsyslog_diag_hostname_SOURCES = gethostn.c
+msggen_SOURCES = msggen.c
+endif
+
EXTRA_DIST = $(man_MANS)
diff --git a/tools/gethostn.c b/tools/gethostn.c
new file mode 100644
index 00000000..df7ce38b
--- /dev/null
+++ b/tools/gethostn.c
@@ -0,0 +1,47 @@
+/* gethostn - a small diagnostic utility to show what the
+ * gethostname() API returns. Of course, this tool duplicates
+ * functionality already found in other tools. But the point is
+ * that the API shall be called by a program that is compiled like
+ * rsyslogd and does exactly what rsyslog does.
+ *
+ * Copyright 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[])
+{
+ char hostname[4096]; /* this should always be sufficient ;) */
+ int err;
+
+ err = gethostname(hostname, sizeof(hostname));
+
+ if(err) {
+ perror("gethostname failed");
+ exit(1);
+ }
+
+ printf("hostname of this system is '%s'.\n", hostname);
+
+ return 0;
+}
diff --git a/tools/iminternal.c b/tools/iminternal.c
index 60460a99..0ceff3d8 100644
--- a/tools/iminternal.c
+++ b/tools/iminternal.c
@@ -185,6 +185,5 @@ rsRetVal modExitIminternal(void)
RETiRet;
}
-/*
- * vi:set ai:
+/* vim:set ai:
*/
diff --git a/tools/msggen.c b/tools/msggen.c
new file mode 100644
index 00000000..7990a3c8
--- /dev/null
+++ b/tools/msggen.c
@@ -0,0 +1,38 @@
+/* msggen - a small diagnostic utility that does very quick
+ * syslog() calls.
+ *
+ * Copyright 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 <stdio.h>
+#include <syslog.h>
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ openlog("msggen", 0 , LOG_LOCAL0);
+
+ for(i = 0 ; i < 10 ; ++i)
+ syslog(LOG_NOTICE, "This is message number %d", i);
+
+ closelog();
+ return 0;
+}
diff --git a/tools/omfile.c b/tools/omfile.c
index d76e24ae..65306846 100644
--- a/tools/omfile.c
+++ b/tools/omfile.c
@@ -44,6 +44,10 @@
#include <unistd.h>
#include <sys/file.h>
+#ifdef OS_SOLARIS
+# include <fcntl.h>
+#endif
+
#include "syslogd.h"
#include "syslogd-types.h"
#include "srUtils.h"
@@ -174,7 +178,7 @@ rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal)
}
iDynaFileCacheSize = iNewVal;
- dbgprintf("DynaFileCacheSize changed to %d.\n", iNewVal);
+ DBGPRINTF("DynaFileCacheSize changed to %d.\n", iNewVal);
RETiRet;
}
@@ -244,7 +248,6 @@ static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringR
*/
pData->f_sizeLimitCmd = (char*) pOch->cmdOnSizeLimit;
-RUNLOG_VAR("%p", pszTplName);
iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts,
(pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName);
@@ -327,7 +330,7 @@ static void dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int b
if(pCache[iEntry] == NULL)
FINALIZE;
- dbgprintf("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
+ 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
@@ -349,9 +352,11 @@ finalize_it:
}
-/* This function frees the dynamic file name cache.
+/* This function frees all dynamic file name cache entries and closes the
+ * relevant files. Part of Shutdown and HUP processing.
+ * rgerhards, 2008-10-23
*/
-static void dynaFileFreeCache(instanceData *pData)
+static inline void dynaFileFreeCacheEntries(instanceData *pData)
{
register int i;
ASSERT(pData != NULL);
@@ -360,17 +365,39 @@ static void dynaFileFreeCache(instanceData *pData)
for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
dynaFileDelCacheEntry(pData->dynCache, i, 1);
}
+ ENDfunc;
+}
+
+/* This function frees the dynamic file name cache.
+ */
+static void dynaFileFreeCache(instanceData *pData)
+{
+ ASSERT(pData != NULL);
+
+ BEGINfunc;
+ dynaFileFreeCacheEntries(pData);
if(pData->dynCache != NULL)
d_free(pData->dynCache);
ENDfunc;
}
-/* This is a shared code for both static and dynamic files.
+/* This is now shared code for all types of files. It simply prepares
+ * file access, which, among others, means the the file wil be opened
+ * and any directories in between will be created (based on config, of
+ * course). -- rgerhards, 2008-10-22
+ * changed to iRet interface - 2009-03-19
*/
-static void prepareFile(instanceData *pData, uchar *newFileName)
+static rsRetVal
+prepareFile(instanceData *pData, uchar *newFileName)
{
+ DEFiRet;
+ if(pData->fileType == eTypePIPE) {
+ pData->fd = open((char*) pData->f_fname, O_RDWR|O_NONBLOCK);
+ FINALIZE; /* we are done in this case */
+ }
+
if(access((char*)newFileName, F_OK) == 0) {
/* file already exists */
pData->fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY,
@@ -379,14 +406,14 @@ static void prepareFile(instanceData *pData, uchar *newFileName)
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 first 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) {
- return; /* we give up */
+ ABORT_FINALIZE(RS_RET_ERR); /* we give up */
}
}
/* no matter if we needed to create directories or not, we now try to create
@@ -398,8 +425,7 @@ static void prepareFile(instanceData *pData, uchar *newFileName)
/* 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(fchown(pData->fd, pData->fileUID, pData->fileGID) != 0) {
if(pData->bFailOnChown) {
int eSave = errno;
close(pData->fd);
@@ -413,6 +439,18 @@ static void prepareFile(instanceData *pData, uchar *newFileName)
}
}
}
+finalize_it:
+ /* this was "pData->fd != 0", which I think was a bug. I guess 0 was intended to mean
+ * non-open file descriptor. Anyhow, I leave this comment for the time being to that if
+ * problems surface, one at least knows what happened. -- rgerhards, 2009-03-19
+ */
+ if(pData->fd != -1 && isatty(pData->fd)) {
+ DBGPRINTF("file %d is a tty file\n", pData->fd);
+ pData->fileType = eTypeTTY;
+ untty();
+ }
+
+ RETiRet;
}
@@ -431,6 +469,8 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
int i;
int iFirstFree;
dynaFileCacheEntry **pCache;
+
+ BEGINfunc
ASSERT(pData != NULL);
ASSERT(newFileName != NULL);
@@ -486,13 +526,13 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
/* 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");
+ DBGPRINTF("prepareDynfile(): could not alloc mem, discarding this request\n");
return -1;
}
}
/* Ok, we finally can open the file */
- prepareFile(pData, newFileName);
+ prepareFile(pData, newFileName); /* ignore exact error, we check fd below */
/* file is either open now or an error state set */
if(pData->fd == -1) {
@@ -500,10 +540,11 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
* 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
+ 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;
@@ -513,8 +554,9 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
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);
+ DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName);
+
+ ENDfunc
return 0;
}
@@ -527,6 +569,7 @@ static int prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsg
static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
{
off_t actualFileSize;
+ int iLenWritten;
DEFiRet;
ASSERT(pData != NULL);
@@ -536,7 +579,14 @@ static rsRetVal writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pDa
*/
if(pData->bDynamicName) {
if(prepareDynFile(pData, ppString[1], iMsgOpts) != 0)
- ABORT_FINALIZE(RS_RET_ERR);
+ ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */
+ }
+
+ if(pData->fd == -1) {
+ rsRetVal iRetLocal;
+ iRetLocal = prepareFile(pData, pData->f_fname);
+ if((iRetLocal != RS_RET_OK) || (pData->fd == -1))
+ ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */
}
/* create the message based on format specified */
@@ -572,42 +622,47 @@ again:
}
}
- if (write(pData->fd, ppString[0], strlen((char*)ppString[0])) < 0) {
+ iLenWritten = write(pData->fd, ppString[0], strlen((char*)ppString[0]));
+//dbgprintf("lenwritten: %d\n", iLenWritten);
+ if(iLenWritten < 0) {
int e = errno;
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ DBGPRINTF("log file (%d) write error %d: %s\n", pData->fd, e, errStr);
- /* 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 a named pipe is full, we suspend this action for a while */
+ if(pData->fileType == eTypePIPE && e == EAGAIN)
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
- /* 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()
+ close(pData->fd);
+ pData->fd = -1; /* tell that fd is no longer open! */
+ if(pData->bDynamicName && pData->iCurrElt != -1) {
+ /* in this case, we need to invalidate the name in the cache, too
+ * otherwise, an invalid fd may show up if we had a file name change.
+ * rgerhards, 2009-03-19
+ */
+ pData->dynCache[pData->iCurrElt]->fd = -1;
+ }
+ /* Check for EBADF on TTY's due to vhangup()
* Linux uses EIO instead (mrn 12 May 96)
*/
- if ((pData->fileType == eTypeTTY || pData->fileType == eTypeCONSOLE)
+ if((pData->fileType == eTypeTTY || pData->fileType == eTypeCONSOLE)
#ifdef linux
- && e == EIO) {
+ && e == EIO
#else
- && e == EBADF) {
+ && 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;
+ iRet = RS_RET_SUSPENDED;
errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
} else {
untty();
goto again;
}
} else {
- iRet = RS_RET_DISABLE_ACTION;
+ iRet = RS_RET_SUSPENDED;
errno = e;
errmsg.LogError(0, NO_ERRCODE, "%s", pData->f_fname);
}
@@ -641,13 +696,8 @@ 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);
+ DBGPRINTF(" (%s)\n", pData->f_fname);
+ iRet = writeFile(ppString, iMsgOpts, pData);
ENDdoAction
@@ -730,7 +780,7 @@ CODESTARTparseSelectorAct
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");
+ DBGPRINTF("Could not allocate memory for dynaFileCache - selector disabled.\n");
}
break;
@@ -764,23 +814,18 @@ CODESTARTparseSelectorAct
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);
- }
+ /* at this stage, we ignore the return value of prepareFile, this is taken
+ * care of in later steps. -- rgerhards, 2009-03-19
+ */
+ prepareFile(pData, pData->f_fname);
- if ( pData->fd < 0 ){
+ if(pData->fd < 0 ) {
pData->fd = -1;
- dbgprintf("Error opening log file: %s\n", pData->f_fname);
+ 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)
+ if(strcmp((char*) p, _PATH_CONSOLE) == 0)
pData->fileType = eTypeCONSOLE;
break;
default:
@@ -815,6 +860,20 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
}
+BEGINdoHUP
+CODESTARTdoHUP
+ if(pData->bDynamicName) {
+ dynaFileFreeCacheEntries(pData);
+ pData->iCurrElt = -1; /* invalidate current element */
+ } else {
+ if(pData->fd != -1) {
+ close(pData->fd);
+ pData->fd = -1;
+ }
+ }
+ENDdoHUP
+
+
BEGINmodExit
CODESTARTmodExit
if(pszTplName != NULL)
@@ -825,6 +884,7 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_doHUP
ENDqueryEtryPt
diff --git a/tools/omfwd.c b/tools/omfwd.c
index 1b617ee1..1dd184ef 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -386,16 +386,19 @@ ENDtryResume
BEGINdoAction
char *psz; /* temporary buffering */
register unsigned l;
+ int iMaxLine;
CODESTARTdoAction
CHKiRet(doTryResume(pData));
+ iMaxLine = glbl.GetMaxLine();
+
dbgprintf(" %s:%s/%s\n", pData->f_hname, getFwdPt(pData),
pData->protocol == FORW_UDP ? "udp" : "tcp");
psz = (char*) ppString[0];
l = strlen((char*) psz);
- if (l > MAXLINE)
- l = MAXLINE;
+ if((int) l > iMaxLine)
+ l = iMaxLine;
# ifdef USE_NETZIP
/* Check if we should compress and, if so, do it. We also
@@ -407,10 +410,14 @@ CODESTARTdoAction
* rgerhards, 2006-11-30
*/
if(pData->compressionLevel && (l > MIN_SIZE_FOR_COMPRESS)) {
- Bytef out[MAXLINE+MAXLINE/100+12] = "z";
+ Bytef *out;
uLongf destLen = sizeof(out) / sizeof(Bytef);
uLong srcLen = l;
int ret;
+ /* TODO: optimize malloc sequence? -- rgerhards, 2008-09-02 */
+ CHKmalloc(out = (Bytef*) malloc(iMaxLine + iMaxLine/100 + 12));
+ out[0] = 'z';
+ out[1] = '\0';
ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz,
srcLen, pData->compressionLevel);
dbgprintf("Compressing message, length was %d now %d, return state %d.\n",
@@ -509,6 +516,9 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
* applies to TCP-based syslog only and is ignored when specified with UDP).
* That is not yet implemented.
* rgerhards, 2006-12-07
+ * In order to support IPv6 addresses, we must introduce an extension to
+ * the hostname. If it is in square brackets, whatever is in them is treated as
+ * the hostname - without any exceptions ;) -- rgerhards, 2008-08-05
*/
if(*p == '(') {
/* at this position, it *must* be an option indicator */
@@ -555,6 +565,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
*/
errmsg.LogError(0, NO_ERRCODE, "Option block not terminated in forwarding action.");
}
+
/* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
* now skip to port and then template name. rgerhards 2005-07-06
*/
@@ -604,6 +615,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
} else {
CHKmalloc(pData->f_hname = strdup((char*) q));
}
+dbgprintf("hostname '%s', port '%s'\n", pData->f_hname, pData->port);
/* process template */
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
diff --git a/tools/rsyslogd.8 b/tools/rsyslogd.8
index ecf26351..7abcf97a 100644
--- a/tools/rsyslogd.8
+++ b/tools/rsyslogd.8
@@ -1,7 +1,7 @@
.\" Copyright 2004-2008 Rainer Gerhards and Adiscon for the rsyslog modifications
.\" May be distributed under the GNU General Public License
.\"
-.TH RSYSLOGD 8 "11 July 2008" "Version 3.18.0" "Linux System Administration"
+.TH RSYSLOGD 8 "29 July 2008" "Version 3.21.1" "Linux System Administration"
.SH NAME
rsyslogd \- reliable and extended syslogd
.SH SYNOPSIS
@@ -21,12 +21,18 @@ rsyslogd \- reliable and extended syslogd
.I hostlist
]
.RB [ " \-n " ]
+.RB [ " \-N "
+.I level
+]
.br
.RB [ " \-q " ]
.RB [ " \-Q " ]
.RB [ " \-s "
.I domainlist
]
+.RB [ " \-u "
+.I userlevel
+]
.RB [ " \-v " ]
.RB [ " \-w " ]
.RB [ " \-x " ]
@@ -166,6 +172,19 @@ Avoid auto-backgrounding. This is needed especially if the
is started and controlled by
.BR init (8).
.TP
+.B "\-N " "level"
+Do a coNfig check. Do NOT run in regular mode, just check configuration
+file correctness.
+This option is meant to verify a config file. To do so, run rsyslogd
+interactively in foreground, specifying -f <config-file> and -N level.
+The level argument modifies behaviour. Currently, 0 is the same as
+not specifying the -N option at all (so this makes limited sense) and
+1 actually activates the code. Later, higher levels will mean more
+verbosity (this is a forward-compatibility option).
+.B rsyslogd
+is started and controlled by
+.BR init (8).
+.TP
.BI "\-q " "add hostname if DNS fails during ACL processing"
During ACL processing, hostnames are resolved to IP addresses for
performance reasons. If DNS fails during that process, the hostname
@@ -186,6 +205,18 @@ is specified and the host logging resolves to satu.infodrom.north.de
no domain would be cut, you will have to specify two domains like:
.BR "\-s north.de:infodrom.north.de" .
.TP
+.BI "\-u " "userlevel"
+This is a "catch all" option for some very seldomly-used user settings.
+The "userlevel" variable selects multiple things. Add the specific values
+to get the combined effect of them.
+A value of 1 prevents rsyslogd from parsing hostnames and tags inside
+messages.
+A value of 2 prevents rsyslogd from changing to the root directory. This
+is almost never a good idea in production use. This option was introduced
+in support of the internal testbed.
+To combine these two features, use a userlevel of 3 (1+2). Whenever you use
+an -u option, make sure you really understand what you do and why you do it.
+.TP
.B "\-v"
Print version and exit.
.TP
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 2c66daac..235bc52e 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -58,34 +58,6 @@
#include "config.h"
#include "rsyslog.h"
-/* change the following setting to e.g. 32768 if you would like to
- * support large message sizes for IHE (32k is the current maximum
- * needed for IHE). I was initially tempted to increase it to 32k,
- * but there is a large memory footprint with the current
- * implementation in rsyslog. This will change as the processing
- * changes, but I have re-set it to 1k, because the vast majority
- * of messages is below that and the memory savings is huge, at
- * least compared to the overall memory footprint.
- *
- * If you intend to receive Windows Event Log data (e.g. via
- * EventReporter - www.eventreporter.com), you might want to
- * increase this number to an even higher value, as event
- * log messages can be very lengthy.
- * rgerhards, 2005-07-05
- *
- * during my recent testing, it showed that 4k seems to be
- * the typical maximum for UDP based syslog. This is a IP stack
- * restriction. Not always ... but very often. If you go beyond
- * that value, be sure to test that rsyslogd actually does what
- * you think it should do ;) Also, it is a good idea to check the
- * doc set for anything on IHE - it most probably has information on
- * message sizes.
- * rgerhards, 2005-08-05
- *
- * I have increased the default message size to 2048 to be in sync
- * with recent IETF syslog standardization efforts.
- * rgerhards, 2006-11-30
- */
#define DEFUPRI (LOG_USER|LOG_NOTICE)
#define TIMERINTVL 30 /* interval for checking flush, mark */
@@ -100,16 +72,22 @@
#include <stdarg.h>
#include <time.h>
#include <assert.h>
-#include <libgen.h>
-#ifdef __sun
+#ifdef OS_SOLARIS
# include <errno.h>
+# include <fcntl.h>
+# include <stropts.h>
+# include <sys/termios.h>
+# include <sys/types.h>
#else
+# include <libgen.h>
# include <sys/errno.h>
#endif
+
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/file.h>
+#include <grp.h>
#if HAVE_SYS_TIMESPEC_H
# include <sys/timespec.h>
@@ -156,6 +134,7 @@
#include "vm.h"
#include "errmsg.h"
#include "datetime.h"
+#include "parser.h"
#include "sysvar.h"
/* definitions for objects we access */
@@ -247,7 +226,7 @@ static char *PidFile = _PATH_LOGPID; /* read-only after startup */
static pid_t myPid; /* our pid for use in self-generated messages, e.g. on startup */
/* mypid is read-only after the initial fork() */
-static int restart = 0; /* do restart (config read) - multithread safe */
+static int bHadHUP = 0; /* did we have a HUP? */
static int bParseHOSTNAMEandTAG = 1; /* global config var: should the hostname and tag be
* parsed inside message - rgerhards, 2006-03-13 */
@@ -255,6 +234,7 @@ static int bFinished = 0; /* used by termination signal handler, read-only excep
* is either 0 or the number of the signal that requested the
* termination.
*/
+static int iConfigVerify = 0; /* is this just a config verify run? */
/* Intervals at which we flush out "message repeated" messages,
* in seconds after previous message is logged. After each flush,
@@ -276,15 +256,16 @@ typedef struct legacyOptsLL_s {
legacyOptsLL_t *pLegacyOptsLL = NULL;
/* global variables for config file state */
-static int bDropTrailingLF = 1; /* drop trailing LF's on reception? */
+int bDropTrailingLF = 1; /* drop trailing LF's on reception? */
int iCompatibilityMode = 0; /* version we should be compatible with; 0 means sysklogd. It is
the default, so if no -c<n> option is given, we make ourselvs
as compatible to sysklogd as possible. */
static int bDebugPrintTemplateList = 1;/* output template list in debug mode? */
static int bDebugPrintCfSysLineHandlerList = 1;/* output cfsyslinehandler list in debug mode? */
static int bDebugPrintModuleList = 1;/* output module list in debug mode? */
-static uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */
-static int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */
+uchar cCCEscapeChar = '\\';/* character to be used to start an escape sequence for control chars */
+int bEscapeCCOnRcv = 1; /* escape control characters on reception: 0 - no, 1 - yes */
+static int bErrMsgToStderr = 1; /* print error messages to stderr (in addition to everything else)? */
int bReduceRepeatMsgs; /* reduce repeated message - 0 - no, 1 - yes */
int bActExecWhenPrevSusp; /* execute action only when previous one was suspended? */
int iActExecOnceInterval = 0; /* execute action once every nn seconds */
@@ -297,11 +278,13 @@ static int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode -
* If the main queue is either not yet ready or not running in
* queueing mode (mode DIRECT!), then this is set to 0.
*/
+static int uidDropPriv = 0; /* user-id to which priveleges should be dropped to (AFTER init()!) */
+static int gidDropPriv = 0; /* group-id to which priveleges should be dropped to (AFTER init()!) */
extern int errno;
/* main message queue and its configuration parameters */
-static queue_t *pMsgQueue = NULL; /* the main message queue */
+static qqueue_t *pMsgQueue = NULL; /* the main message queue */
static int iMainMsgQueueSize = 10000; /* size of the main message queue above */
static int iMainMsgQHighWtrMark = 8000; /* high water mark for disk-assisted queues */
static int iMainMsgQLowWtrMark = 2000; /* low water mark for disk-assisted queues */
@@ -418,7 +401,7 @@ static void processImInternal(void);
static int usage(void)
{
fprintf(stderr, "usage: rsyslogd [-c<version>] [-46AdnqQvwx] [-l<hostlist>] [-s<domainlist>]\n"
- " [-f<conffile>] [-i<pidfile>] [-M<module load path>]\n"
+ " [-f<conffile>] [-i<pidfile>] [-N<level>] [-M<module load path>]\n"
" [-u<number>]\n"
"To run rsyslogd in native mode, use \"rsyslogd -c3 <other options>\"\n\n"
"For further information see http://www.rsyslog.com/doc\n");
@@ -603,8 +586,23 @@ void untty(void)
* rgerhards, 2008-05-16:
* I added an additional calling parameter (hnameIP) to enable specifying the IP
* of a remote host.
+ *
+ * rgerhards, 2008-09-11:
+ * Interface change: added new parameter "InputName", permits the input to provide
+ * a string that identifies it. May be NULL, but must be a valid char* pointer if
+ * non-NULL.
+ *
+ * rgerhards, 2008-10-06:
+ * Interface change: added new parameter "stTime", which enables the caller to provide
+ * a timestamp that is to be used as timegenerated instead of the current system time.
+ * This is meant to facilitate performance optimization. Some inputs support such modes.
+ * If stTime is NULL, the current system time is used.
+ *
+ * rgerhards, 2008-10-09:
+ * interface change: bParseHostname removed, now in flags
*/
-rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int bParseHost, int flags, flowControl_t flowCtlType)
+static inline rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int flags, flowControl_t flowCtlType,
+ uchar *pszInputName, struct syslogTime *stTime, time_t ttGenTime)
{
DEFiRet;
register uchar *p;
@@ -612,11 +610,16 @@ rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int bParseHost, int
msg_t *pMsg;
/* Now it is time to create the message object (rgerhards) */
- CHKiRet(msgConstruct(&pMsg));
+ if(stTime == NULL) {
+ CHKiRet(msgConstruct(&pMsg));
+ } else {
+ CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime));
+ }
+ if(pszInputName != NULL)
+ MsgSetInputName(pMsg, (char*) pszInputName);
MsgSetFlowControlType(pMsg, flowCtlType);
MsgSetRawMsg(pMsg, (char*)msg);
- pMsg->bParseHOSTNAME = bParseHost;
/* test for special codes */
pri = DEFUPRI;
p = msg;
@@ -641,7 +644,7 @@ rsRetVal printline(uchar *hname, uchar *hnameIP, uchar *msg, int bParseHost, int
* the message was received from (that, for obvious reasons,
* being the local host). rgerhards 2004-11-16
*/
- if(bParseHost == 0)
+ if((pMsg->msgFlags & PARSE_HOSTNAME) == 0)
MsgSetHOSTNAME(pMsg, (char*)hname);
MsgSetRcvFrom(pMsg, (char*)hname);
CHKiRet(MsgSetRcvFromIP(pMsg, hnameIP));
@@ -698,18 +701,34 @@ finalize_it:
* rgerhards, 2008-05-16:
* I added an additional calling parameter (hnameIP) to enable specifying the IP
* of a remote host.
+ *
+ * rgerhards, 2008-09-11:
+ * Interface change: added new parameter "InputName", permits the input to provide
+ * a string that identifies it. May be NULL, but must be a valid char* pointer if
+ * non-NULL.
+ *
+ * rgerhards, 2008-10-06:
+ * Interface change: added new parameter "stTime", which enables the caller to provide
+ * a timestamp that is to be used as timegenerated instead of the current system time.
+ * This is meant to facilitate performance optimization. Some inputs support such modes.
+ * If stTime is NULL, the current system time is used.
+ *
+ * rgerhards, 2008-10-09:
+ * interface change: bParseHostname removed, now in flags
*/
rsRetVal
-parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bParseHost, int flags, flowControl_t flowCtlType)
+parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int flags, flowControl_t flowCtlType,
+ uchar *pszInputName, struct syslogTime *stTime, time_t ttGenTime)
{
DEFiRet;
register int iMsg;
uchar *pMsg;
uchar *pData;
uchar *pEnd;
- uchar tmpline[MAXLINE + 1];
+ int iMaxLine;
+ uchar *tmpline = NULL;
# ifdef USE_NETZIP
- uchar deflateBuf[MAXLINE + 1];
+ uchar *deflateBuf = NULL;
uLongf iLenDefBuf;
# endif
@@ -718,6 +737,15 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
assert(msg != NULL);
assert(len >= 0);
+ /* we first allocate work buffers large enough to hold the configured maximum
+ * size of a message. Over time, we should change this to a more optimal way, i.e.
+ * by calling the function with the actual length of the message to be parsed.
+ * rgerhards, 2008-09-02
+ *
+ * TODO: optimize buffer handling */
+ iMaxLine = glbl.GetMaxLine();
+ CHKmalloc(tmpline = malloc(sizeof(uchar) * (iMaxLine + 1)));
+
/* we first check if we have a NUL character at the very end of the
* message. This seems to be a frequent problem with a number of senders.
* So I have now decided to drop these NULs. However, if they are intentional,
@@ -754,14 +782,15 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
*/
if(len > 0 && *msg == 'z') { /* compressed data present? (do NOT change order if conditions!) */
/* we have compressed data, so let's deflate it. We support a maximum
- * message size of MAXLINE. If it is larger, an error message is logged
+ * message size of iMaxLine. If it is larger, an error message is logged
* and the message is dropped. We do NOT try to decompress larger messages
* as such might be used for denial of service. It might happen to later
* builds that such functionality be added as an optional, operator-configurable
* feature.
*/
int ret;
- iLenDefBuf = MAXLINE;
+ iLenDefBuf = iMaxLine;
+ CHKmalloc(deflateBuf = malloc(sizeof(uchar) * (iMaxLine + 1)));
ret = uncompress((uchar *) deflateBuf, &iLenDefBuf, (uchar *) msg+1, len-1);
dbgprintf("Compressed message uncompressed with status %d, length: new %ld, old %d.\n",
ret, (long) iLenDefBuf, len-1);
@@ -794,13 +823,13 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
# endif /* ifdef USE_NETZIP */
while(pData < pEnd) {
- if(iMsg >= MAXLINE) {
+ if(iMsg >= iMaxLine) {
/* emergency, we now need to flush, no matter if
* we are at end of message or not...
*/
- if(iMsg == MAXLINE) {
+ if(iMsg == iMaxLine) {
*(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */
- printline(hname, hnameIP, tmpline, bParseHost, flags, flowCtlType);
+ printline(hname, hnameIP, tmpline, flags, flowCtlType, pszInputName, stTime, ttGenTime);
} else {
/* This case in theory never can happen. If it happens, we have
* a logic error. I am checking for it, because if I would not,
@@ -809,7 +838,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
* (I couldn't do any more smart things anyway...).
* rgerhards, 2007-9-20
*/
- dbgprintf("internal error: iMsg > MAXLINE in printchopped()\n");
+ dbgprintf("internal error: iMsg > max msg size in printchopped()\n");
}
FINALIZE; /* in this case, we are done... nothing left we can do */
}
@@ -817,7 +846,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
/* changed to the sequence (somewhat) proposed in
* draft-ietf-syslog-protocol-19. rgerhards, 2006-11-30
*/
- if(iMsg + 3 < MAXLINE) { /* do we have space? */
+ if(iMsg + 3 < iMaxLine) { /* do we have space? */
*(pMsg + iMsg++) = cCCEscapeChar;
*(pMsg + iMsg++) = '0';
*(pMsg + iMsg++) = '0';
@@ -837,7 +866,7 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
* we known now what's going on.
* rgerhards, 2007-07-17
*/
- if(iMsg + 3 < MAXLINE) { /* do we have space? */
+ if(iMsg + 3 < iMaxLine) { /* do we have space? */
*(pMsg + iMsg++) = cCCEscapeChar;
*(pMsg + iMsg++) = '0' + ((*pData & 0300) >> 6);
*(pMsg + iMsg++) = '0' + ((*pData & 0070) >> 3);
@@ -852,9 +881,15 @@ parseAndSubmitMessage(uchar *hname, uchar *hnameIP, uchar *msg, int len, int bPa
*(pMsg + iMsg) = '\0'; /* space *is* reserved for this! */
/* typically, we should end up here! */
- printline(hname, hnameIP, tmpline, bParseHost, flags, flowCtlType);
+ printline(hname, hnameIP, tmpline, flags, flowCtlType, pszInputName, stTime, ttGenTime);
finalize_it:
+ if(tmpline != NULL)
+ free(tmpline);
+# ifdef USE_NETZIP
+ if(deflateBuf != NULL)
+ free(deflateBuf);
+# endif
RETiRet;
}
@@ -867,10 +902,11 @@ rsRetVal
submitErrMsg(int iErr, uchar *msg)
{
DEFiRet;
- iRet = logmsgInternal(iErr, LOG_SYSLOG|LOG_ERR, msg, ADDDATE);
+ iRet = logmsgInternal(iErr, LOG_SYSLOG|LOG_ERR, msg, 0);
RETiRet;
}
+
/* rgerhards 2004-11-09: the following is a function that can be used
* to log a message orginating from the syslogd itself. In sysklogd code,
* this is done by simply calling logmsg(). However, logmsg() is changed in
@@ -888,6 +924,7 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
DEFiRet;
CHKiRet(msgConstruct(&pMsg));
+ MsgSetInputName(pMsg, "rsyslogd");
MsgSetUxTradMsg(pMsg, (char*)msg);
MsgSetRawMsg(pMsg, (char*)msg);
MsgSetHOSTNAME(pMsg, (char*)glbl.GetLocalHostName());
@@ -906,9 +943,20 @@ logmsgInternal(int iErr, int pri, uchar *msg, int flags)
pMsg->iFacility = LOG_FAC(pri);
pMsg->iSeverity = LOG_PRI(pri);
pMsg->bParseHOSTNAME = 0;
- datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */
flags |= INTERNAL_MSG;
+ /* we now check if we should print internal messages out to stderr. This was
+ * suggested by HKS as a way to help people troubleshoot rsyslog configuration
+ * (by running it interactively. This makes an awful lot of sense, so I add
+ * it here. -- rgerhards, 2008-07-28
+ * Note that error messages can not be disable during a config verify. This
+ * permits us to process unmodified config files which otherwise contain a
+ * supressor statement.
+ */
+ if(bErrMsgToStderr || iConfigVerify) {
+ fprintf(stderr, "rsyslogd: %s\n", msg);
+ }
+
if(bHaveMainQueue == 0) { /* not yet in queued mode */
iminternalAddMsg(pri, pMsg, flags);
} else {
@@ -1028,7 +1076,12 @@ static rsRetVal shouldProcessThisMessage(selector_t *f, msg_t *pMsg, int *bProce
break;
case FIOP_REGEX:
if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue,
- (unsigned char*) pszPropVal) == 0)
+ (unsigned char*) pszPropVal, 0) == RS_RET_OK)
+ bRet = 1;
+ break;
+ case FIOP_EREREGEX:
+ if(rsCStrSzStrMatchRegex(f->f_filterData.prop.pCSCompValue,
+ (unsigned char*) pszPropVal, 1) == RS_RET_OK)
bRet = 1;
break;
default:
@@ -1147,9 +1200,6 @@ processMsg(msg_t *pMsg)
/* The consumer of dequeued messages. This function is called by the
* queue engine on dequeueing of a message. It runs on a SEPARATE
* THREAD.
- * NOTE: Having more than one worker requires guarding of some
- * message object structures and potentially others - need to be checked
- * before we support multiple worker threads on the message queue.
* Please note: the message object is destructed by the queue itself!
*/
static rsRetVal
@@ -1160,6 +1210,9 @@ msgConsumer(void __attribute__((unused)) *notNeeded, void *pUsr)
assert(pMsg != NULL);
+ if((pMsg->msgFlags & NEEDS_PARSING) != 0) {
+ parseMsg(pMsg);
+ }
processMsg(pMsg);
msgDestruct(&pMsg);
@@ -1281,12 +1334,13 @@ static int parseRFCStructuredData(char **pp2parse, char *pResult)
*
* rger, 2005-11-24
*/
-static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
+int parseRFCSyslogMsg(msg_t *pMsg, int flags)
{
char *p2parse;
char *pBuf;
int bContParse = 1;
+ BEGINfunc
assert(pMsg != NULL);
assert(pMsg->pszUxTradMsg != NULL);
p2parse = (char*) pMsg->pszUxTradMsg;
@@ -1311,14 +1365,14 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
*/
/* TIMESTAMP */
- if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == FALSE) {
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
+ if(flags & IGNDATE) {
+ /* we need to ignore the msg data, so simply copy over reception date */
+ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
+ }
+ } else {
dbgprintf("no TIMESTAMP detected!\n");
bContParse = 0;
- flags |= ADDDATE;
- }
-
- if (flags & ADDDATE) {
- datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */
}
/* HOSTNAME */
@@ -1360,6 +1414,7 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
MsgSetMSG(pMsg, p2parse);
free(pBuf);
+ ENDfunc
return 0; /* all ok */
}
@@ -1377,7 +1432,7 @@ static int parseRFCSyslogMsg(msg_t *pMsg, int flags)
* but I thought I log it in this comment.
* rgerhards, 2006-01-10
*/
-static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
+int parseLegacySyslogMsg(msg_t *pMsg, int flags)
{
char *p2parse;
char *pBuf;
@@ -1385,21 +1440,25 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
cstr_t *pStrB;
int iCnt;
int bTAGCharDetected;
+ BEGINfunc
assert(pMsg != NULL);
assert(pMsg->pszUxTradMsg != NULL);
p2parse = (char*) pMsg->pszUxTradMsg;
- /* Check to see if msg contains a timestamp. We stary trying with a
- * high-precision one...
+ /* Check to see if msg contains a timestamp. We start by assuming
+ * that the message timestamp is the time of reciption (which we
+ * generated ourselfs and then try to actually find one inside the
+ * message. There we go from high-to low precison and are done
+ * when we find a matching one. -- rgerhards, 2008-09-16
*/
- if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == TRUE) {
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
/* we are done - parse pointer is moved by ParseTIMESTAMP3339 */;
- } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == TRUE) {
+ } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
/* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
} else if(*p2parse == ' ') { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
++p2parse; /* move over space */
- if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == TRUE) {
+ if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse) == RS_RET_OK) {
/* indeed, we got it! */
/* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
} else {
@@ -1407,19 +1466,12 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
* for this try.
*/
--p2parse;
- flags |= ADDDATE;
}
- } else {
- flags |= ADDDATE;
}
- /* here we need to check if the timestamp is valid. If it is not,
- * we can not continue to parse but must treat the rest as the
- * MSG part of the message (as of RFC 3164).
- * rgerhards 2004-12-03
- */
- if(flags & ADDDATE) {
- datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */
+ if(flags & IGNDATE) {
+ /* we need to ignore the msg data, so simply copy over reception date */
+ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
}
/* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we
@@ -1555,6 +1607,7 @@ static int parseLegacySyslogMsg(msg_t *pMsg, int flags)
/* The rest is the actual MSG */
MsgSetMSG(pMsg, p2parse);
+ ENDfunc
return 0; /* all ok */
}
@@ -1574,7 +1627,7 @@ submitMsg(msg_t *pMsg)
ISOBJ_TYPE_assert(pMsg, msg);
MsgPrepareEnqueue(pMsg);
- queueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
+ qqueueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
RETiRet;
}
@@ -1635,7 +1688,7 @@ logmsg(msg_t *pMsg, int flags)
/* now submit the message to the main queue - then we are done */
pMsg->msgFlags = flags;
MsgPrepareEnqueue(pMsg);
- queueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
+ qqueueEnqObj(pMsgQueue, pMsg->flowCtlType, (void*) pMsg);
ENDfunc
}
@@ -1667,6 +1720,10 @@ DEFFUNC_llExecFunc(flushRptdMsgsActions)
BEGINfunc
LockObj(pAction);
+ /* TODO: time() performance: the call below could be moved to
+ * the beginn of the llExec(). This makes it slightly less correct, but
+ * in an acceptable way. -- rgerhards, 2008-09-16
+ */
if (pAction->f_prevcount && time(NULL) >= REPEATTIME(pAction)) {
dbgprintf("flush %s: repeated %d times, %d sec.\n",
module.GetStateName(pAction->pMod), pAction->f_prevcount,
@@ -1777,7 +1834,7 @@ void legacyOptsParseTCP(char ch, char *arg)
static char conflict = '\0';
if((conflict == 'g' && ch == 't') || (conflict == 't' && ch == 'g')) {
- fprintf(stderr, "rsyslog: If you want to use both -g and -t, use directives instead, -%c ignored.\n", ch);
+ fprintf(stderr, "rsyslogd: If you want to use both -g and -t, use directives instead, -%c ignored.\n", ch);
return;
} else
conflict = ch;
@@ -1850,16 +1907,24 @@ void legacyOptsParseTCP(char ch, char *arg)
* a minimal delay, but it is much cleaner than the approach of doing everything
* inside the signal handler.
* rgerhards, 2005-10-26
+ * Note: we do not call dbgprintf() as this may cause us to block in case something
+ * with the threading is wrong.
*/
static void doDie(int sig)
{
+# define MSG1 "DoDie called.\n"
+# define MSG2 "DoDie called 5 times - unconditional exit\n"
static int iRetries = 0; /* debug aid */
- printf("DoDie called.\n");
+ if(Debug || NoFork)
+ write(1, MSG1, sizeof(MSG1) - 1);
if(iRetries++ == 4) {
- printf("DoDie called 5 times - unconditional exit\n");
+ if(Debug || NoFork)
+ write(1, MSG2, sizeof(MSG2) - 1);
abort();
}
bFinished = sig;
+# undef MSG1
+# undef MSG2
}
@@ -1907,7 +1972,7 @@ die(int sig)
/* close the inputs */
dbgprintf("Terminating input threads...\n");
- thrdTerminateAll(); /* TODO: inputs only, please */
+ thrdTerminateAll();
/* and THEN send the termination log message (see long comment above) */
if (sig) {
@@ -1916,12 +1981,12 @@ die(int sig)
"\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"]" " exiting on signal %d.",
(int) myPid, sig);
errno = 0;
- logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, ADDDATE);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
}
/* drain queue (if configured so) and stop main queue worker thread pool */
dbgprintf("Terminating main queue...\n");
- queueDestruct(&pMsgQueue);
+ qqueueDestruct(&pMsgQueue);
pMsgQueue = NULL;
/* Free ressources and close connections. This includes flushing any remaining
@@ -1992,16 +2057,21 @@ static void doexit()
}
-/* set the action resume interval
- */
+/* set the maximum message size */
+static rsRetVal setMaxMsgSize(void __attribute__((unused)) *pVal, int iNewVal)
+{
+ return glbl.SetMaxLine(iNewVal);
+}
+
+
+/* set the action resume interval */
static rsRetVal setActionResumeInterval(void __attribute__((unused)) *pVal, int iNewVal)
{
return actionSetGlobalResumeInterval(iNewVal);
}
-/* set the processes umask (upon configuration request)
- */
+/* set the processes umask (upon configuration request) */
static rsRetVal setUmask(void __attribute__((unused)) *pVal, int iUmask)
{
umask(iUmask);
@@ -2011,6 +2081,56 @@ static rsRetVal setUmask(void __attribute__((unused)) *pVal, int iUmask)
}
+/* drop to specified group
+ * if something goes wrong, the function never returns
+ * Note that such an abort can cause damage to on-disk structures, so we should
+ * re-design the "interface" in the long term. -- rgerhards, 2008-11-26
+ */
+static void doDropPrivGid(int iGid)
+{
+ int res;
+ uchar szBuf[1024];
+
+ res = setgroups(0, NULL); /* remove all supplementary group IDs */
+ if(res) {
+ perror("could not remove supplemental group IDs");
+ exit(1);
+ }
+ DBGPRINTF("setgroups(0, NULL): %d\n", res);
+ res = setgid(iGid);
+ if(res) {
+ /* if we can not set the userid, this is fatal, so let's unconditionally abort */
+ perror("could not set requested group id");
+ exit(1);
+ }
+ DBGPRINTF("setgid(%d): %d\n", iGid, res);
+ snprintf((char*)szBuf, sizeof(szBuf)/sizeof(uchar), "rsyslogd's groupid changed to %d", iGid);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0);
+}
+
+
+/* drop to specified user
+ * if something goes wrong, the function never returns
+ * Note that such an abort can cause damage to on-disk structures, so we should
+ * re-design the "interface" in the long term. -- rgerhards, 2008-11-19
+ */
+static void doDropPrivUid(int iUid)
+{
+ int res;
+ uchar szBuf[1024];
+
+ res = setuid(iUid);
+ if(res) {
+ /* if we can not set the userid, this is fatal, so let's unconditionally abort */
+ perror("could not set requested userid");
+ exit(1);
+ }
+ DBGPRINTF("setuid(%d): %d\n", iUid, res);
+ snprintf((char*)szBuf, sizeof(szBuf)/sizeof(uchar), "rsyslogd's userid changed to %d", iUid);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, szBuf, 0);
+}
+
+
/* helper to freeSelectors(), used with llExecFunc() to flush
* pending output. -- rgerhards, 2007-08-02
* We do not need to lock the action object here as the processing
@@ -2142,8 +2262,8 @@ static void dbgPrintInitInfo(void)
cCCEscapeChar);
dbgprintf("Main queue size %d messages.\n", iMainMsgQueueSize);
- dbgprintf("Main queue worker threads: %d, Perists every %d updates.\n",
- iMainMsgQueueNumWorkers, iMainMsgQPersistUpdCnt);
+ dbgprintf("Main queue worker threads: %d, wThread shutdown: %d, Perists every %d updates.\n",
+ iMainMsgQueueNumWorkers, iMainMsgQtoWrkShutdown, iMainMsgQPersistUpdCnt);
dbgprintf("Main queue timeouts: shutdown: %d, action completion shutdown: %d, enq: %d\n",
iMainMsgQtoQShutdown, iMainMsgQtoActShutdown, iMainMsgQtoEnq);
dbgprintf("Main queue watermarks: high: %d, low: %d, discard: %d, discard-severity: %d\n",
@@ -2153,13 +2273,11 @@ static void dbgPrintInitInfo(void)
/* TODO: add
iActionRetryCount = 0;
iActionRetryInterval = 30000;
- static int iMainMsgQtoWrkShutdown = 60000;
static int iMainMsgQtoWrkMinMsgs = 100;
static int iMainMsgQbSaveOnShutdown = 1;
iMainMsgQueMaxDiskSpace = 0;
- setQPROP(queueSettoWrkShutdown, "$MainMsgQueueTimeoutWorkerThreadShutdown", 5000);
- setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100);
- setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", 100);
+ setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", 1);
*/
dbgprintf("Work Directory: '%s'.\n", glbl.GetWorkDir());
}
@@ -2196,11 +2314,15 @@ startInputModules(void)
/* INIT -- Initialize syslogd from configuration table
* init() is called at initial startup AND each time syslogd is HUPed
+ * Note that if iConfigVerify is set, only the config file is verified but nothing
+ * else happens. -- rgerhards, 2008-07-28
*/
-static void
+static rsRetVal
init(void)
{
DEFiRet;
+ rsRetVal localRet;
+ int iNbrActions;
char cbuf[BUFSIZ];
char bufStartUpMsg[512];
struct sigaction sigAct;
@@ -2217,7 +2339,7 @@ init(void)
/* delete the message queue, which also flushes all messages left over */
if(pMsgQueue != NULL) {
dbgprintf("deleting main message queue\n");
- queueDestruct(&pMsgQueue); /* delete pThis here! */
+ qqueueDestruct(&pMsgQueue); /* delete pThis here! */
pMsgQueue = NULL;
}
@@ -2243,22 +2365,42 @@ init(void)
*/
conf.cfsysline((uchar*)"ResetConfigVariables");
+ conf.ReInitConf();
+
/* open the configuration file */
- if((iRet = conf.processConfFile(ConfFile)) != RS_RET_OK) {
+ localRet = conf.processConfFile(ConfFile);
+ CHKiRet(conf.GetNbrActActions(&iNbrActions));
+
+ if(localRet != RS_RET_OK) {
+ errmsg.LogError(0, localRet, "CONFIG ERROR: could not interpret master config file '%s'.", ConfFile);
+ } else if(iNbrActions == 0) {
+ errmsg.LogError(0, RS_RET_NO_ACTIONS, "CONFIG ERROR: there are no active actions configured. Inputs will "
+ "run, but no output whatsoever is created.");
+ }
+
+ if(localRet != RS_RET_OK || iNbrActions == 0) {
/* rgerhards: this code is executed to set defaults when the
* config file could not be opened. We might think about
* abandoning the run in this case - but this, too, is not
* very clever... So we stick with what we have.
* We ignore any errors while doing this - we would be lost anyhow...
*/
+ errmsg.LogError(0, NO_ERRCODE, "EMERGENCY CONFIGURATION ACTIVATED - fix rsyslog config file!");
selector_t *f = NULL;
- char szTTYNameBuf[_POSIX_TTY_NAME_MAX+1]; /* +1 for NULL character */
- dbgprintf("primary config file could not be opened - using emergency definitions.\n");
+
+ /* note: we previously used _POSIY_TTY_NAME_MAX+1, but this turned out to be
+ * too low on linux... :-S -- rgerhards, 2008-07-28
+ */
+ char szTTYNameBuf[128];
conf.cfline((uchar*)"*.ERR\t" _PATH_CONSOLE, &f);
+ conf.cfline((uchar*)"syslog.*\t" _PATH_CONSOLE, &f);
conf.cfline((uchar*)"*.PANIC\t*", &f);
+ conf.cfline((uchar*)"syslog.*\troot", &f);
if(ttyname_r(0, szTTYNameBuf, sizeof(szTTYNameBuf)) == 0) {
snprintf(cbuf,sizeof(cbuf), "*.*\t%s", szTTYNameBuf);
conf.cfline((uchar*)cbuf, &f);
+ } else {
+ dbgprintf("error %d obtaining controlling terminal, not using that emergency rule\n", errno);
}
selectorAddList(f);
}
@@ -2296,13 +2438,20 @@ init(void)
}
}
+ /* we are done checking the config - now validate if we should actually run or not.
+ * If not, terminate. -- rgerhards, 2008-07-25
+ */
+ if(iConfigVerify)
+ ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
+
/* switch the message object to threaded operation, if necessary */
+/* TODO:XXX: I think we must do this also if we have action queues! -- rgerhards, 2009-01-26 */
if(MainMsgQueType == QUEUETYPE_DIRECT || iMainMsgQueueNumWorkers > 1) {
MsgEnableThreadSafety();
}
/* create message queue */
- CHKiRet_Hdlr(queueConstruct(&pMsgQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) {
+ CHKiRet_Hdlr(qqueueConstruct(&pMsgQueue, MainMsgQueType, iMainMsgQueueNumWorkers, iMainMsgQueueSize, msgConsumer)) {
/* no queue is fatal, we need to give up in that case... */
fprintf(stderr, "fatal error %d: could not create message queue - rsyslogd can not run!\n", iRet);
exit(1);
@@ -2320,29 +2469,29 @@ init(void)
errmsg.LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, running with default setting", iRet); \
}
- setQPROP(queueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize);
- setQPROP(queueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace);
- setQPROPstr(queueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
- setQPROP(queueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
- setQPROP(queueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
- setQPROP(queueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
- setQPROP(queueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
- setQPROP(queueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq);
- setQPROP(queueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark);
- setQPROP(queueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark);
- setQPROP(queueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark);
- setQPROP(queueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity);
- setQPROP(queueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs);
- setQPROP(queueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown);
- setQPROP(queueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown);
- setQPROP(queueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr);
- setQPROP(queueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr);
+ setQPROP(qqueueSetMaxFileSize, "$MainMsgQueueFileSize", iMainMsgQueMaxFileSize);
+ setQPROP(qqueueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", iMainMsgQueMaxDiskSpace);
+ setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", pszMainMsgQFName);
+ setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", iMainMsgQPersistUpdCnt);
+ setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", iMainMsgQtoQShutdown );
+ setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", iMainMsgQtoActShutdown);
+ setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", iMainMsgQtoWrkShutdown);
+ setQPROP(qqueueSettoEnq, "$MainMsgQueueTimeoutEnqueue", iMainMsgQtoEnq);
+ setQPROP(qqueueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", iMainMsgQHighWtrMark);
+ setQPROP(qqueueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", iMainMsgQLowWtrMark);
+ setQPROP(qqueueSetiDiscardMrk, "$MainMsgQueueDiscardMark", iMainMsgQDiscardMark);
+ setQPROP(qqueueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", iMainMsgQDiscardSeverity);
+ setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", iMainMsgQWrkMinMsgs);
+ setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", bMainMsgQSaveOnShutdown);
+ setQPROP(qqueueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", iMainMsgQDeqSlowdown);
+ setQPROP(qqueueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", iMainMsgQueueDeqtWinFromHr);
+ setQPROP(qqueueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", iMainMsgQueueDeqtWinToHr);
# undef setQPROP
# undef setQPROPstr
/* ... and finally start the queue! */
- CHKiRet_Hdlr(queueStart(pMsgQueue)) {
+ CHKiRet_Hdlr(qqueueStart(pMsgQueue)) {
/* no queue is fatal, we need to give up in that case... */
fprintf(stderr, "fatal error %d: could not start message queue - rsyslogd can not run!\n", iRet);
exit(1);
@@ -2369,7 +2518,7 @@ init(void)
" [origin software=\"rsyslogd\" " "swVersion=\"" VERSION \
"\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] restart",
(int) myPid);
- logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, ADDDATE);
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, 0);
memset(&sigAct, 0, sizeof (sigAct));
sigemptyset(&sigAct.sa_mask);
@@ -2377,7 +2526,9 @@ init(void)
sigaction(SIGHUP, &sigAct, NULL);
dbgprintf(" (re)started.\n");
- ENDfunc
+
+finalize_it:
+ RETiRet;
}
@@ -2461,20 +2612,18 @@ static rsRetVal setMainMsgQueType(void __attribute__((unused)) *pVal, uchar *psz
* The following function is resposible for handling a SIGHUP signal. Since
* we are now doing mallocs/free as part of init we had better not being
* doing this during a signal handler. Instead this function simply sets
- * a flag variable which will tell the main loop to go through a restart.
+ * a flag variable which will tells the main loop to do "the right thing".
*/
void sighup_handler()
{
struct sigaction sigAct;
- restart = 1;
+ bHadHUP = 1;
memset(&sigAct, 0, sizeof (sigAct));
sigemptyset(&sigAct.sa_mask);
sigAct.sa_handler = sighup_handler;
sigaction(SIGHUP, &sigAct, NULL);
-
- return;
}
@@ -2496,6 +2645,49 @@ static void processImInternal(void)
}
+/* helper to doHUP(), this "HUPs" each action. The necessary locking
+ * is done inside the action class and nothing we need to take care of.
+ * rgerhards, 2008-10-22
+ */
+DEFFUNC_llExecFunc(doHUPActions)
+{
+ BEGINfunc
+ actionCallHUPHdlr((action_t*) pData);
+ ENDfunc
+ return RS_RET_OK; /* we ignore errors, we can not do anything either way */
+}
+
+
+/* This function processes a HUP after one has been detected. Note that this
+ * is *NOT* the sighup handler. The signal is recorded by the handler, that record
+ * detected inside the mainloop and then this function is called to do the
+ * real work. -- rgerhards, 2008-10-22
+ */
+static inline void
+doHUP(void)
+{
+ selector_t *f;
+ char buf[512];
+
+ snprintf(buf, sizeof(buf) / sizeof(char),
+ " [origin software=\"rsyslogd\" " "swVersion=\"" VERSION
+ "\" x-pid=\"%d\" x-info=\"http://www.rsyslog.com\"] rsyslogd was HUPed, type '%s'.",
+ (int) myPid, glbl.GetHUPisRestart() ? "restart" : "lightweight");
+ errno = 0;
+ logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0);
+
+ if(glbl.GetHUPisRestart()) {
+ DBGPRINTF("Received SIGHUP, configured to be restart, reloading rsyslogd.\n");
+ init(); /* main queue is stopped as part of init() */
+ } else {
+ DBGPRINTF("Received SIGHUP, configured to be a non-restart type of HUP - notifying actions.\n");
+ for(f = Files; f != NULL ; f = f->f_next) {
+ llExecFunc(&f->llActList, doHUPActions, NULL);
+ }
+ }
+}
+
+
/* This is the main processing loop. It is called after successful initialization.
* When it returns, the syslogd terminates.
* Its sole function is to provide some housekeeping things. The real work is done
@@ -2552,11 +2744,9 @@ mainloop(void)
if(bReduceRepeatMsgs == 1)
doFlushRptdMsgs();
- if(restart) {
- dbgprintf("\nReceived SIGHUP, reloading rsyslogd.\n");
- /* main queue is stopped as part of init() */
- init();
- restart = 0;
+ if(bHadHUP) {
+ doHUP();
+ bHadHUP = 0;
continue;
}
}
@@ -2644,7 +2834,6 @@ static rsRetVal loadBuildInModules(void)
* is that rsyslog will terminate if we can not register our built-in config commands.
* This, I think, is the right thing to do. -- rgerhards, 2007-07-31
*/
-// CHKiRet(regCfSysLineHdlr((uchar *)"workdirectory", 0, eCmdHdlrGetWord, NULL, &pszWorkDir, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"actionresumeretrycount", 0, eCmdHdlrInt, NULL, &glbliActionResumeRetryCount, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuefilename", 0, eCmdHdlrGetWord, NULL, &pszMainMsgQFName, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"mainmsgqueuesize", 0, eCmdHdlrInt, NULL, &iMainMsgQueueSize, NULL));
@@ -2685,6 +2874,12 @@ static rsRetVal loadBuildInModules(void)
NULL, &bDebugPrintCfSysLineHandlerList, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"moddir", 0, eCmdHdlrGetWord, NULL, &pModDir, NULL));
CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"errormessagestostderr", 0, eCmdHdlrBinary, NULL, &bErrMsgToStderr, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, setMaxMsgSize, NULL, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptouser", 0, eCmdHdlrUID, NULL, &uidDropPriv, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptouserid", 0, eCmdHdlrInt, NULL, &uidDropPriv, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroup", 0, eCmdHdlrGID, NULL, &gidDropPriv, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"privdroptogroupid", 0, eCmdHdlrGID, NULL, &gidDropPriv, NULL));
/* now add other modules handlers (we should work on that to be able to do it in ClassInit(), but so far
* that is not possible). -- rgerhards, 2008-01-28
@@ -2727,6 +2922,11 @@ static void printVersion(void)
#else
printf("\tFEATURE_DEBUG (debug build, slow code):\tNo\n");
#endif
+#ifdef HAVE_ATOMIC_BUILTINS
+ printf("\tAtomic operations supported:\t\tYes\n");
+#else
+ printf("\tAtomic operations supported:\t\tNo\n");
+#endif
#ifdef RTINST
printf("\tRuntime Instrumentation (slow code):\tYes\n");
#else
@@ -2740,9 +2940,9 @@ static void printVersion(void)
* move code out of the too-long main() function.
* rgerhards, 2007-10-17
*/
-static void mainThread()
+static rsRetVal mainThread()
{
- BEGINfunc
+ DEFiRet;
uchar *pTmp;
/* Note: signals MUST be processed by the thread this code is running in. The reason
@@ -2771,15 +2971,34 @@ static void mainThread()
pTmp = template_StdPgSQLFmt;
tplLastStaticInit(tplAddLine(" StdPgSQLFmt", &pTmp));
- init();
- if(Debug) {
+ CHKiRet(init());
+
+ if(Debug && debugging_on) {
dbgprintf("Debugging enabled, SIGUSR1 to turn off debugging.\n");
- debugging_on = 1;
}
/* Send a signal to the parent so it can terminate.
*/
- if (myPid != ppid)
- kill (ppid, SIGTERM);
+ if(myPid != ppid)
+ kill(ppid, SIGTERM);
+
+
+ /* If instructed to do so, we now drop privileges. Note that this is not 100% secure,
+ * because inputs and outputs are already running at this time. However, we can implement
+ * dropping of privileges rather quickly and it will work in many cases. While it is not
+ * the ultimate solution, the current one is still much better than not being able to
+ * drop privileges at all. Doing it correctly, requires a change in architecture, which
+ * we should do over time. TODO -- rgerhards, 2008-11-19
+ */
+ if(gidDropPriv != 0) {
+ doDropPrivGid(gidDropPriv);
+ glbl.SetHUPisRestart(0); /* we can not do restart-type HUPs with dropped privs */
+ }
+
+ if(uidDropPriv != 0) {
+ doDropPrivUid(uidDropPriv);
+ glbl.SetHUPisRestart(0); /* we can not do restart-type HUPs with dropped privs */
+ }
+
/* END OF INTIALIZATION
* ... but keep in mind that we might do a restart and thus init() might
@@ -2790,7 +3009,9 @@ static void mainThread()
dbgprintf("initialization completed, transitioning to regular run mode\n");
mainloop();
- ENDfunc
+
+finalize_it:
+ RETiRet;
}
@@ -2832,6 +3053,8 @@ InitGlobalClasses(void)
CHKiRet(actionClassInit());
pErrObj = "template";
CHKiRet(templateInit());
+ pErrObj = "parser";
+ CHKiRet(parserClassInit());
/* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */
pErrObj = "net";
@@ -2877,7 +3100,7 @@ GlobalClassExit(void)
CHKiRet(strmClassInit(NULL));
CHKiRet(wtiClassInit(NULL));
CHKiRet(wtpClassInit(NULL));
- CHKiRet(queueClassInit(NULL));
+ CHKiRet(qqueueClassInit(NULL));
CHKiRet(vmstkClassInit(NULL));
CHKiRet(sysvarClassInit(NULL));
CHKiRet(vmClassInit(NULL));
@@ -2973,6 +3196,98 @@ finalize_it:
}
+/* global initialization, to be done only once and before the mainloop is started.
+ * rgerhards, 2008-07-28 (extracted from realMain())
+ */
+static rsRetVal
+doGlblProcessInit(void)
+{
+ struct sigaction sigAct;
+ int num_fds;
+ int i;
+ DEFiRet;
+
+ checkPermissions();
+ thrdInit();
+
+ if( !(Debug || NoFork) )
+ {
+ dbgprintf("Checking pidfile.\n");
+ if (!check_pid(PidFile))
+ {
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = doexit;
+ sigaction(SIGTERM, &sigAct, NULL);
+
+ if (fork()) {
+ /* Parent process
+ */
+ sleep(300);
+ /* Not reached unless something major went wrong. 5
+ * minutes should be a fair amount of time to wait.
+ * Please note that this procedure is important since
+ * the father must not exit before syslogd isn't
+ * initialized or the klogd won't be able to flush its
+ * logs. -Joey
+ */
+ exit(1); /* "good" exit - after forking, not diasabling anything */
+ }
+ num_fds = getdtablesize();
+ for (i= 0; i < num_fds; i++)
+ (void) close(i);
+ untty();
+ }
+ else
+ {
+ fputs(" Already running.\n", stderr);
+ exit(1); /* "good" exit, done if syslogd is already running */
+ }
+ } else {
+ debugging_on = 1;
+ }
+
+ /* tuck my process id away */
+ dbgprintf("Writing pidfile %s.\n", PidFile);
+ if (!check_pid(PidFile))
+ {
+ if (!write_pid(PidFile))
+ {
+ fputs("Can't write pid.\n", stderr);
+ exit(1); /* exit during startup - questionable */
+ }
+ }
+ else
+ {
+ fputs("Pidfile (and pid) already exist.\n", stderr);
+ exit(1); /* exit during startup - questionable */
+ }
+ myPid = getpid(); /* save our pid for further testing (also used for messages) */
+
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+
+ sigAct.sa_handler = sigsegvHdlr;
+ sigaction(SIGSEGV, &sigAct, NULL);
+ sigAct.sa_handler = sigsegvHdlr;
+ sigaction(SIGABRT, &sigAct, NULL);
+ sigAct.sa_handler = doDie;
+ sigaction(SIGTERM, &sigAct, NULL);
+ sigAct.sa_handler = Debug ? doDie : SIG_IGN;
+ sigaction(SIGINT, &sigAct, NULL);
+ sigaction(SIGQUIT, &sigAct, NULL);
+ sigAct.sa_handler = reapchild;
+ sigaction(SIGCHLD, &sigAct, NULL);
+ sigAct.sa_handler = Debug ? debug_switch : SIG_IGN;
+ sigaction(SIGUSR1, &sigAct, NULL);
+ sigAct.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sigAct, NULL);
+ sigaction(SIGXFSZ, &sigAct, NULL); /* do not abort if 2gig file limit is hit */
+
+ RETiRet;
+}
+
+
/* This is the main entry point into rsyslogd. Over time, we should try to
* modularize it a bit more...
*/
@@ -2980,20 +3295,20 @@ int realMain(int argc, char **argv)
{
DEFiRet;
- register int i;
register uchar *p;
- int num_fds;
int ch;
struct hostent *hent;
extern int optind;
extern char *optarg;
- struct sigaction sigAct;
int bEOptionWasGiven = 0;
int bImUxSockLoaded = 0; /* already generated a $ModLoad imuxsock? */
+ int iHelperUOpt;
+ int bChDirRoot = 1; /* change the current working directory to "/"? */
char *arg; /* for command line option processing */
uchar legacyConfLine[80];
uchar *LocalHostName;
uchar *LocalDomain;
+ uchar *LocalFQDNName;
/* first, parse the command line options. We do not carry out any actual work, just
* see what we should do. This relieves us from certain anomalies and we can process
@@ -3009,7 +3324,7 @@ int realMain(int argc, char **argv)
* only when actually neeeded.
* rgerhards, 2008-04-04
*/
- while ((ch = getopt(argc, argv, "46a:Ac:def:g:hi:l:m:M:nop:qQr::s:t:u:vwx")) != EOF) {
+ while((ch = getopt(argc, argv, "46a:Ac:def:g:hi:l:m:M:nN:op:qQr::s:t:u:vwx")) != EOF) {
switch((char)ch) {
case '4':
case '6':
@@ -3021,6 +3336,7 @@ int realMain(int argc, char **argv)
case 'l':
case 'm': /* mark interval */
case 'n': /* don't fork */
+ case 'N': /* enable config verify mode */
case 'o':
case 'p':
case 'q': /* add hostname if DNS resolving has failed */
@@ -3084,9 +3400,6 @@ int realMain(int argc, char **argv)
ppid = getpid();
- if(chdir ("/") != 0)
- fprintf(stderr, "Can not do 'cd /' - still trying to run\n");
-
CHKiRet_Hdlr(InitGlobalClasses()) {
fprintf(stderr, "rsyslogd initializiation failed - global classes could not be initialized.\n"
"Did you do a \"make install\"?\n"
@@ -3100,7 +3413,9 @@ int realMain(int argc, char **argv)
/* get our host and domain names - we need to do this early as we may emit
* error log messages, which need the correct hostname. -- rgerhards, 2008-04-04
*/
- net.getLocalHostname(&LocalHostName);
+ net.getLocalHostname(&LocalFQDNName);
+ CHKmalloc(LocalHostName = (uchar*) strdup((char*)LocalFQDNName));
+ glbl.SetLocalFQDNName(LocalFQDNName); /* set the FQDN before we modify it */
if((p = (uchar*)strchr((char*)LocalHostName, '.'))) {
*p++ = '\0';
LocalDomain = p;
@@ -3219,6 +3534,9 @@ int realMain(int argc, char **argv)
case 'n': /* don't fork */
NoFork = 1;
break;
+ case 'N': /* enable config verify mode */
+ iConfigVerify = atoi(arg);
+ break;
case 'o':
if(iCompatibilityMode < 3) {
if(!bImUxSockLoaded) {
@@ -3269,8 +3587,11 @@ int realMain(int argc, char **argv)
fprintf(stderr, "-t option only supported in compatibility modes 0 to 2 - ignored\n");
break;
case 'u': /* misc user settings */
- if(atoi(arg) == 1)
+ iHelperUOpt = atoi(arg);
+ if(iHelperUOpt & 0x01)
bParseHOSTNAMEandTAG = 0;
+ if(iHelperUOpt & 0x02)
+ bChDirRoot = 0;
break;
case 'w': /* disable disallowed host warnigs */
glbl.SetOption_DisallowWarning(0);
@@ -3287,12 +3608,26 @@ int realMain(int argc, char **argv)
if(iRet != RS_RET_END_OF_LINKEDLIST)
FINALIZE;
+ if(iConfigVerify) {
+ fprintf(stderr, "rsyslogd: version %s, config validation run (level %d), master config %s\n",
+ VERSION, iConfigVerify, ConfFile);
+ }
+
+ if(bChDirRoot) {
+ if(chdir("/") != 0)
+ fprintf(stderr, "Can not do 'cd /' - still trying to run\n");
+ }
+
+
/* process compatibility mode settings */
- if(iCompatibilityMode < 3) {
+ if(iCompatibilityMode < 4) {
errmsg.LogError(0, NO_ERRCODE, "WARNING: rsyslogd is running in compatibility mode. Automatically "
"generated config directives may interfer with your rsyslog.conf settings. "
- "We suggest upgrading your config and adding -c3 as the first "
+ "We suggest upgrading your config and adding -c4 as the first "
"rsyslogd option.");
+ }
+
+ if(iCompatibilityMode < 3) {
if(MarkInterval > 0) {
legacyOptsEnq((uchar *) "ModLoad immark");
snprintf((char *) legacyConfLine, sizeof(legacyConfLine), "MarkMessagePeriod %d", MarkInterval);
@@ -3310,86 +3645,10 @@ int realMain(int argc, char **argv)
"more and cast your vote if you want us to keep this feature.");
}
- checkPermissions();
- thrdInit();
+ if(!iConfigVerify)
+ CHKiRet(doGlblProcessInit());
- if( !(Debug || NoFork) )
- {
- dbgprintf("Checking pidfile.\n");
- if (!check_pid(PidFile))
- {
- memset(&sigAct, 0, sizeof (sigAct));
- sigemptyset(&sigAct.sa_mask);
- sigAct.sa_handler = doexit;
- sigaction(SIGTERM, &sigAct, NULL);
-
- if (fork()) {
- /*
- * Parent process
- */
- sleep(300);
- /*
- * Not reached unless something major went wrong. 5
- * minutes should be a fair amount of time to wait.
- * Please note that this procedure is important since
- * the father must not exit before syslogd isn't
- * initialized or the klogd won't be able to flush its
- * logs. -Joey
- */
- exit(1); /* "good" exit - after forking, not diasabling anything */
- }
- num_fds = getdtablesize();
- for (i= 0; i < num_fds; i++)
- (void) close(i);
- untty();
- }
- else
- {
- fputs(" Already running.\n", stderr);
- exit(1); /* "good" exit, done if syslogd is already running */
- }
- }
- else
- debugging_on = 1;
-
- /* tuck my process id away */
- dbgprintf("Writing pidfile %s.\n", PidFile);
- if (!check_pid(PidFile))
- {
- if (!write_pid(PidFile))
- {
- fputs("Can't write pid.\n", stderr);
- exit(1); /* exit during startup - questionable */
- }
- }
- else
- {
- fputs("Pidfile (and pid) already exist.\n", stderr);
- exit(1); /* exit during startup - questionable */
- }
- myPid = getpid(); /* save our pid for further testing (also used for messages) */
-
- memset(&sigAct, 0, sizeof (sigAct));
- sigemptyset(&sigAct.sa_mask);
-
- sigAct.sa_handler = sigsegvHdlr;
- sigaction(SIGSEGV, &sigAct, NULL);
- sigAct.sa_handler = sigsegvHdlr;
- sigaction(SIGABRT, &sigAct, NULL);
- sigAct.sa_handler = doDie;
- sigaction(SIGTERM, &sigAct, NULL);
- sigAct.sa_handler = Debug ? doDie : SIG_IGN;
- sigaction(SIGINT, &sigAct, NULL);
- sigaction(SIGQUIT, &sigAct, NULL);
- sigAct.sa_handler = reapchild;
- sigaction(SIGCHLD, &sigAct, NULL);
- sigAct.sa_handler = Debug ? debug_switch : SIG_IGN;
- sigaction(SIGUSR1, &sigAct, NULL);
- sigAct.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sigAct, NULL);
- sigaction(SIGXFSZ, &sigAct, NULL); /* do not abort if 2gig file limit is hit */
-
- mainThread();
+ CHKiRet(mainThread());
/* do any de-init's that need to be done AFTER this comment */
@@ -3398,9 +3657,12 @@ int realMain(int argc, char **argv)
thrdExit();
finalize_it:
- if(iRet != RS_RET_OK)
- fprintf(stderr, "rsyslogd run failed with error %d\n(see rsyslog.h "
- "or http://www.rsyslog.com/errcode to learn what that number means)\n", iRet);
+ if(iRet == RS_RET_VALIDATION_RUN) {
+ fprintf(stderr, "rsyslogd: End of config validation run. Bye.\n");
+ } else if(iRet != RS_RET_OK) {
+ fprintf(stderr, "rsyslogd run failed with error %d (see rsyslog.h "
+ "or try http://www.rsyslog.com/e/%d to learn what that number means)\n", iRet, iRet*-1);
+ }
ENDfunc
return 0;
@@ -3417,6 +3679,5 @@ int main(int argc, char **argv)
dbgClassInit();
return realMain(argc, argv);
}
-
/* vim:set ai:
*/
diff --git a/tools/syslogd.h b/tools/syslogd.h
index e866a16b..f1b11a91 100644
--- a/tools/syslogd.h
+++ b/tools/syslogd.h
@@ -70,7 +70,8 @@ struct filed {
FIOP_CONTAINS = 1, /* contains string? */
FIOP_ISEQUAL = 2, /* is (exactly) equal? */
FIOP_STARTSWITH = 3, /* starts with a string? */
- FIOP_REGEX = 4 /* matches a regular expression? */
+ FIOP_REGEX = 4, /* matches a (BRE) regular expression? */
+ FIOP_EREREGEX = 5 /* matches a ERE regular expression? */
} operation;
cstr_t *pCSCompValue; /* value to "compare" against */
char isNegated; /* actually a boolean ;) */