diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | doc/Makefile.am | 1 | ||||
-rw-r--r-- | doc/imsolaris.html | 47 | ||||
-rw-r--r-- | doc/rsyslog_conf_modules.html | 1 | ||||
-rw-r--r-- | plugins/imsolaris/imsolaris.c | 236 | ||||
-rw-r--r-- | tests/Makefile.am | 10 | ||||
-rwxr-xr-x | tests/diag.sh | 6 | ||||
-rw-r--r-- | tests/diagtalker.c | 156 | ||||
-rw-r--r-- | tests/historical/DiagTalker.java (renamed from tests/DiagTalker.java) | 1 | ||||
-rw-r--r-- | tests/historical/README | 2 |
10 files changed, 343 insertions, 118 deletions
@@ -408,6 +408,7 @@ Version 4.7.1 [v4-devel] (rgerhards), 2010-04-?? Solaris is no longer supported in imklog, but rather there is a new plugin imsolaris, which is used to pull local log sources on a Solaris machine. +- testbench improvement: Java is no longer needed for testing tool creation --------------------------------------------------------------------------- Version 4.7.0 [v4-devel] (rgerhards), 2010-04-14 - new: support for Solaris added (but not yet the Solaris door API) diff --git a/doc/Makefile.am b/doc/Makefile.am index 42a0e83c..3d900cac 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -41,6 +41,7 @@ html_files = \ imtcp.html \ imgssapi.html \ imrelp.html \ + imsolaris.html \ imuxsock.html \ imklog.html \ professional_support.html \ diff --git a/doc/imsolaris.html b/doc/imsolaris.html new file mode 100644 index 00000000..ce0e7e84 --- /dev/null +++ b/doc/imsolaris.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html><head> +<meta http-equiv="Content-Language" content="en"> +<title>Solaris Input Module (imsolaris)</title> + +</head> +<body> +<a href="rsyslog_conf_modules.html">back</a> + +<h1>Solaris Input Module</h1> +<p><b>Module Name: imsolaris</b></p> +<p><b>Author: </b>Rainer Gerhards +<rgerhards@adiscon.com></p> +<p><b>Description</b>:</p> +<p>Reads local Solaris log messages including the kernel log.</p> +<p>This module is specifically tailored for Solaris. Under Solaris, there +is no special kernel input device. Instead, both kernel messages as well as +messages emitted via syslog() are received from a single source. +<p>This module obeys the Solaris door() mechanism to detect a running syslogd +instance. As such, only one can be active at one time. If it detects another +active intance at startup, the module disables itself, but rsyslog will +continue to run. +<p><b>Configuration Directives</b>:</p> +<ul> +<li><strong>$IMSolarisLogSocketName <name></strong><br> +This is the name of the log socket (stream) to read. If not given, /dev/log +is read. +</li> +</ul> +<b>Caveats/Known Bugs:</b> +<p>None currently known. For obvious reasons, works on Solaris, only (and compilation +will most probably fail on any other platform). +<p><b>Sample:</b></p> +<p>The following sample pulls messages from the default log source +<br> +</p> +<textarea rows="15" cols="60">$ModLoad imsolaris +</textarea> +<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] +[<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p> +<p><font size="2">This documentation is part of the +<a href="http://www.rsyslog.com/">rsyslog</a> +project.<br> +Copyright © 2010 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and +<a href="http://www.adiscon.com/">Adiscon</a>. +Released under the GNU GPL version 3 or higher.</font></p> +</body></html> diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html index a246d0ca..b721c935 100644 --- a/doc/rsyslog_conf_modules.html +++ b/doc/rsyslog_conf_modules.html @@ -40,6 +40,7 @@ to message generators. <li>immark - support for mark messages</li> <li><a href="imklog.html">imklog</a> - kernel logging</li> <li><a href="imuxsock.html">imuxsock</a> - unix sockets, including the system log socket</li> +<li><a href="imsolaris.html">imsolaris</a> - input for the Sun Solaris system log source</li> <li><a href="im3195.html">im3195</a> - accepts syslog messages via RFC 3195</li> </ul> diff --git a/plugins/imsolaris/imsolaris.c b/plugins/imsolaris/imsolaris.c index c51a08a3..6b07ba2b 100644 --- a/plugins/imsolaris/imsolaris.c +++ b/plugins/imsolaris/imsolaris.c @@ -102,79 +102,16 @@ static prop_t *pInputName = NULL; /* our inputName currently is always "imuxsock static char *LogName = NULL; /* the log socket name TODO: make configurable! */ - -/* This function receives data from a socket indicated to be ready - * to receive and submits the message received for processing. - * rgerhards, 2007-12-20 - * Interface changed so that this function is passed the array index - * of the socket which is to be processed. This eases access to the - * growing number of properties. -- rgerhards, 2008-08-01 +/* a function to replace the sun logerror() function. + * It generates an error message from the supplied string. The main + * reason for not calling logError directly is that sun_cddl.c does not + * know or has acces to rsyslog objects (namely errmsg) -- and we do not + * want to do this effort. -- rgerhards, 2010-04-19 */ -rsRetVal -solaris_readLog(int fd) +void +imsolaris_logerror(int err, char *errStr) { - DEFiRet; - int iRcvd; - int iMaxLine; - struct strbuf data; - struct strbuf ctl; - struct log_ctl hdr; - int flags; - msg_t *pMsg; - int ret; - uchar bufRcv[4096+1]; - uchar *pRcv = NULL; /* receive buffer */ - char errStr[1024]; - - iMaxLine = glbl.GetMaxLine(); - - /* we optimize performance: if iMaxLine is below 4K (which it is in almost all - * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory - * is used. We could use alloca() to achive a similar aspect, but there are so - * many issues with alloca() that I do not want to take that route. - * rgerhards, 2008-09-02 - */ - if((size_t) iMaxLine < sizeof(bufRcv) - 1) { - pRcv = bufRcv; - } else { - CHKmalloc(pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1))); - } - - data.buf = (char*)pRcv; - data.maxlen = iMaxLine; - ctl.maxlen = sizeof (struct log_ctl); - ctl.buf = (caddr_t)&hdr; - flags = 0; - ret = getmsg(fd, &ctl, &data, &flags); - if(ret < 0) { - rs_strerror_r(errno, errStr, sizeof(errStr)); - DBGPRINTF("imsolaris: getmsg() error on fd %d: %s.\n", fd, errStr); - } - DBGPRINTF("imsolaris: getmsg() returns %d\n", ret); - DBGPRINTF("imsolaris: message from log socket: #%d: %s\n", fd, pRcv); - if (1) {//iRcvd > 0) { - CHKiRet(msgConstruct(&pMsg)); - //MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY); - MsgSetInputName(pMsg, pInputName); - MsgSetRawMsg(pMsg, (char*)pRcv, strlen((char*)pRcv)); - MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())); - pMsg->iFacility = LOG_FAC(hdr.pri); - pMsg->iSeverity = LOG_PRI(hdr.pri); - //pMsg->bParseHOSTNAME = 0; - pMsg->msgFlags = NEEDS_PARSING | NO_PRI_IN_RAW; - CHKiRet(submitMsg(pMsg)); - } else if (iRcvd < 0 && errno != EINTR) { - int en = errno; - rs_strerror_r(en, errStr, sizeof(errStr)); - DBGPRINTF("imsolaris: stream error: %d = %s.\n", errno, errStr); - errmsg.LogError(en, NO_ERRCODE, "imsolaris: stream input error: %s", errStr); - } - -finalize_it: - if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) - free(pRcv); - - RETiRet; + errmsg.LogError(err, RS_RET_ERR_DOOR, "%s", errStr); } @@ -185,10 +122,10 @@ finalize_it: * iteration. * rgerhards, 2010-04-19 */ -static inline void +static void tryRecover(void) { - int tryNum = 0; + int tryNum = 1; int waitsecs; int waitusecs; rsRetVal iRet; @@ -199,17 +136,20 @@ tryRecover(void) while(1) { /* loop broken inside */ iRet = sun_openklog((LogName == NULL) ? PATH_LOG : LogName); if(iRet == RS_RET_OK) { - if(tryNum > 0) { + if(tryNum > 1) { errmsg.LogError(0, iRet, "failure on system log socket recovered."); } break; } /* failure, so sleep a bit. We wait try*10 ms, with a max of 15 seconds */ - if(tryNum == 0) { + if(tryNum == 1) { errmsg.LogError(0, iRet, "failure on system log socket, trying to recover..."); + } waitusecs = tryNum * 10000; waitsecs = waitusecs / 1000000; - if(waitsecs != 15) { + DBGPRINTF("imsolaris: try %d to recover system log socket in %d.%d seconds\n", + tryNum, waitsecs, waitusecs); + if(waitsecs > 15) { waitsecs = 15; waitusecs = 0; } else { @@ -217,21 +157,61 @@ tryRecover(void) } srSleep(waitsecs, waitusecs); ++tryNum; - } } } -/* a function to replace the sun logerror() function. - * It generates an error message from the supplied string. The main - * reason for not calling logError directly is that sun_cddl.c does not - * know or has acces to rsyslog objects (namely errmsg) -- and we do not - * want to do this effort. -- rgerhards, 2010-04-19 +/* This function receives data from a socket indicated to be ready + * to receive and submits the message received for processing. + * rgerhards, 2007-12-20 + * Interface changed so that this function is passed the array index + * of the socket which is to be processed. This eases access to the + * growing number of properties. -- rgerhards, 2008-08-01 */ -void -imsolaris_logerror(int err, char *errStr) +static rsRetVal +readLog(int fd, uchar *pRcv, int iMaxLine) { - errmsg.LogError(err, RS_RET_ERR_DOOR, "%s", errStr); + DEFiRet; + struct strbuf data; + struct strbuf ctl; + struct log_ctl hdr; + int flags; + msg_t *pMsg; + int ret; + char errStr[1024]; + + data.buf = (char*)pRcv; + data.maxlen = iMaxLine; + ctl.maxlen = sizeof (struct log_ctl); + ctl.buf = (caddr_t)&hdr; + flags = 0; + ret = getmsg(fd, &ctl, &data, &flags); + if(ret < 0) { + if(errno == EINTR) { + FINALIZE; + } else { + int en = errno; + rs_strerror_r(errno, errStr, sizeof(errStr)); + DBGPRINTF("imsolaris: stream input error on fd %d: %s.\n", fd, errStr); + errmsg.LogError(en, NO_ERRCODE, "imsolaris: stream input error: %s", errStr); + tryRecover(); + } + } else { + DBGPRINTF("imsolaris: message from log stream %d: %s\n", fd, pRcv); + pRcv[data.len] = '\0'; /* make sure it is a valid C-String */ + CHKiRet(msgConstruct(&pMsg)); + MsgSetInputName(pMsg, pInputName); + MsgSetRawMsg(pMsg, (char*)pRcv, strlen((char*)pRcv)); + MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())); + pMsg->iFacility = LOG_FAC(hdr.pri); + pMsg->iSeverity = LOG_PRI(hdr.pri); + pMsg->bParseHOSTNAME = 0; + pMsg->msgFlags = NEEDS_PARSING | NO_PRI_IN_RAW; + CHKiRet(submitMsg(pMsg)); + } + +finalize_it: + RETiRet; } @@ -248,43 +228,76 @@ getMsgs(int timeout) { DEFiRet; int nfds; + int iMaxLine; + uchar *pRcv = NULL; /* receive buffer */ + uchar bufRcv[4096+1]; char errStr[1024]; - do { - DBGPRINTF("imsolaris: waiting for next message (timeout %d)...\n", timeout); - nfds = poll(&sun_Pfd, 1, timeout); /* wait without timeout */ + iMaxLine = glbl.GetMaxLine(); - /* v5-TODO: here we must check if we should terminante! */ + /* we optimize performance: if iMaxLine is below 4K (which it is in almost all + * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory + * is used. We could use alloca() to achive a similar aspect, but there are so + * many issues with alloca() that I do not want to take that route. + * rgerhards, 2008-09-02 + */ + if((size_t) iMaxLine < sizeof(bufRcv) - 1) { + pRcv = bufRcv; + } else { + CHKmalloc(pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1))); + } - if(nfds == 0) { - if(timeout == 0) { - DBGPRINTF("imsolaris: no more messages, getMsgs() terminates\n"); - FINALIZE; - } else { + do { + DBGPRINTF("imsolaris: waiting for next message (timeout %d)...\n", timeout); + if(timeout == 0) { + nfds = poll(&sun_Pfd, 1, timeout); /* wait without timeout */ + + /* v5-TODO: here we must check if we should terminante! */ + + if(nfds == 0) { + if(timeout == 0) { + DBGPRINTF("imsolaris: no more messages, getMsgs() terminates\n"); + FINALIZE; + } else { + continue; + } + } + + if(nfds < 0) { + if(errno != EINTR) { + int en = errno; + rs_strerror_r(en, errStr, sizeof(errStr)); + DBGPRINTF("imsolaris: poll error: %d = %s.\n", errno, errStr); + errmsg.LogError(en, NO_ERRCODE, "imsolaris: poll error: %s", + errStr); + } continue; - } - } - - if(nfds < 0) { - if(errno != EINTR) { - int en = errno; - rs_strerror_r(en, errStr, sizeof(errStr)); - DBGPRINTF("imsolaris: poll error: %d = %s.\n", errno, errStr); - errmsg.LogError(en, NO_ERRCODE, "imsolaris: poll error: %s", errStr); } - continue; - } - if(sun_Pfd.revents & POLLIN) { - solaris_readLog(sun_Pfd.fd); - } else if(sun_Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) { - tryRecover(); + if(sun_Pfd.revents & POLLIN) { + readLog(sun_Pfd.fd, pRcv, iMaxLine); + } else if(sun_Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) { + tryRecover(); + } + } else { + /* if we have an infinite wait, we do not use poll at all + * I'd consider this a waste of time. However, I do not totally + * remove the code, as it may be useful if we decide at some + * point to provide a capability to support multiple input streams + * at once (this may be useful for a jail). In that case, the poll() + * loop would be needed, and so it doesn't make much sense to change + * the code to not support it. -- rgerhards, 2010-04-20 + */ + readLog(sun_Pfd.fd, pRcv, iMaxLine); } - } while(1); + } while(1); /* TODO: in v5, we must check the termination predicate */ /* Note: in v4, this code is never reached (our thread will be cancelled) */ finalize_it: + if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) + free(pRcv); + RETiRet; } @@ -333,6 +346,7 @@ CODESTARTafterRun /* do cleanup here */ if(pInputName != NULL) prop.Destruct(&pInputName); + free(LogName); ENDafterRun @@ -370,6 +384,8 @@ CODEmodInit_QueryRegCFSLineHdlr /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imsolarislogsocketname", 0, eCmdHdlrGetWord, + NULL, &LogName, STD_LOADABLE_MODULE_ID)); ENDmodInit /* vim:set ai: */ diff --git a/tests/Makefile.am b/tests/Makefile.am index b161d1bc..29a2ce14 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,6 @@ if ENABLE_TESTBENCH TESTRUNS = rt_init rscript -check_PROGRAMS = $(TESTRUNS) ourtail nettester tcpflood chkseq msleep randomgen +check_PROGRAMS = $(TESTRUNS) ourtail nettester tcpflood chkseq msleep randomgen diagtalker TESTS = $(TESTRUNS) cfg.sh \ arrayqueue.sh \ linkedlistqueue.sh \ @@ -68,12 +68,10 @@ if ENABLE_EXTENDED_TESTS TESTS += random.sh endif -check_JAVA = DiagTalker.java - endif # if ENABLE_TESTBENCH TESTS_ENVIRONMENT = RSYSLOG_MODDIR='$(abs_top_builddir)'/runtime/.libs/ -DISTCLEANFILES=rsyslog.pid '$(abs_top_builddir)'/DiagTalker.class +DISTCLEANFILES=rsyslog.pid test_files = testbench.h runtime-dummy.c EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \ @@ -278,7 +276,6 @@ EXTRA_DIST= 1.rstest 2.rstest 3.rstest err1.rstest \ testsuites/dircreate_dflt.conf \ dircreate_off.sh \ testsuites/dircreate_off.conf \ - DiagTalker.java \ cfg.sh ourtail_SOURCES = ourtail.c @@ -288,6 +285,9 @@ chkseq_SOURCES = chkseq.c tcpflood_SOURCES = tcpflood.c tcpflood_LDADD = $(SOL_LIBS) +diagtalker_SOURCES = diagtalker.c +diagtalker_LDADD = $(SOL_LIBS) + randomgen_SOURCES = randomgen.c randomgen_LDADD = $(SOL_LIBS) diff --git a/tests/diag.sh b/tests/diag.sh index 06512d4a..b81e4eb6 100755 --- a/tests/diag.sh +++ b/tests/diag.sh @@ -59,9 +59,9 @@ case $1 in 'wait-queueempty') # wait for main message queue to be empty. $2 is the instance. if [ "$2" == "2" ] then - echo WaitMainQueueEmpty | java -classpath $abs_top_builddir DiagTalker + echo WaitMainQueueEmpty | ./diagtalker else - echo WaitMainQueueEmpty | java -classpath $abs_top_builddir DiagTalker 13501 + echo WaitMainQueueEmpty | ./diagtalker fi ;; 'shutdown-when-empty') # shut rsyslogd down when main queue is empty. $2 is the instance. @@ -83,7 +83,7 @@ case $1 in ;; 'injectmsg') # inject messages via our inject interface (imdiag) echo injecting $3 messages - echo injectmsg $2 $3 $4 $5 | java -classpath $abs_top_builddir DiagTalker + echo injectmsg $2 $3 $4 $5 | ./diagtalker # TODO: some return state checking? (does it really make sense here?) ;; 'check-mainq-spool') # check if mainqueue spool files exist, if not abort (we just check .qi). diff --git a/tests/diagtalker.c b/tests/diagtalker.c new file mode 100644 index 00000000..f5fd1c40 --- /dev/null +++ b/tests/diagtalker.c @@ -0,0 +1,156 @@ +/* A yet very simple tool to talk to imdiag (this replaces the + * previous Java implementation in order to get fewer dependencies). + * + * Copyright 2010 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> +#include <string.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/socket.h> + + +static char *targetIP = "127.0.0.1"; +static int targetPort = 13500; + + +/* open a single tcp connection + */ +int openConn(int *fd) +{ + int sock; + struct sockaddr_in addr; + int port; + int retries = 0; + + if((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) { + perror("socket()"); + exit(1); + } + + port = targetPort; + memset((char *) &addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if(inet_aton(targetIP, &addr.sin_addr)==0) { + fprintf(stderr, "inet_aton() failed\n"); + exit(1); + } + while(1) { /* loop broken inside */ + if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) { + break; + } else { + if(retries++ == 50) { + perror("connect()"); + fprintf(stderr, "connect() failed\n"); + exit(1); + } else { + usleep(100000); /* ms = 1000 us! */ + } + } + } + + *fd = sock; + return 0; +} + + +/* send a string + */ +static void +sendCmd(int fd, char *buf, int len) +{ + int lenSend; + + lenSend = send(fd, buf, len, 0); + if(lenSend != len) { + perror("sending string"); + exit(1); + } +} + + +/* wait for a response from remote system + */ +static void +waitRsp(int fd, char *buf, int len) +{ + int ret; + + ret = recv(fd, buf, len - 1, 0); + if(ret < 0) { + perror("receiving response"); + exit(1); + } + /* we assume the message was complete, it may be better to wait + * for a LF... + */ + buf[ret] = '\0'; +} + + +/* do the actual processing + */ +static void +doProcessing() +{ + int fd; + int len; + char line[2048]; + + openConn(&fd); + while(!feof(stdin)) { + if(fgets(line, sizeof(line) - 1, stdin) == NULL) + break; + len = strlen(line); + sendCmd(fd, line, len); + waitRsp(fd, line, sizeof(line)); + printf("imdiag: %s", line); + } +} + + +/* Run the test. + * rgerhards, 2009-04-03 + */ +int main(int argc, char *argv[]) +{ + int ret = 0; + int opt; + + while((opt = getopt(argc, argv, "f:t:p:c:C:m:i:I:P:d:n:M:rB")) != -1) { + switch (opt) { + case 't': targetIP = optarg; + break; + case 'p': targetPort = atoi(optarg); + break; + default: printf("invalid option '%c' or value missing - terminating...\n", opt); + exit (1); + break; + } + } + + doProcessing(); + + exit(ret); +} diff --git a/tests/DiagTalker.java b/tests/historical/DiagTalker.java index 5a6f7dd5..147cec34 100644 --- a/tests/DiagTalker.java +++ b/tests/historical/DiagTalker.java @@ -1,3 +1,4 @@ +This tool has been replaced by ./tests/diagtalker.c in release 4.7.1 /* A yet very simple tool to talk to imdiag. * * Copyright 2009 Rainer Gerhards and Adiscon GmbH. diff --git a/tests/historical/README b/tests/historical/README new file mode 100644 index 00000000..5f10ecef --- /dev/null +++ b/tests/historical/README @@ -0,0 +1,2 @@ +This directory contains tools that are currently not being used, but are +kept because they may be used again in the future. |