From 8f8f65abb66d1a7839c30c2d1b4b4d653a8990cc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 16 Apr 2008 10:26:54 +0200 Subject: moved files to the runtime there are still some files left which could go into the runtime, but I think we will delete most of them once we are done with the full modularization. --- Makefile.am | 63 +- atomic.h | 50 - conf.c | 1 + datetime.c | 629 ------------ datetime.h | 51 - debug.c | 1331 ------------------------- debug.h | 145 --- errmsg.c | 120 --- errmsg.h | 45 - glbl.h | 15 +- liblogging-stub.h | 26 - linkedlist.c | 413 -------- linkedlist.h | 72 -- module-template.h | 481 --------- modules.c | 802 --------------- modules.h | 150 --- msg.c | 2293 ------------------------------------------ msg.h | 177 ---- obj-types.h | 405 -------- obj.c | 1342 ------------------------- obj.h | 124 --- objomsr.c | 145 --- objomsr.h | 45 - parse.c | 2 +- plugins/ommysql/Makefile.am | 2 +- rsyslog.h | 270 ----- runtime/Makefile.am | 32 +- runtime/atomic.h | 51 + runtime/datetime.c | 630 ++++++++++++ runtime/datetime.h | 52 + runtime/debug.c | 1332 +++++++++++++++++++++++++ runtime/debug.h | 146 +++ runtime/errmsg.c | 122 +++ runtime/errmsg.h | 46 + runtime/linkedlist.c | 414 ++++++++ runtime/linkedlist.h | 73 ++ runtime/module-template.h | 482 +++++++++ runtime/modules.c | 803 +++++++++++++++ runtime/modules.h | 150 +++ runtime/msg.c | 2294 +++++++++++++++++++++++++++++++++++++++++++ runtime/msg.h | 178 ++++ runtime/obj-types.h | 406 ++++++++ runtime/obj.c | 1336 +++++++++++++++++++++++++ runtime/obj.h | 125 +++ runtime/objomsr.c | 145 +++ runtime/objomsr.h | 46 + runtime/rsyslog.h | 272 +++++ runtime/srUtils.h | 126 +++ runtime/srutils.c | 509 ++++++++++ runtime/stringbuf.c | 1080 ++++++++++++++++++++ runtime/stringbuf.h | 169 ++++ runtime/syslogd-types.h | 103 ++ srUtils.c | 506 ---------- srUtils.h | 125 --- stringbuf.c | 1079 -------------------- stringbuf.h | 168 ---- syslogd-types.h | 104 -- threads.h | 5 +- 58 files changed, 11152 insertions(+), 11156 deletions(-) delete mode 100644 atomic.h delete mode 100644 datetime.c delete mode 100644 datetime.h delete mode 100644 debug.c delete mode 100644 debug.h delete mode 100644 errmsg.c delete mode 100644 errmsg.h delete mode 100644 liblogging-stub.h delete mode 100644 linkedlist.c delete mode 100644 linkedlist.h delete mode 100644 module-template.h delete mode 100644 modules.c delete mode 100644 modules.h delete mode 100644 msg.c delete mode 100644 msg.h delete mode 100644 obj-types.h delete mode 100644 obj.c delete mode 100644 obj.h delete mode 100644 objomsr.c delete mode 100644 objomsr.h delete mode 100644 rsyslog.h create mode 100644 runtime/atomic.h create mode 100644 runtime/datetime.c create mode 100644 runtime/datetime.h create mode 100644 runtime/debug.c create mode 100644 runtime/debug.h create mode 100644 runtime/errmsg.c create mode 100644 runtime/errmsg.h create mode 100644 runtime/linkedlist.c create mode 100644 runtime/linkedlist.h create mode 100644 runtime/module-template.h create mode 100644 runtime/modules.c create mode 100644 runtime/modules.h create mode 100644 runtime/msg.c create mode 100644 runtime/msg.h create mode 100644 runtime/obj-types.h create mode 100644 runtime/obj.c create mode 100644 runtime/obj.h create mode 100644 runtime/objomsr.c create mode 100644 runtime/objomsr.h create mode 100644 runtime/rsyslog.h create mode 100644 runtime/srUtils.h create mode 100644 runtime/srutils.c create mode 100644 runtime/stringbuf.c create mode 100644 runtime/stringbuf.h create mode 100644 runtime/syslogd-types.h delete mode 100644 srUtils.c delete mode 100644 srUtils.h delete mode 100644 stringbuf.c delete mode 100644 stringbuf.h delete mode 100644 syslogd-types.h diff --git a/Makefile.am b/Makefile.am index a4d1ea5b..5829ff86 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,66 +4,43 @@ man_MANS = if ENABLE_RSYSLOGD sbin_PROGRAMS += rsyslogd rsyslogd_SOURCES = \ - datetime.c \ - datetime.h \ - errmsg.c \ - errmsg.h \ syslogd.c \ syslogd.h \ - debug.c \ - debug.h \ - glbl.h \ - pidfile.c \ - pidfile.h \ - template.c \ - outchannel.c \ - stringbuf.c \ - stringbuf.h \ - srUtils.c \ - srUtils.h \ - parse.c \ - parse.h \ - syslogd-types.h \ - template.h \ - outchannel.h \ - liblogging-stub.h \ - threads.c \ - threads.h \ - obj.c \ - obj.h \ - obj-types.h \ - msg.c \ - msg.h \ - conf.c \ - conf.h \ omshell.c \ omshell.h \ omusrmsg.c \ omusrmsg.h \ omfwd.c \ omfwd.h \ - tcpsyslog.c \ - tcpsyslog.h \ omfile.c \ omfile.h \ omdiscard.c \ omdiscard.h \ - modules.c \ - modules.h \ - module-template.h \ - objomsr.c \ - objomsr.h \ - cfsysline.c \ - cfsysline.h \ - linkedlist.c \ - linkedlist.h \ iminternal.c \ iminternal.h \ + pidfile.c \ + pidfile.h \ + \ action.c \ action.h \ - atomic.h + threads.c \ + threads.h \ + \ + parse.c \ + parse.h \ + \ + outchannel.c \ + outchannel.h \ + template.c \ + template.h \ + conf.c \ + conf.h \ + tcpsyslog.c \ + tcpsyslog.h \ + cfsysline.c \ + cfsysline.h -rsyslogd_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/\" $(pthreads_cflags) $(rsrt_cflags) +rsyslogd_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) rsyslogd_LDADD = $(zlib_libs) $(pthreads_libs) $(dl_libs) $(rt_libs) $(rsrt_libs) rsyslogd_LDFLAGS = -export-dynamic diff --git a/atomic.h b/atomic.h deleted file mode 100644 index 2421c826..00000000 --- a/atomic.h +++ /dev/null @@ -1,50 +0,0 @@ -/* This header supplies atomic operations. So far, we rely on GCC's - * atomic builtins. I have no idea if we can check them via autotools, - * but I am making the necessary provisioning to live without them if - * they are not available. Please note that you should only use the macros - * here if you think you can actually live WITHOUT an explicit atomic operation, - * because in the non-presence of them, we simply do it without atomicitiy. - * Which, for word-aligned data types, usually (but only usually!) should work. - * - * We are using the functions described in - * http:/gcc.gnu.org/onlinedocs/gcc/Atomic-Builtins.html - * - * THESE MACROS MUST ONLY BE USED WITH WORD-SIZED DATA TYPES! - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" /* autotools! */ - -#ifndef INCLUDED_ATOMIC_H -#define INCLUDED_ATOMIC_H - -/* set the following to 1 if we have atomic operations (and #undef it otherwise) */ -/* #define DO_HAVE_ATOMICS 1 */ -/* for this release, we disable atomic calls because there seem to be some - * portability problems and we can not fix that without destabilizing the build. - * They simply came in too late. -- rgerhards, 2008-04-02 - */ -/* make sure they are not used! -#define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&data, 1)) -#define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&data, 1) -*/ -#define ATOMIC_INC(data) (++(data)) - -#endif /* #ifndef INCLUDED_ATOMIC_H */ diff --git a/conf.c b/conf.c index dac46970..098448c1 100644 --- a/conf.c +++ b/conf.c @@ -563,6 +563,7 @@ cflineParseFileName(uchar* p, uchar *pFileName, omodStringRequest_t *pOMSR, int * passed back to the caller. * rgerhards 2005-09-15 */ +/* GPLv3 - stems back to sysklogd */ static rsRetVal cflineProcessTradPRIFilter(uchar **pline, register selector_t *f) { uchar *p; diff --git a/datetime.c b/datetime.c deleted file mode 100644 index a4817a6d..00000000 --- a/datetime.c +++ /dev/null @@ -1,629 +0,0 @@ -/* The datetime object. It contains date and time related functions. - * - * Module begun 2008-03-05 by Rainer Gerhards, based on some code - * from syslogd.c. The main intension was to move code out of syslogd.c - * in a useful manner. It is still undecided if all functions will continue - * to stay here or some will be moved into parser modules (once we have them). - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#include "config.h" -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -# include -#endif - -#include "rsyslog.h" -#include "obj.h" -#include "modules.h" -#include "datetime.h" -#include "sysvar.h" -#include "srUtils.h" -#include "stringbuf.h" -#include "errmsg.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(errmsg) - - -/* ------------------------------ methods ------------------------------ */ - - -/** - * Get the current date/time in the best resolution the operating - * system has to offer (well, actually at most down to the milli- - * second level. - * - * The date and time is returned in separate fields as this is - * most portable and removes the need for additional structures - * (but I have to admit it is somewhat "bulky";)). - * - * Obviously, all caller-provided pointers must not be NULL... - */ -static void getCurrTime(struct syslogTime *t) -{ - struct timeval tp; - struct tm *tm; - struct tm tmBuf; - long lBias; -# if defined(__hpux) - struct timezone tz; -# endif - - assert(t != NULL); -# if defined(__hpux) - /* TODO: check this: under HP UX, the tz information is actually valid - * data. So we need to obtain and process it there. - */ - gettimeofday(&tp, &tz); -# else - gettimeofday(&tp, NULL); -# endif - tm = localtime_r((time_t*) &(tp.tv_sec), &tmBuf); - - t->year = tm->tm_year + 1900; - t->month = tm->tm_mon + 1; - t->day = tm->tm_mday; - t->hour = tm->tm_hour; - t->minute = tm->tm_min; - t->second = tm->tm_sec; - t->secfrac = tp.tv_usec; - t->secfracPrecision = 6; - -# if __sun - /* Solaris uses a different method of exporting the time zone. - * It is UTC - localtime, which is the opposite sign of mins east of GMT. - */ - lBias = -(daylight ? altzone : timezone); -# elif defined(__hpux) - lBias = tz.tz_dsttime ? - tz.tz_minuteswest : 0; -# else - lBias = tm->tm_gmtoff; -# endif - if(lBias < 0) - { - t->OffsetMode = '-'; - lBias *= -1; - } - else - t->OffsetMode = '+'; - t->OffsetHour = lBias / 3600; - t->OffsetMinute = lBias % 3600; -} - - - - -/******************************************************************* - * BEGIN CODE-LIBLOGGING * - ******************************************************************* - * Code in this section is borrowed from liblogging. This is an - * interim solution. Once liblogging is fully integrated, this is - * to be removed (see http://www.monitorware.com/liblogging for - * more details. 2004-11-16 rgerhards - * - * Please note that the orginal liblogging code is modified so that - * it fits into the context of the current version of syslogd.c. - * - * DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!! - */ - -/** - * Parse a 32 bit integer number from a string. - * - * \param ppsz Pointer to the Pointer to the string being parsed. It - * must be positioned at the first digit. Will be updated - * so that on return it points to the first character AFTER - * the integer parsed. - * \retval The number parsed. - */ - -static int srSLMGParseInt32(char** ppsz) -{ - int i; - - i = 0; - while(isdigit((int) **ppsz)) - { - i = i * 10 + **ppsz - '0'; - ++(*ppsz); - } - - return i; -} - - -/** - * Parse a TIMESTAMP-3339. - * updates the parse pointer position. - */ -static int -ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS) -{ - char *pszTS = *ppszTS; - - assert(pTime != NULL); - assert(ppszTS != NULL); - assert(pszTS != NULL); - - pTime->year = srSLMGParseInt32(&pszTS); - - /* We take the liberty to accept slightly malformed timestamps e.g. in - * the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course, - * with the current state of affairs, we would never run into this code - * here because at postion 11, there is no "T" in such cases ;) - */ - if(*pszTS++ != '-') - return FALSE; - pTime->month = srSLMGParseInt32(&pszTS); - if(pTime->month < 1 || pTime->month > 12) - return FALSE; - - if(*pszTS++ != '-') - return FALSE; - pTime->day = srSLMGParseInt32(&pszTS); - if(pTime->day < 1 || pTime->day > 31) - return FALSE; - - if(*pszTS++ != 'T') - return FALSE; - - pTime->hour = srSLMGParseInt32(&pszTS); - if(pTime->hour < 0 || pTime->hour > 23) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->minute = srSLMGParseInt32(&pszTS); - if(pTime->minute < 0 || pTime->minute > 59) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->second = srSLMGParseInt32(&pszTS); - if(pTime->second < 0 || pTime->second > 60) - return FALSE; - - /* Now let's see if we have secfrac */ - if(*pszTS == '.') - { - char *pszStart = ++pszTS; - pTime->secfrac = srSLMGParseInt32(&pszTS); - pTime->secfracPrecision = (int) (pszTS - pszStart); - } - else - { - pTime->secfracPrecision = 0; - pTime->secfrac = 0; - } - - /* check the timezone */ - if(*pszTS == 'Z') - { - pszTS++; /* eat Z */ - pTime->OffsetMode = 'Z'; - pTime->OffsetHour = 0; - pTime->OffsetMinute = 0; - } - else if((*pszTS == '+') || (*pszTS == '-')) - { - pTime->OffsetMode = *pszTS; - pszTS++; - - pTime->OffsetHour = srSLMGParseInt32(&pszTS); - if(pTime->OffsetHour < 0 || pTime->OffsetHour > 23) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->OffsetMinute = srSLMGParseInt32(&pszTS); - if(pTime->OffsetMinute < 0 || pTime->OffsetMinute > 59) - return FALSE; - } - else - /* there MUST be TZ information */ - return FALSE; - - /* OK, we actually have a 3339 timestamp, so let's indicated this */ - if(*pszTS == ' ') - ++pszTS; - else - return FALSE; - - /* update parse pointer */ - *ppszTS = pszTS; - - return TRUE; -} - - -/** - * Parse a TIMESTAMP-3164. - * Returns TRUE on parse OK, FALSE on parse error. - */ -static int -ParseTIMESTAMP3164(struct syslogTime *pTime, char* pszTS) -{ - assert(pTime != NULL); - assert(pszTS != NULL); - - getCurrTime(pTime); /* obtain the current year and UTC offsets! */ - - /* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec), - * we may see the following character sequences occur: - * - * J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec - * - * We will use this for parsing, as it probably is the - * fastest way to parse it. - * - * 2005-07-18, well sometimes it pays to be a bit more verbose, even in C... - * Fixed a bug that lead to invalid detection of the data. The issue was that - * we had an if(++pszTS == 'x') inside of some of the consturcts below. However, - * there were also some elseifs (doing the same ++), which than obviously did not - * check the orginal character but the next one. Now removed the ++ and put it - * into the statements below. Was a really nasty bug... I didn't detect it before - * june, when it first manifested. This also lead to invalid parsing of the rest - * of the message, as the time stamp was not detected to be correct. - rgerhards - */ - switch(*pszTS++) - { - case 'J': - if(*pszTS == 'a') { - ++pszTS; - if(*pszTS == 'n') { - ++pszTS; - pTime->month = 1; - } else - return FALSE; - } else if(*pszTS == 'u') { - ++pszTS; - if(*pszTS == 'n') { - ++pszTS; - pTime->month = 6; - } else if(*pszTS == 'l') { - ++pszTS; - pTime->month = 7; - } else - return FALSE; - } else - return FALSE; - break; - case 'F': - if(*pszTS == 'e') { - ++pszTS; - if(*pszTS == 'b') { - ++pszTS; - pTime->month = 2; - } else - return FALSE; - } else - return FALSE; - break; - case 'M': - if(*pszTS == 'a') { - ++pszTS; - if(*pszTS == 'r') { - ++pszTS; - pTime->month = 3; - } else if(*pszTS == 'y') { - ++pszTS; - pTime->month = 5; - } else - return FALSE; - } else - return FALSE; - break; - case 'A': - if(*pszTS == 'p') { - ++pszTS; - if(*pszTS == 'r') { - ++pszTS; - pTime->month = 4; - } else - return FALSE; - } else if(*pszTS == 'u') { - ++pszTS; - if(*pszTS == 'g') { - ++pszTS; - pTime->month = 8; - } else - return FALSE; - } else - return FALSE; - break; - case 'S': - if(*pszTS == 'e') { - ++pszTS; - if(*pszTS == 'p') { - ++pszTS; - pTime->month = 9; - } else - return FALSE; - } else - return FALSE; - break; - case 'O': - if(*pszTS == 'c') { - ++pszTS; - if(*pszTS == 't') { - ++pszTS; - pTime->month = 10; - } else - return FALSE; - } else - return FALSE; - break; - case 'N': - if(*pszTS == 'o') { - ++pszTS; - if(*pszTS == 'v') { - ++pszTS; - pTime->month = 11; - } else - return FALSE; - } else - return FALSE; - break; - case 'D': - if(*pszTS == 'e') { - ++pszTS; - if(*pszTS == 'c') { - ++pszTS; - pTime->month = 12; - } else - return FALSE; - } else - return FALSE; - break; - default: - return FALSE; - } - - /* done month */ - - if(*pszTS++ != ' ') - return FALSE; - - /* we accept a slightly malformed timestamp when receiving. This is - * we accept one-digit days - */ - if(*pszTS == ' ') - ++pszTS; - - pTime->day = srSLMGParseInt32(&pszTS); - if(pTime->day < 1 || pTime->day > 31) - return FALSE; - - if(*pszTS++ != ' ') - return FALSE; - pTime->hour = srSLMGParseInt32(&pszTS); - if(pTime->hour < 0 || pTime->hour > 23) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->minute = srSLMGParseInt32(&pszTS); - if(pTime->minute < 0 || pTime->minute > 59) - return FALSE; - - if(*pszTS++ != ':') - return FALSE; - pTime->second = srSLMGParseInt32(&pszTS); - if(pTime->second < 0 || pTime->second > 60) - return FALSE; - if(*pszTS++ != ':') - - /* OK, we actually have a 3164 timestamp, so let's indicate this - * and fill the rest of the properties. */ - pTime->timeType = 1; - pTime->secfracPrecision = 0; - pTime->secfrac = 0; - return TRUE; -} - -/******************************************************************* - * END CODE-LIBLOGGING * - *******************************************************************/ - -/** - * Format a syslogTimestamp into format required by MySQL. - * We are using the 14 digits format. For example 20041111122600 - * is interpreted as '2004-11-11 12:26:00'. - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string terminator). If 0 is returend, an error occured. - */ -int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst) -{ - /* currently we do not consider localtime/utc. This may later be - * added. If so, I recommend using a property replacer option - * and/or a global configuration option. However, we should wait - * on user requests for this feature before doing anything. - * rgerhards, 2007-06-26 - */ - assert(ts != NULL); - assert(pDst != NULL); - - if (iLenDst < 15) /* we need at least 14 bytes - 14 digits for timestamp + '\n' */ - return(0); - - return(snprintf(pDst, iLenDst, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d", - ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); - -} - -int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst) -{ - /* see note in formatTimestampToMySQL, applies here as well */ - assert(ts != NULL); - assert(pDst != NULL); - - if (iLenDst < 21) /* we need 20 bytes + '\n' */ - return(0); - - return(snprintf(pDst, iLenDst, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", - ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); -} - -/** - * Format a syslogTimestamp to a RFC3339 timestamp string (as - * specified in syslog-protocol). - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string terminator). If 0 is returend, an error occured. - */ -int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf) -{ - int iRet; - char szTZ[7]; /* buffer for TZ information */ - - assert(ts != NULL); - assert(pBuf != NULL); - - if(iLenBuf < 20) - return(0); /* we NEED at least 20 bytes */ - - /* do TZ information first, this is easier to take care of "Z" zone in rfc3339 */ - if(ts->OffsetMode == 'Z') { - szTZ[0] = 'Z'; - szTZ[1] = '\0'; - } else { - snprintf(szTZ, sizeof(szTZ) / sizeof(char), "%c%2.2d:%2.2d", - ts->OffsetMode, ts->OffsetHour, ts->OffsetMinute); - } - - if(ts->secfracPrecision > 0) - { /* we now need to include fractional seconds. While doing so, we must look at - * the precision specified. For example, if we have millisec precision (3 digits), a - * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this - * is a huge difference ;). To avoid this, we first create a format string with - * the specific precision and *then* use that format string to do the actual - * formating (mmmmhhh... kind of self-modifying code... ;)). - */ - char szFmtStr[64]; - /* be careful: there is ONE actual %d in the format string below ;) */ - snprintf(szFmtStr, sizeof(szFmtStr), - "%%04d-%%02d-%%02dT%%02d:%%02d:%%02d.%%0%dd%%s", - ts->secfracPrecision); - iRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->year, ts->month, ts->day, - ts->hour, ts->minute, ts->second, ts->secfrac, szTZ); - } - else - iRet = snprintf(pBuf, iLenBuf, - "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d%s", - ts->year, ts->month, ts->day, - ts->hour, ts->minute, ts->second, szTZ); - return(iRet); -} - -/** - * Format a syslogTimestamp to a RFC3164 timestamp sring. - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string termnator). If 0 is returend, an error occured. - */ -int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf) -{ - static char* monthNames[13] = {"ERR", "Jan", "Feb", "Mar", - "Apr", "May", "Jun", "Jul", - "Aug", "Sep", "Oct", "Nov", "Dec"}; - assert(ts != NULL); - assert(pBuf != NULL); - - if(iLenBuf < 16) - return(0); /* we NEED 16 bytes */ - return(snprintf(pBuf, iLenBuf, "%s %2d %2.2d:%2.2d:%2.2d", - monthNames[ts->month], ts->day, ts->hour, - ts->minute, ts->second - )); -} - -/** - * Format a syslogTimestamp to a text format. - * The caller must provide the timestamp as well as a character - * buffer that will receive the resulting string. The function - * returns the size of the timestamp written in bytes (without - * the string termnator). If 0 is returend, an error occured. - */ -#if 0 /* This method is currently not called, be we like to preserve it */ -static int formatTimestamp(struct syslogTime *ts, char* pBuf, size_t iLenBuf) -{ - assert(ts != NULL); - assert(pBuf != NULL); - - if(ts->timeType == 1) { - return(formatTimestamp3164(ts, pBuf, iLenBuf)); - } - - if(ts->timeType == 2) { - return(formatTimestamp3339(ts, pBuf, iLenBuf)); - } - - return(0); -} -#endif -/* queryInterface function - * rgerhards, 2008-03-05 - */ -BEGINobjQueryInterface(datetime) -CODESTARTobjQueryInterface(datetime) - if(pIf->ifVersion != datetimeCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->getCurrTime = getCurrTime; - pIf->ParseTIMESTAMP3339 = ParseTIMESTAMP3339; - pIf->ParseTIMESTAMP3164 = ParseTIMESTAMP3164; - pIf->formatTimestampToMySQL = formatTimestampToMySQL; - pIf->formatTimestampToPgSQL = formatTimestampToPgSQL; - pIf->formatTimestamp3339 = formatTimestamp3339; - pIf->formatTimestamp3164 = formatTimestamp3164; -finalize_it: -ENDobjQueryInterface(datetime) - - -/* Initialize the datetime class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINAbstractObjClassInit(datetime, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - CHKiRet(objUse(errmsg, CORE_COMPONENT)); - -ENDObjClassInit(datetime) - -/* vi:set ai: - */ diff --git a/datetime.h b/datetime.h deleted file mode 100644 index a35dfe8a..00000000 --- a/datetime.h +++ /dev/null @@ -1,51 +0,0 @@ -/* The datetime object. Contains time-related functions. - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef INCLUDED_DATETIME_H -#define INCLUDED_DATETIME_H - -#include "datetime.h" - -/* TODO: define error codes */ -#define NO_ERRCODE -1 - -/* the datetime object */ -typedef struct datetime_s { -} datetime_t; - - -/* interfaces */ -BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */ - void (*getCurrTime)(struct syslogTime *t); - //static int srSLMGParseInt32(char** ppsz); - int (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS); - int (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char* pszTS); - int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst); - int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst); - int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); - int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); -ENDinterface(datetime) -#define datetimeCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - -/* prototypes */ -PROTOTYPEObj(datetime); - -#endif /* #ifndef INCLUDED_DATETIME_H */ diff --git a/debug.c b/debug.c deleted file mode 100644 index 29c65cf1..00000000 --- a/debug.c +++ /dev/null @@ -1,1331 +0,0 @@ -/* debug.c - * - * This file proides debug and run time error analysis support. Some of the - * settings are very performance intense and my be turned off during a release - * build. - * - * File begun on 2008-01-22 by RGerhards - * - * Some functions are controlled by environment variables: - * - * RSYSLOG_DEBUGLOG if set, a debug log file is written to that location - * RSYSLOG_DEBUG specific debug options - * - * For details, visit doc/debug.html - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" /* autotools! */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rsyslog.h" -#include "debug.h" -#include "atomic.h" -#include "obj.h" - - -/* static data (some time to be replaced) */ -DEFobjCurrIf(obj) -int Debug; /* debug flag - read-only after startup */ -int debugging_on = 0; /* read-only, except on sig USR1 */ -static int bLogFuncFlow = 0; /* shall the function entry and exit be logged to the debug log? */ -static int bLogAllocFree = 0; /* shall calls to (m/c)alloc and free be logged to the debug log? */ -static int bPrintFuncDBOnExit = 0; /* shall the function entry and exit be logged to the debug log? */ -static int bPrintMutexAction = 0; /* shall mutex calls be printed to the debug log? */ -static int bPrintTime = 1; /* print a timestamp together with debug message */ -static int bPrintAllDebugOnExit = 0; -static int bAbortTrace = 1; /* print a trace after SIGABRT or SIGSEGV */ -static char *pszAltDbgFileName = NULL; /* if set, debug output is *also* sent to here */ -static FILE *altdbg = NULL; /* and the handle for alternate debug output */ -static FILE *stddbg; - -/* list of files/objects that should be printed */ -typedef struct dbgPrintName_s { - uchar *pName; - struct dbgPrintName_s *pNext; -} dbgPrintName_t; - - -/* forward definitions */ -static void dbgGetThrdName(char *pszBuf, size_t lenBuf, pthread_t thrd, int bIncludeNumID); -static dbgThrdInfo_t *dbgGetThrdInfo(void); -static int dbgPrintNameIsInList(const uchar *pName, dbgPrintName_t *pRoot); - - -/* This lists are single-linked and members are added at the top */ -static dbgPrintName_t *printNameFileRoot = NULL; - - -/* list of all known FuncDBs. We use a special list, because it must only be single-linked. As - * functions never disappear, we only need to add elements when we see a new one and never need - * to remove anything. For this, we simply add at the top, which saves us a Last pointer. The goal - * is to use as few memory as possible. - */ -typedef struct dbgFuncDBListEntry_s { - dbgFuncDB_t *pFuncDB; - struct dbgFuncDBListEntry_s *pNext; -} dbgFuncDBListEntry_t; -dbgFuncDBListEntry_t *pFuncDBListRoot; - -static pthread_mutex_t mutFuncDBList; - -typedef struct dbgMutLog_s { - struct dbgMutLog_s *pNext; - struct dbgMutLog_s *pPrev; - pthread_mutex_t *mut; - pthread_t thrd; - dbgFuncDB_t *pFuncDB; - int lockLn; /* the actual line where the mutex was locked */ - short mutexOp; -} dbgMutLog_t; -static dbgMutLog_t *dbgMutLogListRoot = NULL; -static dbgMutLog_t *dbgMutLogListLast = NULL; -static pthread_mutex_t mutMutLog; - - -static dbgThrdInfo_t *dbgCallStackListRoot = NULL; -static dbgThrdInfo_t *dbgCallStackListLast = NULL; -static pthread_mutex_t mutCallStack; - -static pthread_mutex_t mutdbgprintf; -static pthread_mutex_t mutdbgoprint; - -static pthread_key_t keyCallStack; - - -/* we do not have templates, so we use some macros to create linked list handlers - * for the several types - * DLL means "doubly linked list" - * rgerhards, 2008-01-23 - */ -#define DLL_Del(type, pThis) \ - if(pThis->pPrev != NULL) \ - pThis->pPrev->pNext = pThis->pNext; \ - if(pThis->pNext != NULL) \ - pThis->pNext->pPrev = pThis->pPrev; \ - if(pThis == dbg##type##ListRoot) \ - dbg##type##ListRoot = pThis->pNext; \ - if(pThis == dbg##type##ListLast) \ - dbg##type##ListLast = pThis->pPrev; \ - free(pThis); - -#define DLL_Add(type, pThis) \ - if(dbg##type##ListRoot == NULL) { \ - dbg##type##ListRoot = pThis; \ - dbg##type##ListLast = pThis; \ - } else { \ - pThis->pPrev = dbg##type##ListLast; \ - dbg##type##ListLast->pNext = pThis; \ - dbg##type##ListLast = pThis; \ - } - -/* we need to do our own mutex cancel cleanup handler as it shall not - * be subject to the debugging instrumentation (that would probably run us - * into an infinite loop - */ -static void dbgMutexCancelCleanupHdlr(void *pmut) -{ - pthread_mutex_unlock((pthread_mutex_t*) pmut); -} - - -/* handler to update the last execution location seen - * rgerhards, 2008-01-28 - */ -static inline void -dbgRecordExecLocation(int iStackPtr, int line) -{ - dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); - pThrd->lastLine[iStackPtr] = line; -} - - -/* ------------------------- mutex tracking code ------------------------- */ - -/* ------------------------- FuncDB utility functions ------------------------- */ - -#define SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ((int) (sizeof(pFuncDB->mutInfo) / sizeof(dbgFuncDBmutInfoEntry_t))) - -/* print a FuncDB - */ -static void dbgFuncDBPrint(dbgFuncDB_t *pFuncDB) -{ - assert(pFuncDB != NULL); - assert(pFuncDB->magic == dbgFUNCDB_MAGIC); - /* make output suitable for sorting on invocation count */ - dbgprintf("%10.10ld times called: %s:%d:%s\n", pFuncDB->nTimesCalled, pFuncDB->file, pFuncDB->line, pFuncDB->func); -} - - -/* print all funcdb entries - */ -static void dbgFuncDBPrintAll(void) -{ - dbgFuncDBListEntry_t *pFuncDBList; - int nFuncs = 0; - - for(pFuncDBList = pFuncDBListRoot ; pFuncDBList != NULL ; pFuncDBList = pFuncDBList->pNext) { - dbgFuncDBPrint(pFuncDBList->pFuncDB); - nFuncs++; - } - - dbgprintf("%d unique functions called\n", nFuncs); -} - - -/* find a mutex inside the FuncDB mutex table. Returns NULL if not found. Only mutexes from the same thread - * are found. - */ -static inline dbgFuncDBmutInfoEntry_t *dbgFuncDBGetMutexInfo(dbgFuncDB_t *pFuncDB, pthread_mutex_t *pmut) -{ - int i; - int iFound = -1; - pthread_t ourThrd = pthread_self(); - - for(i = 0 ; i < SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ; ++i) { - if(pFuncDB->mutInfo[i].pmut == pmut && pFuncDB->mutInfo[i].lockLn != -1 && pFuncDB->mutInfo[i].thrd == ourThrd) { - iFound = i; - break; - } - } - - return (iFound == -1) ? NULL : &pFuncDB->mutInfo[i]; -} - - -/* print any mutex that can be found in the FuncDB. Custom header is provided. - * "thrd" is the thread that is searched. If it is 0, mutexes for all threads - * shall be printed. - */ -static inline void -dbgFuncDBPrintActiveMutexes(dbgFuncDB_t *pFuncDB, char *pszHdrText, pthread_t thrd) -{ - int i; - char pszThrdName[64]; - - for(i = 0 ; i < SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ; ++i) { - if(pFuncDB->mutInfo[i].lockLn != -1 && (thrd == 0 || thrd == pFuncDB->mutInfo[i].thrd)) { - dbgGetThrdName(pszThrdName, sizeof(pszThrdName), pFuncDB->mutInfo[i].thrd, 1); - dbgprintf("%s:%d:%s:invocation %ld: %s %p[%d/%s]\n", pFuncDB->file, pFuncDB->line, pFuncDB->func, - pFuncDB->mutInfo[i].lInvocation, pszHdrText, (void*)pFuncDB->mutInfo[i].pmut, i, - pszThrdName); - } - } -} - -/* find a free mutex info spot in FuncDB. NULL is returned if table is full. - */ -static inline dbgFuncDBmutInfoEntry_t *dbgFuncDBFindFreeMutexInfo(dbgFuncDB_t *pFuncDB) -{ - int i; - int iFound = -1; - - for(i = 0 ; i < SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ; ++i) { - if(pFuncDB->mutInfo[i].lockLn == -1) { - iFound = i; - break; - } - } - - if(iFound == -1) { - dbgprintf("%s:%d:%s: INFO: out of space in FuncDB for mutex info (max %d entries) - ignoring\n", - pFuncDB->file, pFuncDB->line, pFuncDB->func, SIZE_FUNCDB_MUTEX_TABLE(pFuncDB)); - } - - return (iFound == -1) ? NULL : &pFuncDB->mutInfo[i]; -} - -/* add a mutex lock to the FuncDB. If the size is exhausted, info is discarded. - */ -static inline void dbgFuncDBAddMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_t *pmut, int lockLn) -{ - dbgFuncDBmutInfoEntry_t *pMutInfo; - - if((pMutInfo = dbgFuncDBFindFreeMutexInfo(pFuncDB)) != NULL) { - pMutInfo->pmut = pmut; - pMutInfo->lockLn = lockLn; - pMutInfo->lInvocation = pFuncDB->nTimesCalled; - pMutInfo->thrd = pthread_self(); - } -} - -/* remove a locked mutex from the FuncDB (unlock case!). - */ -static inline void dbgFuncDBRemoveMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_t *pmut) -{ - dbgFuncDBmutInfoEntry_t *pMutInfo; - - if((pMutInfo = dbgFuncDBGetMutexInfo(pFuncDB, pmut)) != NULL) { - pMutInfo->lockLn = -1; - } -} - - -/* ------------------------- END FuncDB utility functions ------------------------- */ - -/* ########################################################################### - * IMPORTANT NOTE - * Mutex instrumentation reduces the code's concurrency and thus affects its - * order of execution. It is vital to test the code also with mutex - * instrumentation turned off! Some bugs may not show up while it on... - * ########################################################################### - */ - -/* constructor & add new entry to list - */ -dbgMutLog_t *dbgMutLogAddEntry(pthread_mutex_t *pmut, short mutexOp, dbgFuncDB_t *pFuncDB, int lockLn) -{ - dbgMutLog_t *pLog; - - pLog = calloc(1, sizeof(dbgMutLog_t)); - assert(pLog != NULL); - - /* fill data members */ - pLog->mut = pmut; - pLog->thrd = pthread_self(); - pLog->mutexOp = mutexOp; - pLog->lockLn = lockLn; - pLog->pFuncDB = pFuncDB; - - DLL_Add(MutLog, pLog); - - return pLog; -} - - -/* destruct log entry - */ -void dbgMutLogDelEntry(dbgMutLog_t *pLog) -{ - assert(pLog != NULL); - DLL_Del(MutLog, pLog); -} - - -/* print a single mutex log entry */ -static void dbgMutLogPrintOne(dbgMutLog_t *pLog) -{ - char *strmutop; - char buf[64]; - char pszThrdName[64]; - - assert(pLog != NULL); - switch(pLog->mutexOp) { - case MUTOP_LOCKWAIT: - strmutop = "waited on"; - break; - case MUTOP_LOCK: - strmutop = "owned"; - break; - default: - snprintf(buf, sizeof(buf)/sizeof(char), "unknown state %d - should not happen!", pLog->mutexOp); - strmutop = buf; - break; - } - - dbgGetThrdName(pszThrdName, sizeof(pszThrdName), pLog->thrd, 1); - dbgprintf("mutex 0x%lx is being %s by code at %s:%d, thread %s\n", (unsigned long) pLog->mut, - strmutop, pLog->pFuncDB->file, - (pLog->mutexOp == MUTOP_LOCK) ? pLog->lockLn : pLog->pFuncDB->line, - pszThrdName); -} - -/* print the complete mutex log */ -static void dbgMutLogPrintAll(void) -{ - dbgMutLog_t *pLog; - - dbgprintf("Mutex log for all known mutex operations:\n"); - for(pLog = dbgMutLogListRoot ; pLog != NULL ; pLog = pLog->pNext) - dbgMutLogPrintOne(pLog); - -} - - -/* find the last log entry for that specific mutex object. Is used to delete - * a thread's own requests. Searches occur from the back. - * The pFuncDB is optional and may be NULL to indicate no specific funciont is - * reqested (aka "it is ignored" ;)). This is important for the unlock case. - */ -dbgMutLog_t *dbgMutLogFindSpecific(pthread_mutex_t *pmut, short mutop, dbgFuncDB_t *pFuncDB) -{ - dbgMutLog_t *pLog; - pthread_t mythrd = pthread_self(); - - pLog = dbgMutLogListLast; - while(pLog != NULL) { - if( pLog->mut == pmut && pLog->thrd == mythrd && pLog->mutexOp == mutop - && (pFuncDB == NULL || pLog->pFuncDB == pFuncDB)) - break; - pLog = pLog->pPrev; - } - - return pLog; -} - - -/* find mutex object from the back of the list */ -dbgMutLog_t *dbgMutLogFindFromBack(pthread_mutex_t *pmut, dbgMutLog_t *pLast) -{ - dbgMutLog_t *pLog; - - if(pLast == NULL) - pLog = dbgMutLogListLast; - else - pLog = pLast->pPrev; /* if we get the last processed one, we need to go one before it, else its an endless loop */ - - while(pLog != NULL) { - if(pLog->mut == pmut) { - break; - } - pLog = pLog->pPrev; - } - - return pLog; -} - - -/* find lock aquire for mutex from back of list */ -dbgMutLog_t *dbgMutLogFindHolder(pthread_mutex_t *pmut) -{ - dbgMutLog_t *pLog; - - pLog = dbgMutLogFindFromBack(pmut, NULL); - while(pLog != NULL) { - if(pLog->mutexOp == MUTOP_LOCK) - break; - pLog = dbgMutLogFindFromBack(pmut, pLog); - } - - return pLog; -} - -/* report wait on a mutex and add it to the mutex log */ -static inline void dbgMutexPreLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln) -{ - dbgMutLog_t *pHolder; - dbgMutLog_t *pLog; - char pszBuf[128]; - char pszHolderThrdName[64]; - char *pszHolder; - - pthread_mutex_lock(&mutMutLog); - pHolder = dbgMutLogFindHolder(pmut); - pLog = dbgMutLogAddEntry(pmut, MUTOP_LOCKWAIT, pFuncDB, ln); - - if(pHolder == NULL) - pszHolder = "[NONE]"; - else { - dbgGetThrdName(pszHolderThrdName, sizeof(pszHolderThrdName), pHolder->thrd, 1); - snprintf(pszBuf, sizeof(pszBuf)/sizeof(char), "%s:%d [%s]", pHolder->pFuncDB->file, pHolder->lockLn, pszHolderThrdName); - pszHolder = pszBuf; - } - - if(bPrintMutexAction) - dbgprintf("%s:%d:%s: mutex %p waiting on lock, held by %s\n", pFuncDB->file, ln, pFuncDB->func, (void*)pmut, pszHolder); - pthread_mutex_unlock(&mutMutLog); -} - - -/* report aquired mutex */ -static inline void dbgMutexLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int lockLn) -{ - dbgMutLog_t *pLog; - - pthread_mutex_lock(&mutMutLog); - - /* find and delete "waiting" entry */ - pLog = dbgMutLogFindSpecific(pmut, MUTOP_LOCKWAIT, pFuncDB); - assert(pLog != NULL); - dbgMutLogDelEntry(pLog); - - /* add "lock" entry */ - pLog = dbgMutLogAddEntry(pmut, MUTOP_LOCK, pFuncDB, lockLn); - dbgFuncDBAddMutexLock(pFuncDB, pmut, lockLn); - pthread_mutex_unlock(&mutMutLog); - if(bPrintMutexAction) - dbgprintf("%s:%d:%s: mutex %p aquired\n", pFuncDB->file, lockLn, pFuncDB->func, (void*)pmut); -} - -/* if we unlock, we just remove the lock aquired entry from the log list */ -static inline void dbgMutexUnlockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int unlockLn) -{ - dbgMutLog_t *pLog; - - pthread_mutex_lock(&mutMutLog); - pLog = dbgMutLogFindSpecific(pmut, MUTOP_LOCK, NULL); - assert(pLog != NULL); - - /* we found the last lock entry. We now need to see from which FuncDB we need to - * remove it. This is recorded inside the mutex log entry. - */ - dbgFuncDBRemoveMutexLock(pLog->pFuncDB, pmut); - - /* donw with the log entry, get rid of it... */ - dbgMutLogDelEntry(pLog); - - pthread_mutex_unlock(&mutMutLog); - if(bPrintMutexAction) - dbgprintf("%s:%d:%s: mutex %p UNlocked\n", pFuncDB->file, unlockLn, pFuncDB->func, (void*)pmut); -} - - -/* wrapper for pthread_mutex_lock() */ -int dbgMutexLock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) -{ - int ret; - - dbgRecordExecLocation(iStackPtr, ln); - dbgMutexPreLockLog(pmut, pFuncDB, ln); - ret = pthread_mutex_lock(pmut); - if(ret == 0) { - dbgMutexLockLog(pmut, pFuncDB, ln); - } else { - dbgprintf("%s:%d:%s: ERROR: pthread_mutex_lock() for mutex %p failed with error %d\n", - pFuncDB->file, ln, pFuncDB->func, (void*)pmut, ret); - } - - return ret; -} - - -/* wrapper for pthread_mutex_unlock() */ -int dbgMutexUnlock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) -{ - int ret; - dbgRecordExecLocation(iStackPtr, ln); - dbgMutexUnlockLog(pmut, pFuncDB, ln); - ret = pthread_mutex_unlock(pmut); - return ret; -} - - -/* wrapper for pthread_cond_wait() */ -int dbgCondWait(pthread_cond_t *cond, pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) -{ - int ret; - dbgRecordExecLocation(iStackPtr, ln); - dbgMutexUnlockLog(pmut, pFuncDB, ln); - if(bPrintMutexAction) { - dbgprintf("%s:%d:%s: mutex %p waiting on condition %p\n", pFuncDB->file, pFuncDB->line, - pFuncDB->func, (void*)pmut, (void*)cond); - } - dbgMutexPreLockLog(pmut, pFuncDB, ln); - ret = pthread_cond_wait(cond, pmut); - return ret; -} - - -/* wrapper for pthread_cond_timedwait() */ -int dbgCondTimedWait(pthread_cond_t *cond, pthread_mutex_t *pmut, const struct timespec *abstime, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) -{ - int ret; - dbgRecordExecLocation(iStackPtr, ln); - dbgMutexUnlockLog(pmut, pFuncDB, ln); - dbgMutexPreLockLog(pmut, pFuncDB, ln); - if(bPrintMutexAction) { - dbgprintf("%s:%d:%s: mutex %p waiting on condition %p (with timeout)\n", pFuncDB->file, - pFuncDB->line, pFuncDB->func, (void*)pmut, (void*)cond); - } - ret = pthread_cond_timedwait(cond, pmut, abstime); - dbgMutexLockLog(pmut, pFuncDB, ln); - return ret; -} - - -/* ------------------------- end mutex tracking code ------------------------- */ - - -/* ------------------------- malloc/free tracking code ------------------------- */ - -/* wrapper for free() */ -void dbgFree(void *pMem, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) -{ - dbgRecordExecLocation(iStackPtr, ln); - if(bLogAllocFree) { - dbgprintf("%s:%d:%s: free %p\n", pFuncDB->file, ln, pFuncDB->func, (void*) pMem); - } - free(pMem); -} - - -/* ------------------------- end malloc/free tracking code ------------------------- */ - -/* ------------------------- thread tracking code ------------------------- */ - -/* get ptr to call stack - if none exists, create a new stack - */ -static dbgThrdInfo_t *dbgGetThrdInfo(void) -{ - dbgThrdInfo_t *pThrd; - - pthread_mutex_lock(&mutCallStack); - if((pThrd = pthread_getspecific(keyCallStack)) == NULL) { - /* construct object */ - pThrd = calloc(1, sizeof(dbgThrdInfo_t)); - pThrd->thrd = pthread_self(); - (void) pthread_setspecific(keyCallStack, pThrd); - DLL_Add(CallStack, pThrd); - } - pthread_mutex_unlock(&mutCallStack); - return pThrd; -} - - - -/* find a specific thread ID. It must be present, else something is wrong - */ -static inline dbgThrdInfo_t *dbgFindThrd(pthread_t thrd) -{ - dbgThrdInfo_t *pThrd; - - for(pThrd = dbgCallStackListRoot ; pThrd != NULL ; pThrd = pThrd->pNext) { - if(pThrd->thrd == thrd) - break; - } - return pThrd; -} - - -/* build a string with the thread name. If none is set, the thread ID is - * used instead. Caller must provide buffer space. If bIncludeNumID is set - * to 1, the numerical ID is always included. - * rgerhards 2008-01-23 - */ -static void dbgGetThrdName(char *pszBuf, size_t lenBuf, pthread_t thrd, int bIncludeNumID) -{ - dbgThrdInfo_t *pThrd; - - assert(pszBuf != NULL); - - pThrd = dbgFindThrd(thrd); - - if(pThrd == 0 || pThrd->pszThrdName == NULL) { - /* no thread name, use numeric value */ - snprintf(pszBuf, lenBuf, "%lx", (long) thrd); - } else { - if(bIncludeNumID) { - snprintf(pszBuf, lenBuf, "%s (%lx)", pThrd->pszThrdName, (long) thrd); - } else { - snprintf(pszBuf, lenBuf, "%s", pThrd->pszThrdName); - } - } - -} - - -/* set a name for the current thread. The caller provided string is duplicated. - */ -void dbgSetThrdName(uchar *pszName) -{ - dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); - if(pThrd->pszThrdName != NULL) - free(pThrd->pszThrdName); - pThrd->pszThrdName = strdup((char*)pszName); -} - - -/* destructor for a call stack object */ -static void dbgCallStackDestruct(void *arg) -{ - dbgThrdInfo_t *pThrd = (dbgThrdInfo_t*) arg; - - dbgprintf("destructor for debug call stack %p called\n", pThrd); - if(pThrd->pszThrdName != NULL) { - free(pThrd->pszThrdName); - } - - pthread_mutex_lock(&mutCallStack); - DLL_Del(CallStack, pThrd); - pthread_mutex_unlock(&mutCallStack); -} - - -/* print a thread's call stack - */ -static void dbgCallStackPrint(dbgThrdInfo_t *pThrd) -{ - int i; - char pszThrdName[64]; - - pthread_mutex_lock(&mutCallStack); - dbgGetThrdName(pszThrdName, sizeof(pszThrdName), pThrd->thrd, 1); - dbgprintf("\n"); - dbgprintf("Recorded Call Order for Thread '%s':\n", pszThrdName); - for(i = 0 ; i < pThrd->stackPtr ; i++) { - dbgprintf("%d: %s:%d:%s:\n", i, pThrd->callStack[i]->file, pThrd->lastLine[i], pThrd->callStack[i]->func); - } - dbgprintf("maximum number of nested calls for this thread: %d.\n", pThrd->stackPtrMax); - dbgprintf("NOTE: not all calls may have been recorded, code does not currently guarantee that!\n"); - pthread_mutex_unlock(&mutCallStack); -} - -/* print all threads call stacks - */ -static void dbgCallStackPrintAll(void) -{ - dbgThrdInfo_t *pThrd; - /* stack info */ - for(pThrd = dbgCallStackListRoot ; pThrd != NULL ; pThrd = pThrd->pNext) { - dbgCallStackPrint(pThrd); - } -} - - -/* handler for SIGSEGV - MUST terminiate the app, but does so in a somewhat - * more meaningful way. - * rgerhards, 2008-01-22 - */ -void -sigsegvHdlr(int signum) -{ - char *signame; - struct sigaction sigAct; - - /* first, restore the default abort handler */ - memset(&sigAct, 0, sizeof (sigAct)); - sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = SIG_DFL; - sigaction(SIGABRT, &sigAct, NULL); - - /* then do our actual processing */ - if(signum == SIGSEGV) { - signame = " (SIGSEGV)"; - } else if(signum == SIGABRT) { - signame = " (SIGABRT)"; - } else { - signame = ""; - } - - dbgprintf("\n\n\n\nSignal %d%s occured, execution must be terminated.\n\n\n\n", signum, signame); - - if(bAbortTrace) { - dbgPrintAllDebugInfo(); - dbgprintf("If the call trace is empty, you may want to ./configure --enable-rtinst\n"); - dbgprintf("\n\nTo submit bug reports, visit http://www.rsyslog.com/bugs\n\n"); - } - - dbgprintf("\n\nTo submit bug reports, visit http://www.rsyslog.com/bugs\n\n"); - if(stddbg != NULL) fflush(stddbg); - if(altdbg != NULL) fflush(altdbg); - - /* and finally abort... */ - /* TODO: think about restarting rsyslog in this case: may be a good idea, - * but may also be a very bad one (restart loops!) - */ - abort(); -} - - -/* print some debug output when an object is given - * This is mostly a copy of dbgprintf, but I do not know how to combine it - * into a single function as we have variable arguments and I don't know how to call - * from one vararg function into another. I don't dig in this, it is OK for the - * time being. -- rgerhards, 2008-01-29 - */ -void -dbgoprint(obj_t *pObj, char *fmt, ...) -{ - static pthread_t ptLastThrdID = 0; - static int bWasNL = 0; - va_list ap; - static char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ - static char pszWriteBuf[1024]; - size_t lenWriteBuf; - struct timespec t; - - if(!(Debug && debugging_on)) - return; - - /* a quick and very dirty hack to enable us to display just from those objects - * that we are interested in. So far, this must be changed at compile time (and - * chances are great it is commented out while you read it. Later, this shall - * be selectable via the environment. -- rgerhards, 2008-02-20 - */ -#if 0 - if(objGetObjID(pObj) != OBJexpr) - return; -#endif - - - pthread_mutex_lock(&mutdbgoprint); - pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgoprint); - - /* The bWasNL handler does not really work. It works if no thread - * switching occurs during non-NL messages. Else, things are messed - * up. Anyhow, it works well enough to provide useful help during - * getting this up and running. It is questionable if the extra effort - * is worth fixing it, giving the limited appliability. - * rgerhards, 2005-10-25 - * I have decided that it is not worth fixing it - especially as it works - * pretty well. - * rgerhards, 2007-06-15 - */ - if(ptLastThrdID != pthread_self()) { - if(!bWasNL) { - if(stddbg != NULL) fprintf(stddbg, "\n"); - if(altdbg != NULL) fprintf(altdbg, "\n"); - bWasNL = 1; - } - ptLastThrdID = pthread_self(); - } - - /* do not cache the thread name, as the caller might have changed it - * TODO: optimized, invalidate cache when new name is set - */ - dbgGetThrdName(pszThrdName, sizeof(pszThrdName), ptLastThrdID, 0); - - if(bWasNL) { - if(bPrintTime) { - clock_gettime(CLOCK_REALTIME, &t); - if(stddbg != NULL) fprintf(stddbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - if(altdbg != NULL) fprintf(altdbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - } - if(stddbg != NULL) fprintf(stddbg, "%s: ", pszThrdName); - if(altdbg != NULL) fprintf(altdbg, "%s: ", pszThrdName); - /* print object name header if we have an object */ - if(pObj != NULL) { - if(stddbg != NULL) fprintf(stddbg, "%s: ", obj.GetName(pObj)); - if(altdbg != NULL) fprintf(altdbg, "%s: ", obj.GetName(pObj)); - } - } - bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0; - va_start(ap, fmt); - lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); - if(lenWriteBuf >= sizeof(pszWriteBuf)) { - /* if our buffer was too small, we simply truncate. TODO: maybe something better? */ - lenWriteBuf = sizeof(pszWriteBuf) - 1; - } - va_end(ap); - /* - if(stddbg != NULL) fprintf(stddbg, "%s", pszWriteBuf); - if(altdbg != NULL) fprintf(altdbg, "%s", pszWriteBuf); - */ - if(stddbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, stddbg); - if(altdbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, altdbg); - - if(stddbg != NULL) fflush(stddbg); - if(altdbg != NULL) fflush(altdbg); - pthread_cleanup_pop(1); -} - - -/* print some debug output when no object is given - * WARNING: duplicate code, see dbgoprin above! - */ -void -dbgprintf(char *fmt, ...) -{ - static pthread_t ptLastThrdID = 0; - static int bWasNL = 0; - va_list ap; - static char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ - static char pszWriteBuf[1024]; - size_t lenWriteBuf; - struct timespec t; - - if(!(Debug && debugging_on)) - return; - - pthread_mutex_lock(&mutdbgprintf); - pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprintf); - - /* The bWasNL handler does not really work. It works if no thread - * switching occurs during non-NL messages. Else, things are messed - * up. Anyhow, it works well enough to provide useful help during - * getting this up and running. It is questionable if the extra effort - * is worth fixing it, giving the limited appliability. - * rgerhards, 2005-10-25 - * I have decided that it is not worth fixing it - especially as it works - * pretty well. - * rgerhards, 2007-06-15 - */ - if(ptLastThrdID != pthread_self()) { - if(!bWasNL) { - if(stddbg != NULL) fprintf(stddbg, "\n"); - if(altdbg != NULL) fprintf(altdbg, "\n"); - bWasNL = 1; - } - ptLastThrdID = pthread_self(); - } - - /* do not cache the thread name, as the caller might have changed it - * TODO: optimized, invalidate cache when new name is set - */ - dbgGetThrdName(pszThrdName, sizeof(pszThrdName), ptLastThrdID, 0); - - if(bWasNL) { - if(bPrintTime) { - clock_gettime(CLOCK_REALTIME, &t); - if(stddbg != NULL) fprintf(stddbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - if(altdbg != NULL) fprintf(altdbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); - } - if(stddbg != NULL) fprintf(stddbg, "%s: ", pszThrdName); - if(altdbg != NULL) fprintf(altdbg, "%s: ", pszThrdName); - } - bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0; - va_start(ap, fmt); - lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); - if(lenWriteBuf >= sizeof(pszWriteBuf)) { - /* if our buffer was too small, we simply truncate. TODO: maybe something better? */ - lenWriteBuf = sizeof(pszWriteBuf) - 1; - } - va_end(ap); - /* - if(stddbg != NULL) fprintf(stddbg, "%s", pszWriteBuf); - if(altdbg != NULL) fprintf(altdbg, "%s", pszWriteBuf); - */ - if(stddbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, stddbg); - if(altdbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, altdbg); - - if(stddbg != NULL) fflush(stddbg); - if(altdbg != NULL) fflush(altdbg); - pthread_cleanup_pop(1); -} - -void tester(void) -{ -BEGINfunc -ENDfunc -} - -/* handler called when a function is entered. This function creates a new - * funcDB on the heap if the passed-in pointer is NULL. - */ -int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int line) -{ - int iStackPtr = 0; /* TODO: find some better default, this one hurts the least, but it is not clean */ - dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); - dbgFuncDBListEntry_t *pFuncDBListEntry; - unsigned int i; - dbgFuncDB_t *pFuncDB; - - assert(ppFuncDB != NULL); - assert(file != NULL); - assert(func != NULL); - pFuncDB = *ppFuncDB; - assert((pFuncDB == NULL) || (pFuncDB->magic == dbgFUNCDB_MAGIC)); - - if(pFuncDB == NULL) { - /* we do not yet have a funcDB and need to create a new one. We also add it - * to the linked list of funcDBs. Please note that when a module is unloaded and - * then reloaded again, we currently do not try to find its previous funcDB but - * instead create a duplicate. While finding the past one is straightforward, it - * opens up the question what to do with e.g. mutex data left in it. We do not - * yet see any need to handle these questions, so duplicaton seems to be the right - * thing to do. -- rgerhards, 2008-03-10 - */ - /* dbgprintf("%s:%d:%s: called first time, initializing FuncDB\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); */ - /* get a new funcDB and add it to the list (all of this is protected by the mutex) */ - pthread_mutex_lock(&mutFuncDBList); - if((pFuncDBListEntry = calloc(1, sizeof(dbgFuncDBListEntry_t))) == NULL) { - dbgprintf("Error %d allocating memory for FuncDB List entry, not adding\n", errno); - pthread_mutex_unlock(&mutFuncDBList); - goto exit_it; - } else { - if((pFuncDB = calloc(1, sizeof(dbgFuncDB_t))) == NULL) { - dbgprintf("Error %d allocating memory for FuncDB, not adding\n", errno); - free(pFuncDBListEntry); - pthread_mutex_unlock(&mutFuncDBList); - goto exit_it; - } else { - pFuncDBListEntry->pFuncDB = pFuncDB; - pFuncDBListEntry->pNext = pFuncDBListRoot; - pFuncDBListRoot = pFuncDBListEntry; - } - } - /* now intialize the funcDB - * note that we duplicate the strings, because the address provided may go away - * if a loadable module is unloaded! - */ - pFuncDB->magic = dbgFUNCDB_MAGIC; - pFuncDB->file = strdup(file); - pFuncDB->func = strdup(func); - pFuncDB->line = line; - pFuncDB->nTimesCalled = 0; - for(i = 0 ; i < sizeof(pFuncDB->mutInfo)/sizeof(dbgFuncDBmutInfoEntry_t) ; ++i) { - pFuncDB->mutInfo[i].lockLn = -1; /* set to not Locked */ - } - - /* a round of safety checks... */ - if(pFuncDB->file == NULL || pFuncDB->func == NULL) { - dbgprintf("Error %d allocating memory for FuncDB, not adding\n", errno); - /* do a little bit of cleanup */ - if(pFuncDB->file != NULL) - free(pFuncDB->file); - if(pFuncDB->func != NULL) - free(pFuncDB->func); - free(pFuncDB); - free(pFuncDBListEntry); - pthread_mutex_unlock(&mutFuncDBList); - goto exit_it; - } - - /* done mutex-protected operations */ - pthread_mutex_unlock(&mutFuncDBList); - - *ppFuncDB = pFuncDB; /* all went well, so we can update the caller */ - } - - /* when we reach this point, we have a fully-initialized FuncDB! */ - ATOMIC_INC(pFuncDB->nTimesCalled); - if(bLogFuncFlow && dbgPrintNameIsInList((const uchar*)pFuncDB->file, printNameFileRoot)) - dbgprintf("%s:%d: %s: enter\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); - if(pThrd->stackPtr >= (int) (sizeof(pThrd->callStack) / sizeof(dbgFuncDB_t*))) { - dbgprintf("%s:%d: %s: debug module: call stack for this thread full, suspending call tracking\n", - pFuncDB->file, pFuncDB->line, pFuncDB->func); - iStackPtr = pThrd->stackPtr; - } else { - iStackPtr = pThrd->stackPtr++; - if(pThrd->stackPtr > pThrd->stackPtrMax) - pThrd->stackPtrMax = pThrd->stackPtr; - pThrd->callStack[iStackPtr] = pFuncDB; - pThrd->lastLine[iStackPtr] = line; - } - -exit_it: - return iStackPtr; -} - - -/* handler called when a function is exited - */ -void dbgExitFunc(dbgFuncDB_t *pFuncDB, int iStackPtrRestore, int iRet) -{ - dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); - - assert(iStackPtrRestore >= 0); - assert(pFuncDB != NULL); - assert(pFuncDB->magic == dbgFUNCDB_MAGIC); - - dbgFuncDBPrintActiveMutexes(pFuncDB, "WARNING: mutex still owned by us as we exit function, mutex: ", pthread_self()); - if(bLogFuncFlow && dbgPrintNameIsInList((const uchar*)pFuncDB->file, printNameFileRoot)) { - if(iRet == RS_RET_NO_IRET) - dbgprintf("%s:%d: %s: exit: (no iRet)\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); - else - dbgprintf("%s:%d: %s: exit: %d\n", pFuncDB->file, pFuncDB->line, pFuncDB->func, iRet); - } - pThrd->stackPtr = iStackPtrRestore; - if(pThrd->stackPtr < 0) { - dbgprintf("Stack pointer for thread %lx below 0 - resetting (some RETiRet still wrong!)\n", (long) pthread_self()); - pThrd->stackPtr = 0; - } -} - - -/* externally-callable handler to record the last exec location. We use a different function - * so that the internal one can be inline. - */ -void -dbgSetExecLocation(int iStackPtr, int line) -{ - dbgRecordExecLocation(iStackPtr, line); -} - - -void dbgPrintAllDebugInfo(void) -{ - dbgCallStackPrintAll(); - dbgMutLogPrintAll(); - if(bPrintFuncDBOnExit) - dbgFuncDBPrintAll(); -} - - -/* Handler for SIGUSR2. Dumps all available debug output - */ -static void sigusr2Hdlr(int __attribute__((unused)) signum) -{ - dbgprintf("SIGUSR2 received, dumping debug information\n"); - dbgPrintAllDebugInfo(); -} - -/* support system to set debug options at runtime */ - - -/* parse a param/value pair from the current location of the - * option string. Returns 1 if an option was found, 0 - * otherwise. 0 means there are NO MORE options to be - * processed. -- rgerhards, 2008-02-28 - */ -static int -dbgGetRTOptNamVal(uchar **ppszOpt, uchar **ppOptName, uchar **ppOptVal) -{ - int bRet = 0; - uchar *p; - size_t i; - static uchar optname[128]; /* not thread- or reentrant-safe, but that */ - static uchar optval[1024]; /* doesn't matter (called only once at startup) */ - - assert(ppszOpt != NULL); - assert(*ppszOpt != NULL); - - /* make sure we have some initial values */ - optname[0] = '\0'; - optval[0] = '\0'; - - p = *ppszOpt; - /* skip whitespace */ - while(*p && isspace(*p)) - ++p; - - /* name - up until '=' or whitespace */ - i = 0; - while(i < (sizeof(optname)/sizeof(uchar) - 1) && *p && *p != '=' && !isspace(*p)) { - optname[i++] = *p++; - } - - if(i > 0) { - bRet = 1; - optname[i] = '\0'; - if(*p == '=') { - /* we have a value, get it */ - ++p; - i = 0; - while(i < (sizeof(optval)/sizeof(uchar) - 1) && *p && !isspace(*p)) { - optval[i++] = *p++; - } - optval[i] = '\0'; - } - } - - /* done */ - *ppszOpt = p; - *ppOptName = optname; - *ppOptVal = optval; - return bRet; -} - - -/* create new PrintName list entry and add it to list (they will never - * be removed. -- rgerhards, 2008-02-28 - */ -static void -dbgPrintNameAdd(uchar *pName, dbgPrintName_t **ppRoot) -{ - dbgPrintName_t *pEntry; - - if((pEntry = calloc(1, sizeof(dbgPrintName_t))) == NULL) { - fprintf(stderr, "ERROR: out of memory during debug setup\n"); - exit(1); - } - - if((pEntry->pName = (uchar*) strdup((char*) pName)) == NULL) { - fprintf(stderr, "ERROR: out of memory during debug setup\n"); - exit(1); - } - - if(*ppRoot != NULL) { - pEntry->pNext = *ppRoot; /* we enqueue at the front */ - } - *ppRoot = pEntry; - -printf("Name %s added to %p\n", pName, *ppRoot); -} - - -/* check if name is in a printName list - returns 1 if so, 0 otherwise. - * There is one special handling: if the root pointer is NULL, the function - * always returns 1. This is because when no name is set, output shall be - * unrestricted. - * rgerhards, 2008-02-28 - */ -static int -dbgPrintNameIsInList(const uchar *pName, dbgPrintName_t *pRoot) -{ - int bFound = 0; - dbgPrintName_t *pEntry = pRoot; - - if(pRoot == NULL) - bFound = 1; - - while(pEntry != NULL && !bFound) { - if(!strcasecmp((char*)pEntry->pName, (char*)pName)) { - bFound = 1; - } else { - pEntry = pEntry->pNext; - } - } - - return bFound; -} - - -/* read in the runtime options - * rgerhards, 2008-02-28 - */ -static void -dbgGetRuntimeOptions(void) -{ - uchar *pszOpts; - uchar *optval; - uchar *optname; - - /* set some defaults */ - stddbg = stdout; - - if((pszOpts = (uchar*) getenv("RSYSLOG_DEBUG")) != NULL) { - /* we have options set, so let's process them */ - while(dbgGetRTOptNamVal(&pszOpts, &optname, &optval)) { - if(!strcasecmp((char*)optname, "help")) { - fprintf(stderr, - "rsyslogd runtime debug support - help requested, rsyslog terminates\n\n" - "environment variables:\n" - "addional logfile: export RSYSLOG_DEBUGFILE=\"/path/to/file\"\n" - "to set: export RSYSLOG_DEBUG=\"cmd cmd cmd\"\n\n" - "Commands are (all case-insensitive):\n" - "help (this list, terminates rsyslogd\n" - "LogFuncFlow\n" - "LogAllocFree (very partly implemented)\n" - "PrintFuncDB\n" - "PrintMutexAction\n" - "PrintAllDebugInfoOnExit (not yet implemented)\n" - "NoLogTimestamp\n" - "Nostdoout\n" - "filetrace=file (may be provided multiple times)\n" - "\nSee debug.html in your doc set or http://www.rsyslog.com for details\n"); - exit(1); - } else if(!strcasecmp((char*)optname, "debug")) { - /* this is earlier in the process than the -d option, as such it - * allows us to spit out debug messages from the very beginning. - */ - Debug = 1; - debugging_on = 1; - } else if(!strcasecmp((char*)optname, "logfuncflow")) { - bLogFuncFlow = 1; - } else if(!strcasecmp((char*)optname, "logallocfree")) { - bLogAllocFree = 1; - } else if(!strcasecmp((char*)optname, "printfuncdb")) { - bPrintFuncDBOnExit = 1; - } else if(!strcasecmp((char*)optname, "printmutexaction")) { - bPrintMutexAction = 1; - } else if(!strcasecmp((char*)optname, "printalldebuginfoonexit")) { - bPrintAllDebugOnExit = 1; - } else if(!strcasecmp((char*)optname, "nologtimestamp")) { - bPrintTime = 0; - } else if(!strcasecmp((char*)optname, "nostdout")) { - stddbg = NULL; - } else if(!strcasecmp((char*)optname, "noaborttrace")) { - bAbortTrace = 0; - } else if(!strcasecmp((char*)optname, "filetrace")) { - if(*optval == '\0') { - fprintf(stderr, "Error: logfile debug option requires filename, " - "e.g. \"logfile=debug.c\"\n"); - exit(1); - } else { - /* create new entry and add it to list */ - dbgPrintNameAdd(optval, &printNameFileRoot); - } - } else { - fprintf(stderr, "Error: invalid debug option '%s', value '%s' - ignored\n", - optval, optname); - } - } - } -} - - -/* end support system to set debug options at runtime */ - -rsRetVal dbgClassInit(void) -{ - DEFiRet; - - struct sigaction sigAct; - sigset_t sigSet; - - (void) pthread_key_create(&keyCallStack, dbgCallStackDestruct); /* MUST be the first action done! */ - - /* we initialize all Mutexes with code, as some platforms seem to have - * bugs in the static initializer macros. So better be on the safe side... - * rgerhards, 2008-03-06 - */ - pthread_mutex_init(&mutFuncDBList, NULL); - pthread_mutex_init(&mutMutLog, NULL); - pthread_mutex_init(&mutCallStack, NULL); - pthread_mutex_init(&mutdbgprintf, NULL); - pthread_mutex_init(&mutdbgoprint, NULL); - - /* while we try not to use any of the real rsyslog code (to avoid infinite loops), we - * need to have the ability to query object names. Thus, we need to obtain a pointer to - * the object interface. -- rgerhards, 2008-02-29 - */ - CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ - - memset(&sigAct, 0, sizeof (sigAct)); - sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = sigusr2Hdlr; - sigaction(SIGUSR2, &sigAct, NULL); - - sigemptyset(&sigSet); - sigaddset(&sigSet, SIGUSR2); - pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL); - - dbgGetRuntimeOptions(); /* init debug system from environment */ - pszAltDbgFileName = getenv("RSYSLOG_DEBUGLOG"); - - if(pszAltDbgFileName != NULL) { - /* we have a secondary file, so let's open it) */ - if((altdbg = fopen(pszAltDbgFileName, "w")) == NULL) { - fprintf(stderr, "alternate debug file could not be opened, ignoring. Error: %s\n", strerror(errno)); - } - } - - dbgSetThrdName((uchar*)"main thread"); - -finalize_it: - RETiRet; -} - - -rsRetVal dbgClassExit(void) -{ - dbgFuncDBListEntry_t *pFuncDBListEtry, *pToDel; - pthread_key_delete(keyCallStack); - - if(bPrintAllDebugOnExit) - dbgPrintAllDebugInfo(); - - if(altdbg != NULL) - fclose(altdbg); - - /* now free all of our memory to make the memory debugger happy... */ - pFuncDBListEtry = pFuncDBListRoot; - while(pFuncDBListEtry != NULL) { - pToDel = pFuncDBListEtry; - pFuncDBListEtry = pFuncDBListEtry->pNext; - free(pToDel->pFuncDB->file); - free(pToDel->pFuncDB->func); - free(pToDel->pFuncDB); - free(pToDel); - } - - return RS_RET_OK; -} -/* vi:set ai: - */ diff --git a/debug.h b/debug.h deleted file mode 100644 index 4dcc593a..00000000 --- a/debug.h +++ /dev/null @@ -1,145 +0,0 @@ -/* debug.h - * - * Definitions for the debug and run-time analysis support module. - * Contains a lot of macros. - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef DEBUG_H_INCLUDED -#define DEBUG_H_INCLUDED - -#include -#include "obj-types.h" - -/* external static data elements (some time to be replaced) */ -extern int Debug; /* debug flag - read-only after startup */ -extern int debugging_on; /* read-only, except on sig USR1 */ - -/* data types */ - -/* the function database. It is used as a static var inside each function. That provides - * us the fast access to it that we need to make the instrumentation work. It's address - * also serves as a unique function identifier and can be used inside other structures - * to refer to the function (e.g. for pretty-printing names). - * rgerhards, 2008-01-24 - */ -typedef struct dbgFuncDBmutInfoEntry_s { - pthread_mutex_t *pmut; - int lockLn; /* line where it was locked (inside our func): -1 means mutex is not locked */ - pthread_t thrd; /* thrd where the mutex was locked */ - unsigned long lInvocation; /* invocation (unique during program run!) of this function that locked the mutex */ -} dbgFuncDBmutInfoEntry_t; -typedef struct dbgFuncDB_s { - unsigned magic; - unsigned long nTimesCalled; - char *func; - char *file; - int line; - dbgFuncDBmutInfoEntry_t mutInfo[5]; - /* remember to update the initializer if you add anything or change the order! */ -} dbgFuncDB_t; -#define dbgFUNCDB_MAGIC 0xA1B2C3D4 -#define dbgFuncDB_t_INITIALIZER \ - { \ - .magic = dbgFUNCDB_MAGIC,\ - .nTimesCalled = 0,\ - .func = __func__, \ - .file = __FILE__, \ - .line = __LINE__ \ - } - -/* the structure below was originally just the thread's call stack, but it has - * a bit evolved over time. So we have now ended up with the fact that it - * all debug info we know about the thread. - */ -typedef struct dbgCallStack_s { - pthread_t thrd; - dbgFuncDB_t *callStack[500]; - int lastLine[500]; /* last line where code execution was seen */ - int stackPtr; - int stackPtrMax; - char *pszThrdName; - struct dbgCallStack_s *pNext; - struct dbgCallStack_s *pPrev; -} dbgThrdInfo_t; - - -/* prototypes */ -rsRetVal dbgClassInit(void); -rsRetVal dbgClassExit(void); -void sigsegvHdlr(int signum); -void dbgoprint(obj_t *pObj, char *fmt, ...) __attribute__((format(printf, 2, 3))); -void dbgprintf(char *fmt, ...) __attribute__((format(printf, 1, 2))); -int dbgMutexLock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); -int dbgMutexUnlock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); -int dbgCondWait(pthread_cond_t *cond, pthread_mutex_t *pmut, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); -int dbgCondTimedWait(pthread_cond_t *cond, pthread_mutex_t *pmut, const struct timespec *abstime, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); -void dbgFree(void *pMem, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr); -int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int line); -void dbgExitFunc(dbgFuncDB_t *pFuncDB, int iStackPtrRestore, int iRet); -void dbgSetExecLocation(int iStackPtr, int line); -void dbgSetThrdName(uchar *pszName); -void dbgPrintAllDebugInfo(void); - -/* macros */ -#ifdef RTINST -# define BEGINfunc static dbgFuncDB_t *pdbgFuncDB; int dbgCALLStaCK_POP_POINT = dbgEntrFunc(&pdbgFuncDB, __FILE__, __func__, __LINE__); -# define ENDfunc dbgExitFunc(pdbgFuncDB, dbgCALLStaCK_POP_POINT, RS_RET_NO_IRET); -# define ENDfuncIRet dbgExitFunc(pdbgFuncDB, dbgCALLStaCK_POP_POINT, iRet); -# define ASSERT(x) assert(x) -#else -# define BEGINfunc -# define ENDfunc -# define ENDfuncIRet -# define ASSERT(x) -#endif -#ifdef RTINST -# define RUNLOG dbgSetExecLocation(dbgCALLStaCK_POP_POINT, __LINE__); dbgprintf("%s:%d: %s: log point\n", __FILE__, __LINE__, __func__) -# define RUNLOG_VAR(fmt, x) dbgSetExecLocation(dbgCALLStaCK_POP_POINT, __LINE__);\ - dbgprintf("%s:%d: %s: var '%s'[%s]: " fmt "\n", __FILE__, __LINE__, __func__, #x, fmt, x) -# define RUNLOG_STR(str) dbgSetExecLocation(dbgCALLStaCK_POP_POINT, __LINE__);\ - dbgprintf("%s:%d: %s: %s\n", __FILE__, __LINE__, __func__, str) -#else -# define RUNLOG -# define RUNLOG_VAR(fmt, x) -# define RUNLOG_STR(str) -#endif - -/* mutex operations */ -#define MUTOP_LOCKWAIT 1 -#define MUTOP_LOCK 2 -#define MUTOP_UNLOCK 3 - - -/* debug aides */ -#ifdef RTINST -#define d_pthread_mutex_lock(x) dbgMutexLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) -#define d_pthread_mutex_unlock(x) dbgMutexUnlock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) -#define d_pthread_cond_wait(cond, mut) dbgCondWait(cond, mut, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) -#define d_pthread_cond_timedwait(cond, mut, to) dbgCondTimedWait(cond, mut, to, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) -#define d_free(x) dbgFree(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) -#else -#define d_pthread_mutex_lock(x) pthread_mutex_lock(x) -#define d_pthread_mutex_unlock(x) pthread_mutex_unlock(x) -#define d_pthread_cond_wait(cond, mut) pthread_cond_wait(cond, mut) -#define d_pthread_cond_timedwait(cond, mut, to) pthread_cond_timedwait(cond, mut, to) -#define d_free(x) free(x) -#endif -#endif /* #ifndef DEBUG_H_INCLUDED */ diff --git a/errmsg.c b/errmsg.c deleted file mode 100644 index 907046b9..00000000 --- a/errmsg.c +++ /dev/null @@ -1,120 +0,0 @@ -/* The errmsg object. - * - * Module begun 2008-03-05 by Rainer Gerhards, based on some code - * from syslogd.c - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#include "config.h" -#include -#include -#include -#include -#include - -#include "rsyslog.h" -#include "syslogd.h" -#include "obj.h" -#include "errmsg.h" -#include "sysvar.h" -#include "srUtils.h" -#include "stringbuf.h" - -/* static data */ -DEFobjStaticHelpers - - -/* ------------------------------ methods ------------------------------ */ - - -/* TODO: restructure this code some time. Especially look if we need - * to check errno and, if so, how to do that in a clean way. - */ -static void __attribute__((format(printf, 2, 3))) -LogError(int __attribute__((unused)) iErrCode, char *fmt, ... ) -{ - va_list ap; - char buf[1024]; - char msg[1024]; - char errStr[1024]; - size_t lenBuf; - - BEGINfunc - assert(fmt != NULL); - /* Format parameters */ - va_start(ap, fmt); - lenBuf = vsnprintf(buf, sizeof(buf), fmt, ap); - if(lenBuf >= sizeof(buf)) { - /* if our buffer was too small, we simply truncate. */ - lenBuf--; - } - va_end(ap); - - /* Log the error now */ - buf[sizeof(buf)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ - - dbgprintf("Called LogError, msg: %s\n", buf); - - if (errno == 0) { - snprintf(msg, sizeof(msg), "%s", buf); - } else { - rs_strerror_r(errno, errStr, sizeof(errStr)); - snprintf(msg, sizeof(msg), "%s: %s", buf, errStr); - } - msg[sizeof(msg)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ - errno = 0; - logmsgInternal(LOG_SYSLOG|LOG_ERR, msg, ADDDATE); - - ENDfunc -} - - -/* queryInterface function - * rgerhards, 2008-03-05 - */ -BEGINobjQueryInterface(errmsg) -CODESTARTobjQueryInterface(errmsg) - if(pIf->ifVersion != errmsgCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->LogError = LogError; -finalize_it: -ENDobjQueryInterface(errmsg) - - -/* Initialize the errmsg class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-02-19 - */ -BEGINAbstractObjClassInit(errmsg, 1, OBJ_IS_CORE_MODULE) /* class, version */ - /* request objects we use */ - - /* set our own handlers */ -ENDObjClassInit(errmsg) - -/* vi:set ai: - */ diff --git a/errmsg.h b/errmsg.h deleted file mode 100644 index 12469581..00000000 --- a/errmsg.h +++ /dev/null @@ -1,45 +0,0 @@ -/* The errmsg object. It is used to emit error message inside rsyslog. - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef INCLUDED_ERRMSG_H -#define INCLUDED_ERRMSG_H - -#include "errmsg.h" - -/* TODO: define error codes */ -#define NO_ERRCODE -1 - -/* the errmsg object */ -typedef struct errmsg_s { -} errmsg_t; - - -/* interfaces */ -BEGINinterface(errmsg) /* name must also be changed in ENDinterface macro! */ - void __attribute__((format(printf, 2, 3))) (*LogError)(int iErrCode, char *pszErrFmt, ... ); -ENDinterface(errmsg) -#define errmsgCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -PROTOTYPEObj(errmsg); - -#endif /* #ifndef INCLUDED_ERRMSG_H */ diff --git a/glbl.h b/glbl.h index 6d08ddd5..5385006a 100644 --- a/glbl.h +++ b/glbl.h @@ -10,22 +10,23 @@ * * Copyright 2008 Rainer Gerhards and Adiscon GmbH. * - * This file is part of rsyslog. + * This file is part of the rsyslog runtime library. * - * 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 rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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, + * The rsyslog runtime library 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. + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with Rsyslog. If not, see . + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . * * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. */ #ifndef GLOBALS_H_INCLUDED diff --git a/liblogging-stub.h b/liblogging-stub.h deleted file mode 100644 index 03315f08..00000000 --- a/liblogging-stub.h +++ /dev/null @@ -1,26 +0,0 @@ -/* This is a (now *very slim*) stub for some liblogging - * code we use in rsyslog. - * - * Copyright (C) 2004, 2007 by 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef __LIB3195_LIBLOGGINGSTUB_H_INCLUDED__ -#define __LIB3195_LIBLOGGINGSTUB_H_INCLUDED__ 1 -#include -#endif diff --git a/linkedlist.c b/linkedlist.c deleted file mode 100644 index 9adf40c4..00000000 --- a/linkedlist.c +++ /dev/null @@ -1,413 +0,0 @@ -/* linkedlist.c - * This file set implements a generic linked list object. It can be used - * wherever a linke list is required. - * - * NOTE: we do not currently provide a constructor and destructor for the - * object itself as we assume it will always be part of another strucuture. - * Having a pointer to it, I think, does not really make sense but costs - * performance. Consequently, there is is llInit() and llDestroy() and they - * do what a constructor and destructur do, except for creating the - * linkedList_t structure itself. - * - * File begun on 2007-07-31 by RGerhards - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" - -#include -#include -#include - -#include "rsyslog.h" -#include "linkedlist.h" - - -/* Initialize an existing linkedList_t structure - * pKey destructor may be zero to take care of non-keyed lists. - */ -rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pKeyDestructor)(void*), int (*pCmpOp)()) -{ - assert(pThis != NULL); - assert(pEltDestructor != NULL); - - pThis->pEltDestruct = pEltDestructor; - pThis->pKeyDestruct = pKeyDestructor; - pThis->cmpOp = pCmpOp; - pThis->pKey = NULL; - pThis->iNumElts = 0; - pThis->pRoot = NULL; - pThis->pLast = NULL; - - return RS_RET_OK; -}; - - -/* llDestroyEltData - destroys a list element - * It is a separate function as the - * functionality is needed in multiple code-pathes. - */ -static rsRetVal llDestroyElt(linkedList_t *pList, llElt_t *pElt) -{ - DEFiRet; - - assert(pList != NULL); - assert(pElt != NULL); - - /* we ignore errors during destruction, as we need to try - * free the element in any case. - */ - if(pElt->pData != NULL) - pList->pEltDestruct(pElt->pData); - if(pElt->pKey != NULL) - pList->pKeyDestruct(pElt->pKey); - free(pElt); - pList->iNumElts--; /* one less */ - - RETiRet; -} - - -/* llDestroy - destroys a COMPLETE linkedList - */ -rsRetVal llDestroy(linkedList_t *pThis) -{ - DEFiRet; - llElt_t *pElt; - llElt_t *pEltPrev; - - assert(pThis != NULL); - - pElt = pThis->pRoot; - while(pElt != NULL) { - pEltPrev = pElt; - pElt = pElt->pNext; - /* we ignore errors during destruction, as we need to try - * finish the linked list in any case. - */ - llDestroyElt(pThis, pEltPrev); - } - /* now clean up the pointers */ - pThis->pRoot = NULL; - pThis->pLast = NULL; - - RETiRet; -} - -/* llDestroyRootElt - destroy the root element but otherwise - * keeps this list intact. -- rgerhards, 2007-08-03 - */ -rsRetVal llDestroyRootElt(linkedList_t *pThis) -{ - DEFiRet; - llElt_t *pPrev; - - if(pThis->pRoot == NULL) { - ABORT_FINALIZE(RS_RET_EMPTY_LIST); - } - - pPrev = pThis->pRoot; - if(pPrev->pNext == NULL) { - /* it was the only list element */ - pThis->pLast = NULL; - pThis->pRoot = NULL; - } else { - /* there are other list elements */ - pThis->pRoot = pPrev->pNext; - } - - CHKiRet(llDestroyElt(pThis, pPrev)); - -finalize_it: - RETiRet; -} - - -/* get next user data element of a linked list. The caller must also - * provide a "cookie" to the function. On initial call, it must be - * NULL. Other than that, the caller is not allowed to to modify the - * cookie. In the current implementation, the cookie is an actual - * pointer to the current list element, but this is nothing that the - * caller should rely on. - */ -rsRetVal llGetNextElt(linkedList_t *pThis, linkedListCookie_t *ppElt, void **ppUsr) -{ - llElt_t *pElt; - DEFiRet; - - assert(pThis != NULL); - assert(ppElt != NULL); - assert(ppUsr != NULL); - - pElt = *ppElt; - - pElt = (pElt == NULL) ? pThis->pRoot : pElt->pNext; - - if(pElt == NULL) { - iRet = RS_RET_END_OF_LINKEDLIST; - } else { - *ppUsr = pElt->pData; - } - - *ppElt = pElt; - - RETiRet; -} - - -/* return the key of an Elt - * rgerhards, 2007-09-11: note that ppDatea is actually a void**, - * but I need to make it a void* to avoid lots of compiler warnings. - * It will be converted later down in the code. - */ -rsRetVal llGetKey(llElt_t *pThis, void *ppData) -{ - assert(pThis != NULL); - assert(ppData != NULL); - - *(void**) ppData = pThis->pKey; - - return RS_RET_OK; -} - - -/* construct a new llElt_t - */ -static rsRetVal llEltConstruct(llElt_t **ppThis, void *pKey, void *pData) -{ - DEFiRet; - llElt_t *pThis; - - assert(ppThis != NULL); - - if((pThis = (llElt_t*) calloc(1, sizeof(llElt_t))) == NULL) { - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - - pThis->pKey = pKey; - pThis->pData = pData; - -finalize_it: - *ppThis = pThis; - RETiRet; -} - - -/* append a user element to the end of the linked list. This includes setting a key. If no - * key is desired, simply pass in a NULL pointer for it. - */ -rsRetVal llAppend(linkedList_t *pThis, void *pKey, void *pData) -{ - llElt_t *pElt; - DEFiRet; - - CHKiRet(llEltConstruct(&pElt, pKey, pData)); - - pThis->iNumElts++; /* one more */ - if(pThis->pLast == NULL) { - pThis->pRoot = pElt; - } else { - pThis->pLast->pNext = pElt; - } - pThis->pLast = pElt; - -finalize_it: - RETiRet; -} - - -/* unlink a requested element. As we have singly-linked lists, the - * caller also needs to pass in the previous element (or NULL, if it is the - * root element). - * rgerhards, 2007-11-21 - */ -static rsRetVal llUnlinkElt(linkedList_t *pThis, llElt_t *pElt, llElt_t *pEltPrev) -{ - assert(pElt != NULL); - - if(pEltPrev == NULL) { /* root element? */ - pThis->pRoot = pElt->pNext; - } else { /* regular element */ - pEltPrev->pNext = pElt->pNext; - } - - if(pElt == pThis->pLast) - pThis->pLast = pEltPrev; - - return RS_RET_OK; -} - - -/* unlinks and immediately deletes an element. Previous element must - * be given (or zero if the root element is to be deleted). - * rgerhards, 2007-11-21 - */ -static rsRetVal llUnlinkAndDelteElt(linkedList_t *pThis, llElt_t *pElt, llElt_t *pEltPrev) -{ - DEFiRet; - - assert(pElt != NULL); - - CHKiRet(llUnlinkElt(pThis, pElt, pEltPrev)); - CHKiRet(llDestroyElt(pThis, pElt)); - -finalize_it: - RETiRet; -} - -/* find a user element based on the provided key - this is the - * internal variant, which also tracks the last element pointer - * before the found element. This is necessary to delete elements. - * NULL means there is no element in front of it, aka the found elt - * is the root elt. - * rgerhards, 2007-11-21 - */ -static rsRetVal llFindElt(linkedList_t *pThis, void *pKey, llElt_t **ppElt, llElt_t **ppEltPrev) -{ - DEFiRet; - llElt_t *pElt; - llElt_t *pEltPrev = NULL; - int bFound = 0; - - assert(pThis != NULL); - assert(pKey != NULL); - assert(ppElt != NULL); - assert(ppEltPrev != NULL); - - pElt = pThis->pRoot; - while(pElt != NULL && bFound == 0) { - if(pThis->cmpOp(pKey, pElt->pKey) == 0) - bFound = 1; - else { - pEltPrev = pElt; - pElt = pElt->pNext; - } - } - - if(bFound == 1) { - *ppElt = pElt; - *ppEltPrev = pEltPrev; - } else - iRet = RS_RET_NOT_FOUND; - - RETiRet; -} - - -/* find a user element based on the provided key - */ -rsRetVal llFind(linkedList_t *pThis, void *pKey, void **ppData) -{ - DEFiRet; - llElt_t *pElt; - llElt_t *pEltPrev; - - CHKiRet(llFindElt(pThis, pKey, &pElt, &pEltPrev)); - - /* if we reach this point, we have found the element */ - *ppData = pElt->pData; - -finalize_it: - RETiRet; -} - - -/* find a delete an element based on user-provided key. The element is - * delete, the caller does not receive anything. If we need to receive - * the element before destruction, we may implement an llFindAndUnlink() - * at that time. - * rgerhards, 2007-11-21 - */ -rsRetVal llFindAndDelete(linkedList_t *pThis, void *pKey) -{ - DEFiRet; - llElt_t *pElt; - llElt_t *pEltPrev; - - CHKiRet(llFindElt(pThis, pKey, &pElt, &pEltPrev)); - - /* if we reach this point, we have found an element */ - CHKiRet(llUnlinkAndDelteElt(pThis, pElt, pEltPrev)); - -finalize_it: - RETiRet; -} - - -/* provide the count of linked list elements - */ -rsRetVal llGetNumElts(linkedList_t *pThis, int *piCnt) -{ - DEFiRet; - - assert(pThis != NULL); - assert(piCnt != NULL); - - *piCnt = pThis->iNumElts; - - RETiRet; -} - - -/* execute a function on all list members. The functions receives a - * user-supplied parameter, which may be either a simple value - * or a pointer to a structure with more data. If the user-supplied - * function does not return RS_RET_OK, this function here terminates. - * rgerhards, 2007-08-02 - * rgerhards, 2007-11-21: added functionality to delete a list element. - * If the called user function returns RS_RET_OK_DELETE_LISTENTRY the current element - * is deleted. - */ -rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam) -{ - DEFiRet; - rsRetVal iRetLL; - void *pData; - linkedListCookie_t llCookie = NULL; - linkedListCookie_t llCookiePrev = NULL; /* previous list element (needed for deletion, NULL = at root) */ - - assert(pThis != NULL); - assert(pFunc != NULL); - - while((iRetLL = llGetNextElt(pThis, &llCookie, (void**)&pData)) == RS_RET_OK) { - iRet = pFunc(pData, pParam); - if(iRet == RS_RET_OK_DELETE_LISTENTRY) { - /* delete element */ - CHKiRet(llUnlinkAndDelteElt(pThis, llCookie, llCookiePrev)); - /* we need to revert back, as we have just deleted the current element. - * So the actual current element is the one before it, which happens to be - * stored in llCookiePrev. -- rgerhards, 2007-11-21 - */ - llCookie = llCookiePrev; - } else if (iRet != RS_RET_OK) { - goto finalize_it; - } - llCookiePrev = llCookie; - } - - if(iRetLL != RS_RET_END_OF_LINKEDLIST) - iRet = iRetLL; - -finalize_it: - RETiRet; -} - - -/* - * vi:set ai: - */ diff --git a/linkedlist.h b/linkedlist.h deleted file mode 100644 index 98fb76a5..00000000 --- a/linkedlist.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Definition of the linkedlist object. - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#ifndef LINKEDLIST_H_INCLUDED -#define LINKEDLIST_H_INCLUDED - -/* this is a single entry for a parse routine. It describes exactly - * one entry point/handler. - * The short name is cslch (Configfile SysLine CommandHandler) - */ -struct llElt_s { /* config file sysline parse entry */ - struct llElt_s *pNext; - void *pKey; /* key for this element */ - void *pData; /* user-supplied data pointer */ -}; -typedef struct llElt_s llElt_t; - - -/* this is the list of known configuration commands with pointers to - * their handlers. - * The short name is cslc (Configfile SysLine Command) - */ -struct linkedList_s { /* config file sysline parse entry */ - int iNumElts; /* number of elements in list */ - rsRetVal (*pEltDestruct)(void*pData); /* destructor for user pointer in llElt_t's */ - rsRetVal (*pKeyDestruct)(void*pKey); /* destructor for key pointer in llElt_t's */ - int (*cmpOp)(void*, void*); /* pointer to key compare operation function, retval like strcmp */ - void *pKey; /* the list key (searchable, if set) */ - llElt_t *pRoot; /* list root */ - llElt_t *pLast; /* list tail */ -}; -typedef struct linkedList_s linkedList_t; - -typedef llElt_t* linkedListCookie_t; /* this type avoids exposing internals and keeps us flexible */ - -/* prototypes */ -rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pKeyDestructor)(void*), int (*pCmpOp)()); -rsRetVal llDestroy(linkedList_t *pThis); -rsRetVal llDestroyRootElt(linkedList_t *pThis); -rsRetVal llGetNextElt(linkedList_t *pThis, linkedListCookie_t *ppElt, void **ppUsr); -rsRetVal llAppend(linkedList_t *pThis, void *pKey, void *pData); -rsRetVal llFind(linkedList_t *pThis, void *pKey, void **ppData); -rsRetVal llGetKey(llElt_t *pThis, void *ppData); -rsRetVal llGetNumElts(linkedList_t *pThis, int *piCnt); -rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam); -rsRetVal llFindAndDelete(linkedList_t *pThis, void *pKey); -/* use the macro below to define a function that will be executed by - * llExecFunc() - */ -#define DEFFUNC_llExecFunc(funcName)\ - static rsRetVal funcName(void __attribute__((unused)) *pData, void __attribute__((unused)) *pParam) - -#endif /* #ifndef LINKEDLIST_H_INCLUDED */ diff --git a/module-template.h b/module-template.h deleted file mode 100644 index d5e142b4..00000000 --- a/module-template.h +++ /dev/null @@ -1,481 +0,0 @@ -/* module-template.h - * This header contains macros that can be used to implement the - * plumbing of modules. - * - * File begun on 2007-07-25 by RGerhards - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef MODULE_TEMPLATE_H_INCLUDED -#define MODULE_TEMPLATE_H_INCLUDED 1 - -#include "modules.h" -#include "obj.h" -#include "objomsr.h" -#include "threads.h" - -/* macro to define standard output-module static data members - */ -#define DEF_MOD_STATIC_DATA \ - static __attribute__((unused)) rsRetVal (*omsdRegCFSLineHdlr)(); - -#define DEF_OMOD_STATIC_DATA \ - DEF_MOD_STATIC_DATA \ - DEFobjCurrIf(obj) -#define DEF_IMOD_STATIC_DATA \ - DEF_MOD_STATIC_DATA \ - DEFobjCurrIf(obj) -#define DEF_LMOD_STATIC_DATA \ - DEF_MOD_STATIC_DATA - - -/* Macro to define the module type. Each module can only have a single type. If - * a module provides multiple types, several separate modules must be created which - * then should share a single library containing the majority of code. This macro - * must be present in each module. -- rgerhards, 2007-12-14 - */ -#define MODULE_TYPE(x)\ -static rsRetVal modGetType(eModType_t *modType) \ - { \ - *modType = x; \ - return RS_RET_OK;\ - } - -#define MODULE_TYPE_INPUT MODULE_TYPE(eMOD_IN) -#define MODULE_TYPE_OUTPUT MODULE_TYPE(eMOD_OUT) -#define MODULE_TYPE_LIB \ - DEF_LMOD_STATIC_DATA \ - MODULE_TYPE(eMOD_LIB) - -/* macro to define a unique module id. This must be able to fit in a void*. The - * module id must be unique inside a running rsyslogd application. It is used to - * track ownership of several objects. Most importantly, when the module is - * unloaded the module id value is used to find what needs to be destroyed. - * We currently use a pointer to modExit() as the module id. This sounds to be - * reasonable save, as each module must have this entry point AND there is no valid - * reason for twice this entry point being in memory. - * rgerhards, 2007-11-21 - */ -#define STD_LOADABLE_MODULE_ID ((void*) modExit) - - -/* macro to implement the "modGetID()" interface function - * rgerhards 2007-11-21 - */ -#define DEFmodGetID \ -static rsRetVal modGetID(void **pID) \ - { \ - *pID = STD_LOADABLE_MODULE_ID;\ - return RS_RET_OK;\ - } - -/* to following macros are used to generate function headers and standard - * functionality. It works as follows (described on the sample case of - * createInstance()): - * - * BEGINcreateInstance - * ... custom variable definitions (on stack) ... (if any) - * CODESTARTcreateInstance - * ... custom code ... (if any) - * ENDcreateInstance - */ - -/* createInstance() - */ -#define BEGINcreateInstance \ -static rsRetVal createInstance(instanceData **ppData)\ - {\ - DEFiRet; /* store error code here */\ - instanceData *pData; /* use this to point to data elements */ - -#define CODESTARTcreateInstance \ - if((pData = calloc(1, sizeof(instanceData))) == NULL) {\ - *ppData = NULL;\ - ENDfunc \ - return RS_RET_OUT_OF_MEMORY;\ - } - -#define ENDcreateInstance \ - *ppData = pData;\ - RETiRet;\ -} - -/* freeInstance() - * This is the cleanup function for the module instance. It is called immediately before - * the module instance is destroyed (unloaded). The module should do any cleanup - * here, e.g. close file, free instantance heap memory and the like. Control will - * not be passed back to the module once this function is finished. Keep in mind, - * however, that other instances may still be loaded and used. So do not destroy - * anything that may be used by another instance. If you have such a ressource, you - * currently need to do the instance counting yourself. - */ -#define BEGINfreeInstance \ -static rsRetVal freeInstance(void* pModData)\ -{\ - DEFiRet;\ - instanceData *pData; - -#define CODESTARTfreeInstance \ - pData = (instanceData*) pModData; - -#define ENDfreeInstance \ - if(pData != NULL)\ - free(pData); /* we need to free this in any case */\ - RETiRet;\ -} - -/* isCompatibleWithFeature() - */ -#define BEGINisCompatibleWithFeature \ -static rsRetVal isCompatibleWithFeature(syslogFeature __attribute__((unused)) eFeat)\ -{\ - rsRetVal iRet = RS_RET_INCOMPATIBLE; \ - BEGINfunc - -#define CODESTARTisCompatibleWithFeature - -#define ENDisCompatibleWithFeature \ - RETiRet;\ -} - -/* doAction() - */ -#define BEGINdoAction \ -static rsRetVal doAction(uchar __attribute__((unused)) **ppString, unsigned __attribute__((unused)) iMsgOpts, instanceData __attribute__((unused)) *pData)\ -{\ - DEFiRet; - -#define CODESTARTdoAction \ - /* ppString may be NULL if the output module requested no strings */ - -#define ENDdoAction \ - RETiRet;\ -} - - -/* dbgPrintInstInfo() - * Extra comments: - * Print debug information about this instance. - */ -#define BEGINdbgPrintInstInfo \ -static rsRetVal dbgPrintInstInfo(void *pModData)\ -{\ - DEFiRet;\ - instanceData *pData = NULL; - -#define CODESTARTdbgPrintInstInfo \ - pData = (instanceData*) pModData; - -#define ENDdbgPrintInstInfo \ - RETiRet;\ -} - - -/* parseSelectorAct() - * Extra comments: - * try to process a selector action line. Checks if the action - * applies to this module and, if so, processed it. If not, it - * is left untouched. The driver will then call another module. - * On exit, ppModData must point to instance data. Also, a string - * request object must be created and filled. A macro is defined - * for that. - * For the most usual case, we have defined a macro below. - * If more than one string is requested, the macro can be used together - * with own code that overwrites the entry count. In this case, the - * macro must come before the own code. It is recommended to be - * placed right after CODESTARTparseSelectorAct. - */ -#define BEGINparseSelectorAct \ -static rsRetVal parseSelectorAct(uchar **pp, void **ppModData, omodStringRequest_t **ppOMSR)\ -{\ - DEFiRet;\ - uchar *p;\ - instanceData *pData = NULL; - -#define CODESTARTparseSelectorAct \ - assert(pp != NULL);\ - assert(ppModData != NULL);\ - assert(ppOMSR != NULL);\ - p = *pp; - -#define CODE_STD_STRING_REQUESTparseSelectorAct(NumStrReqEntries) \ - CHKiRet(OMSRconstruct(ppOMSR, NumStrReqEntries)); - -#define CODE_STD_FINALIZERparseSelectorAct \ -finalize_it:\ - if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) {\ - *ppModData = pData;\ - *pp = p;\ - } else {\ - /* cleanup, we failed */\ - if(*ppOMSR != NULL) {\ - OMSRdestruct(*ppOMSR);\ - *ppOMSR = NULL;\ - }\ - if(pData != NULL) {\ - freeInstance(pData);\ - } \ - } - -#define ENDparseSelectorAct \ - RETiRet;\ -} - - -/* tryResume() - * This entry point is called to check if a module can resume operations. This - * happens when a module requested that it be suspended. In suspended state, - * the engine periodically tries to resume the module. If that succeeds, normal - * processing continues. If not, the module will not be called unless a - * tryResume() call succeeds. - * Returns RS_RET_OK, if resumption succeeded, RS_RET_SUSPENDED otherwise - * rgerhard, 2007-08-02 - */ -#define BEGINtryResume \ -static rsRetVal tryResume(instanceData __attribute__((unused)) *pData)\ -{\ - DEFiRet; - -#define CODESTARTtryResume \ - assert(pData != NULL); - -#define ENDtryResume \ - RETiRet;\ -} - - - -/* queryEtryPt() - */ -#define BEGINqueryEtryPt \ -DEFmodGetID \ -static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\ -{\ - DEFiRet; - -#define CODESTARTqueryEtryPt \ - if((name == NULL) || (pEtryPoint == NULL)) {\ - ENDfunc \ - return RS_RET_PARAM_ERROR;\ - } \ - *pEtryPoint = NULL; - -#define ENDqueryEtryPt \ - if(iRet == RS_RET_OK)\ - if(*pEtryPoint == NULL) { \ - dbgprintf("entry point '%s' not present in module\n", name); \ - iRet = RS_RET_MODULE_ENTRY_POINT_NOT_FOUND;\ - } \ - RETiRet;\ -} - -/* the following definition is the standard block for queryEtryPt for all types - * of modules. It should be included in any module, and typically is so by calling - * the module-type specific macros. - */ -#define CODEqueryEtryPt_STD_MOD_QUERIES \ - if(!strcmp((char*) name, "modExit")) {\ - *pEtryPoint = modExit;\ - } else if(!strcmp((char*) name, "modGetID")) {\ - *pEtryPoint = modGetID;\ - } else if(!strcmp((char*) name, "getType")) {\ - *pEtryPoint = modGetType;\ - } - -/* the following definition is the standard block for queryEtryPt for output - * modules. This can be used if no specific handling (e.g. to cover version - * differences) is needed. - */ -#define CODEqueryEtryPt_STD_OMOD_QUERIES \ - CODEqueryEtryPt_STD_MOD_QUERIES \ - else if(!strcmp((char*) name, "doAction")) {\ - *pEtryPoint = doAction;\ - } else if(!strcmp((char*) name, "dbgPrintInstInfo")) {\ - *pEtryPoint = dbgPrintInstInfo;\ - } else if(!strcmp((char*) name, "freeInstance")) {\ - *pEtryPoint = freeInstance;\ - } else if(!strcmp((char*) name, "parseSelectorAct")) {\ - *pEtryPoint = parseSelectorAct;\ - } else if(!strcmp((char*) name, "isCompatibleWithFeature")) {\ - *pEtryPoint = isCompatibleWithFeature;\ - } else if(!strcmp((char*) name, "tryResume")) {\ - *pEtryPoint = tryResume;\ - } - -/* the following definition is the standard block for queryEtryPt for INPUT - * modules. This can be used if no specific handling (e.g. to cover version - * differences) is needed. - */ -#define CODEqueryEtryPt_STD_IMOD_QUERIES \ - CODEqueryEtryPt_STD_MOD_QUERIES \ - else if(!strcmp((char*) name, "runInput")) {\ - *pEtryPoint = runInput;\ - } else if(!strcmp((char*) name, "willRun")) {\ - *pEtryPoint = willRun;\ - } else if(!strcmp((char*) name, "afterRun")) {\ - *pEtryPoint = afterRun;\ - } - -/* the following definition is the standard block for queryEtryPt for LIBRARY - * modules. This can be used if no specific handling (e.g. to cover version - * differences) is needed. - */ -#define CODEqueryEtryPt_STD_LIB_QUERIES \ - CODEqueryEtryPt_STD_MOD_QUERIES - -/* modInit() - * This has an extra parameter, which is the specific name of the modInit - * function. That is needed for built-in modules, which must have unique - * names in order to link statically. Please note that this is alwaysy only - * the case with modInit() and NO other entry point. The reason is that only - * modInit() is visible form a linker/loader point of view. All other entry - * points are passed via rsyslog-internal query functions and are defined - * static inside the modules source. This is an important concept, as it allows - * us to support different interface versions within a single module. (Granted, - * we do not currently have different interface versions, so we can not put - * it to a test - but our firm believe is that we can do all abstraction needed...) - * - * Extra Comments: - * initialize the module - * - * Later, much more must be done. So far, we only return a pointer - * to the queryEtryPt() function - * TODO: do interface version checking & handshaking - * iIfVersRequetsed is the version of the interface specification that the - * caller would like to see being used. ipIFVersProvided is what we - * decide to provide. - * rgerhards, 2007-11-21: see modExit() comment below for important information - * on the need to initialize static data with code. modInit() may be called on a - * cached, left-in-memory copy of a previous incarnation. - */ -#define BEGINmodInit(uniqName) \ -rsRetVal modInit##uniqName(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t __attribute__((unused)) *pModInfo)\ -{\ - DEFiRet; \ - rsRetVal (*pObjGetObjInterface)(obj_if_t *pIf); - -#define CODESTARTmodInit \ - assert(pHostQueryEtryPt != NULL);\ - iRet = pHostQueryEtryPt((uchar*)"objGetObjInterface", &pObjGetObjInterface); \ - if((iRet != RS_RET_OK) || (pQueryEtryPt == NULL) || (ipIFVersProvided == NULL) || (pObjGetObjInterface == NULL)) { \ - ENDfunc \ - return (iRet == RS_RET_OK) ? RS_RET_PARAM_ERROR : iRet; \ - } \ - /* now get the obj interface so that we can access other objects */ \ - CHKiRet(pObjGetObjInterface(&obj)); - -#define ENDmodInit \ -finalize_it:\ - *pQueryEtryPt = queryEtryPt;\ - RETiRet;\ -} - - -/* definitions for host API queries */ -#define CODEmodInit_QueryRegCFSLineHdlr \ - CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr)); - -#endif /* #ifndef MODULE_TEMPLATE_H_INCLUDED */ - -/* modExit() - * This is the counterpart to modInit(). It destroys a module and makes it ready for - * unloading. It is similiar to freeInstance() for the instance data. Please note that - * this entry point needs to free any module-globale data structures and registrations. - * For example, the CfSysLineHandlers a module has registered need to be unregistered - * here. This entry point is only called immediately before unloading of the module. So - * it is likely to be destroyed. HOWEVER, the caller may decide to keep the module cached. - * So a module must never assume that it is actually destroyed. A call to modInit() may - * happen immediately after modExit(). So a module can NOT assume that static data elements - * are being re-initialized by the loader - this must always be done by module code itself. - * It is suggested to do this in modInit(). - rgerhards, 2007-11-21 - */ -#define BEGINmodExit \ -static rsRetVal modExit(void)\ -{\ - DEFiRet; - -#define CODESTARTmodExit - -#define ENDmodExit \ - RETiRet;\ -} - - -/* runInput() - * This is the main function for input modules. It is used to gather data from the - * input source and submit it to the message queue. Each runInput() instance has its own - * thread. This is handled by the rsyslog engine. It needs to spawn off new threads only - * if there is a module-internal need to do so. - */ -#define BEGINrunInput \ -static rsRetVal runInput(thrdInfo_t __attribute__((unused)) *pThrd)\ -{\ - DEFiRet; - -#define CODESTARTrunInput \ - dbgSetThrdName((uchar*)__FILE__); /* we need to provide something better later */ - -#define ENDrunInput \ - RETiRet;\ -} - - -/* willRun() - * This is a function that will be replaced in the longer term. It is used so - * that a module can tell the caller if it will run or not. This is to be replaced - * when we introduce input module instances. However, these require config syntax - * changes and I may (or may not... ;)) hold that until another config file - * format is available. -- rgerhards, 2007-12-17 - * returns RS_RET_NO_RUN if it will not run (RS_RET_OK or error otherwise) - */ -#define BEGINwillRun \ -static rsRetVal willRun(void)\ -{\ - DEFiRet; - -#define CODESTARTwillRun - -#define ENDwillRun \ - RETiRet;\ -} - - -/* afterRun() - * This function is called after an input module has been run and its thread has - * been terminated. It shall do any necessary cleanup. - * This is expected to evolve into a freeInstance type of call once the input module - * interface evolves to support multiple instances. - * rgerhards, 2007-12-17 - */ -#define BEGINafterRun \ -static rsRetVal afterRun(void)\ -{\ - DEFiRet; - -#define CODESTARTafterRun - -#define ENDafterRun \ - RETiRet;\ -} - - -/* - * vi:set ai: - */ diff --git a/modules.c b/modules.c deleted file mode 100644 index 32a71c0c..00000000 --- a/modules.c +++ /dev/null @@ -1,802 +0,0 @@ -/* modules.c - * This is the implementation of syslogd modules object. - * This object handles plug-ins and build-in modules of all kind. - * - * Modules are reference-counted. Anyone who access a module must call - * Use() before any function is accessed and Release() when he is done. - * When the reference count reaches 0, rsyslog unloads the module (that - * may be changed in the future to cache modules). Rsyslog does NOT - * unload modules with a reference count > 0, even if the unload - * method is called! - * - * File begun on 2007-07-22 by RGerhards - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" -#include "rsyslog.h" -#include -#include -#include -#include -#include -#include -#include -#ifdef OS_BSD -# include "libgen.h" -#endif - -#include /* TODO: replace this with the libtools equivalent! */ - -#include -#include - -#include "syslogd.h" -#include "cfsysline.h" -#include "modules.h" -#include "errmsg.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(errmsg) - -static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */ -static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */ - -/* config settings */ -uchar *pModDir = NULL; /* read-only after startup */ - - -#ifdef DEBUG -/* we add some home-grown support to track our users (and detect who does not free us). In - * the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11 - */ - -/* add a user to the current list of users (always at the root) */ -static void -modUsrAdd(modInfo_t *pThis, char *pszUsr) -{ - modUsr_t *pUsr; - - BEGINfunc - if((pUsr = calloc(1, sizeof(modUsr_t))) == NULL) - goto finalize_it; - - if((pUsr->pszFile = strdup(pszUsr)) == NULL) { - free(pUsr); - goto finalize_it; - } - - if(pThis->pModUsrRoot != NULL) { - pUsr->pNext = pThis->pModUsrRoot; - } - pThis->pModUsrRoot = pUsr; - -finalize_it: - ENDfunc; -} - - -/* remove a user from the current user list - * rgerhards, 2008-03-11 - */ -static void -modUsrDel(modInfo_t *pThis, char *pszUsr) -{ - modUsr_t *pUsr; - modUsr_t *pPrev = NULL; - - for(pUsr = pThis->pModUsrRoot ; pUsr != NULL ; pUsr = pUsr->pNext) { - if(!strcmp(pUsr->pszFile, pszUsr)) - break; - else - pPrev = pUsr; - } - - if(pUsr == NULL) { - dbgprintf("oops - tried to delete user %s from module %s and it wasn't registered as one...\n", - pszUsr, pThis->pszName); - } else { - if(pPrev == NULL) { - /* This was at the root! */ - pThis->pModUsrRoot = pUsr->pNext; - } else { - pPrev->pNext = pUsr->pNext; - } - /* free ressources */ - free(pUsr->pszFile); - free(pUsr); - pUsr = NULL; /* just to make sure... */ - } -} - - -/* print a short list all all source files using the module in question - * rgerhards, 2008-03-11 - */ -static void -modUsrPrint(modInfo_t *pThis) -{ - modUsr_t *pUsr; - - for(pUsr = pThis->pModUsrRoot ; pUsr != NULL ; pUsr = pUsr->pNext) { - dbgprintf("\tmodule %s is currently in use by file %s\n", - pThis->pszName, pUsr->pszFile); - } -} - - -/* print all loaded modules and who is accessing them. This is primarily intended - * to be called at end of run to detect "module leaks" and who is causing them. - * rgerhards, 2008-03-11 - */ -//static void -void -modUsrPrintAll(void) -{ - modInfo_t *pMod; - - BEGINfunc - for(pMod = pLoadedModules ; pMod != NULL ; pMod = pMod->pNext) { - dbgprintf("printing users of loadable module %s, refcount %u, ptr %p, type %d\n", pMod->pszName, pMod->uRefCnt, pMod, pMod->eType); - modUsrPrint(pMod); - } - ENDfunc -} - -#endif /* #ifdef DEBUG */ - - -/* Construct a new module object - */ -static rsRetVal moduleConstruct(modInfo_t **pThis) -{ - modInfo_t *pNew; - - if((pNew = calloc(1, sizeof(modInfo_t))) == NULL) - return RS_RET_OUT_OF_MEMORY; - - /* OK, we got the element, now initialize members that should - * not be zero-filled. - */ - - *pThis = pNew; - return RS_RET_OK; -} - - -/* Destructs a module object. The object must not be linked to the - * linked list of modules. Please note that all other dependencies on this - * modules must have been removed before (e.g. CfSysLineHandlers!) - */ -static void moduleDestruct(modInfo_t *pThis) -{ - assert(pThis != NULL); - if(pThis->pszName != NULL) - free(pThis->pszName); - if(pThis->pModHdlr != NULL) { -# ifdef VALGRIND -# warning "dlclose disabled for valgrind" -# else - dlclose(pThis->pModHdlr); -# endif - } - - free(pThis); -} - - -/* The following function is the queryEntryPoint for host-based entry points. - * Modules may call it to get access to core interface functions. Please note - * that utility functions can be accessed via shared libraries - at least this - * is my current shool of thinking. - * Please note that the implementation as a query interface allows to take - * care of plug-in interface version differences. -- rgerhards, 2007-07-31 - */ -static rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) -{ - DEFiRet; - - if((name == NULL) || (pEtryPoint == NULL)) - return RS_RET_PARAM_ERROR; - - if(!strcmp((char*) name, "regCfSysLineHdlr")) { - *pEtryPoint = regCfSysLineHdlr; - } else if(!strcmp((char*) name, "objGetObjInterface")) { - *pEtryPoint = objGetObjInterface; - } else { - *pEtryPoint = NULL; /* to be on the safe side */ - ABORT_FINALIZE(RS_RET_ENTRY_POINT_NOT_FOUND); - } - -finalize_it: - RETiRet; -} - - -/* get the name of a module - */ -static uchar *modGetName(modInfo_t *pThis) -{ - return((pThis->pszName == NULL) ? (uchar*) "" : pThis->pszName); -} - - -/* get the state-name of a module. The state name is its name - * together with a short description of the module state (which - * is pulled from the module itself. - * rgerhards, 2007-07-24 - * TODO: the actual state name is not yet pulled - */ -static uchar *modGetStateName(modInfo_t *pThis) -{ - return(modGetName(pThis)); -} - - -/* Add a module to the loaded module linked list - */ -static inline void -addModToList(modInfo_t *pThis) -{ - assert(pThis != NULL); - - if(pLoadedModules == NULL) { - pLoadedModules = pLoadedModulesLast = pThis; - } else { - /* there already exist entries */ - pThis->pPrev = pLoadedModulesLast; - pLoadedModulesLast->pNext = pThis; - pLoadedModulesLast = pThis; - } -} - - -/* Get the next module pointer - this is used to traverse the list. - * The function returns the next pointer or NULL, if there is no next one. - * The last object must be provided to the function. If NULL is provided, - * it starts at the root of the list. Even in this case, NULL may be - * returned - then, the list is empty. - * rgerhards, 2007-07-23 - */ -static modInfo_t *GetNxt(modInfo_t *pThis) -{ - modInfo_t *pNew; - - if(pThis == NULL) - pNew = pLoadedModules; - else - pNew = pThis->pNext; - - return(pNew); -} - - -/* this function is like GetNxt(), but it returns pointers to - * modules of specific type only. As we currently deal just with output modules, - * it is a dummy, to be filled with real code later. - * rgerhards, 2007-07-24 - */ -static modInfo_t *GetNxtType(modInfo_t *pThis, eModType_t rqtdType) -{ - modInfo_t *pMod = pThis; - - do { - pMod = GetNxt(pMod); - } while(!(pMod == NULL || pMod->eType == rqtdType)); /* warning: do ... while() */ - - return pMod; -} - - -/* Prepare a module for unloading. - * This is currently a dummy, to be filled when we have a plug-in - * interface - rgerhards, 2007-08-09 - * rgerhards, 2007-11-21: - * When this function is called, all instance-data must already have - * been destroyed. In the case of output modules, this happens when the - * rule set is being destroyed. When we implement other module types, we - * need to think how we handle it there (and if we have any instance data). - * rgerhards, 2008-03-10: reject unload request if the module has a reference - * count > 0. - */ -static rsRetVal -modPrepareUnload(modInfo_t *pThis) -{ - DEFiRet; - void *pModCookie; - - assert(pThis != NULL); - - if(pThis->uRefCnt > 0) { - dbgprintf("rejecting unload of module '%s' because it has a refcount of %d\n", - pThis->pszName, pThis->uRefCnt); - ABORT_FINALIZE(RS_RET_MODULE_STILL_REFERENCED); - } - - CHKiRet(pThis->modGetID(&pModCookie)); - pThis->modExit(); /* tell the module to get ready for unload */ - CHKiRet(unregCfSysLineHdlrs4Owner(pModCookie)); - -finalize_it: - RETiRet; -} - - -/* Add an already-loaded module to the module linked list. This function does - * everything needed to fully initialize the module. - */ -static rsRetVal -doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_t*), uchar *name, void *pModHdlr) -{ - DEFiRet; - modInfo_t *pNew = NULL; - rsRetVal (*modGetType)(eModType_t *pType); - - assert(modInit != NULL); - - if((iRet = moduleConstruct(&pNew)) != RS_RET_OK) { - pNew = NULL; - ABORT_FINALIZE(iRet); - } - - CHKiRet((*modInit)(CURR_MOD_IF_VERSION, &pNew->iIFVers, &pNew->modQueryEtryPt, queryHostEtryPt, pNew)); - - if(pNew->iIFVers != CURR_MOD_IF_VERSION) { - ABORT_FINALIZE(RS_RET_MISSING_INTERFACE); - } - - /* We now poll the module to see what type it is. We do this only once as this - * can never change in the lifetime of an module. -- rgerhards, 2007-12-14 - */ - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getType", &modGetType)); - CHKiRet((iRet = (*modGetType)(&pNew->eType)) != RS_RET_OK); - dbgprintf("module of type %d being loaded.\n", pNew->eType); - - /* OK, we know we can successfully work with the module. So we now fill the - * rest of the data elements. First we load the interfaces common to all - * module types. - */ - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modGetID", &pNew->modGetID)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modExit", &pNew->modExit)); - - /* ... and now the module-specific interfaces */ - switch(pNew->eType) { - case eMOD_IN: - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"runInput", &pNew->mod.im.runInput)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"willRun", &pNew->mod.im.willRun)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"afterRun", &pNew->mod.im.afterRun)); - break; - case eMOD_OUT: - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"dbgPrintInstInfo", &pNew->dbgPrintInstInfo)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"doAction", &pNew->mod.om.doAction)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"parseSelectorAct", &pNew->mod.om.parseSelectorAct)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature", &pNew->isCompatibleWithFeature)); - CHKiRet((*pNew->modQueryEtryPt)((uchar*)"tryResume", &pNew->tryResume)); - break; - case eMOD_LIB: - break; - } - - pNew->pszName = (uchar*) strdup((char*)name); /* we do not care if strdup() fails, we can accept that */ - pNew->pModHdlr = pModHdlr; - /* TODO: take this from module */ - if(pModHdlr == NULL) - pNew->eLinkType = eMOD_LINK_STATIC; - else - pNew->eLinkType = eMOD_LINK_DYNAMIC_LOADED; - - /* we initialized the structure, now let's add it to the linked list of modules */ - addModToList(pNew); - -finalize_it: - if(iRet != RS_RET_OK) { - if(pNew != NULL) - moduleDestruct(pNew); - } - - RETiRet; -} - -/* Print loaded modules. This is more or less a - * debug or test aid, but anyhow I think it's worth it... - * This only works if the dbgprintf() subsystem is initialized. - * TODO: update for new input modules! - */ -static void modPrintList(void) -{ - modInfo_t *pMod; - - pMod = GetNxt(NULL); - while(pMod != NULL) { - dbgprintf("Loaded Module: Name='%s', IFVersion=%d, ", - (char*) modGetName(pMod), pMod->iIFVers); - dbgprintf("type="); - switch(pMod->eType) { - case eMOD_OUT: - dbgprintf("output"); - break; - case eMOD_IN: - dbgprintf("input"); - break; - case eMOD_LIB: - dbgprintf("library"); - break; - } - dbgprintf(" module.\n"); - dbgprintf("Entry points:\n"); - dbgprintf("\tqueryEtryPt: 0x%lx\n", (unsigned long) pMod->modQueryEtryPt); - dbgprintf("\tdoAction: 0x%lx\n", (unsigned long) pMod->mod.om.doAction); - dbgprintf("\tparseSelectorAct: 0x%lx\n", (unsigned long) pMod->mod.om.parseSelectorAct); - dbgprintf("\tdbgPrintInstInfo: 0x%lx\n", (unsigned long) pMod->dbgPrintInstInfo); - dbgprintf("\tfreeInstance: 0x%lx\n", (unsigned long) pMod->freeInstance); - dbgprintf("\n"); - pMod = GetNxt(pMod); /* done, go next */ - } -} - - -/* unlink and destroy a module. The caller must provide a pointer to the module - * itself as well as one to its immediate predecessor. - * rgerhards, 2008-02-26 - */ -static rsRetVal -modUnlinkAndDestroy(modInfo_t **ppThis) -{ - DEFiRet; - modInfo_t *pThis; - - assert(ppThis != NULL); - pThis = *ppThis; - assert(pThis != NULL); - - /* first check if we are permitted to unload */ - if(pThis->eType == eMOD_LIB) { - if(pThis->uRefCnt > 0) { - dbgprintf("module %s NOT unloaded because it still has a refcount of %u\n", - pThis->pszName, pThis->uRefCnt); -# ifdef DEBUG - //modUsrPrintAll(); -# endif - ABORT_FINALIZE(RS_RET_MODULE_STILL_REFERENCED); - } - } - - /* we need to unlink the module before we can destruct it -- rgerhards, 2008-02-26 */ - if(pThis->pPrev == NULL) { - /* module is root, so we need to set a new root */ - pLoadedModules = pThis->pNext; - } else { - pThis->pPrev->pNext = pThis->pNext; - } - - if(pThis->pNext == NULL) { - pLoadedModulesLast = pThis->pPrev; - } else { - pThis->pNext->pPrev = pThis->pPrev; - } - - /* finally, we are ready for the module to go away... */ - dbgprintf("Unloading module %s\n", modGetName(pThis)); - CHKiRet(modPrepareUnload(pThis)); - *ppThis = pThis->pNext; - - moduleDestruct(pThis); - -finalize_it: - RETiRet; -} - - -/* unload all loaded modules of a specific type (use eMOD_ALL if you want to - * unload all module types). The unload happens only if the module is no longer - * referenced. So some modules may survive this call. - * rgerhards, 2008-03-11 - */ -static rsRetVal -modUnloadAndDestructAll(eModLinkType_t modLinkTypesToUnload) -{ - DEFiRet; - modInfo_t *pModCurr; /* module currently being processed */ - - pModCurr = GetNxt(NULL); - while(pModCurr != NULL) { - if(modLinkTypesToUnload == eMOD_LINK_ALL || pModCurr->eLinkType == modLinkTypesToUnload) { - if(modUnlinkAndDestroy(&pModCurr) == RS_RET_MODULE_STILL_REFERENCED) { - pModCurr = GetNxt(pModCurr); - } - /* Note: if the module was successfully unloaded, it has updated the - * pModCurr pointer to the next module. So we do NOT need to advance - * to the next module on successful unload. - */ - } else { - pModCurr = GetNxt(pModCurr); - } - } - -# ifdef DEBUG - if(pLoadedModules != NULL) { - dbgprintf("modules still loaded after module.UnloadAndDestructAll:\n"); - modUsrPrintAll(); - } -# endif - - RETiRet; -} - - -/* load a module and initialize it, based on doModLoad() from conf.c - * rgerhards, 2008-03-05 - * varmojfekoj added support for dynamically loadable modules on 2007-08-13 - * rgerhards, 2007-09-25: please note that the non-threadsafe function dlerror() is - * called below. This is ok because modules are currently only loaded during - * configuration file processing, which is executed on a single thread. Should we - * change that design at any stage (what is unlikely), we need to find a - * replacement. - */ -static rsRetVal -Load(uchar *pModName) -{ - DEFiRet; - - size_t iPathLen, iModNameLen; - uchar szPath[PATH_MAX]; - uchar *pModNameCmp; - int bHasExtension; - void *pModHdlr, *pModInit; - modInfo_t *pModInfo; - - assert(pModName != NULL); - dbgprintf("Requested to load module '%s'\n", pModName); - - iModNameLen = strlen((char *) pModName); - if(iModNameLen > 3 && !strcmp((char *) pModName + iModNameLen - 3, ".so")) { - iModNameLen -= 3; - bHasExtension = TRUE; - } else - bHasExtension = FALSE; - - pModInfo = GetNxt(NULL); - while(pModInfo != NULL) { - if(!strncmp((char *) pModName, (char *) (pModNameCmp = modGetName(pModInfo)), iModNameLen) && - (!*(pModNameCmp + iModNameLen) || !strcmp((char *) pModNameCmp + iModNameLen, ".so"))) { - dbgprintf("Module '%s' already loaded\n", pModName); - ABORT_FINALIZE(RS_RET_OK); - } - pModInfo = GetNxt(pModInfo); - } - - /* now build our load module name */ - if(*pModName == '/') { - *szPath = '\0'; /* we do not need to append the path - its already in the module name */ - iPathLen = 0; - } else { - *szPath = '\0'; - strncat((char *) szPath, (pModDir == NULL) ? _PATH_MODDIR : (char*) pModDir, sizeof(szPath) - 1); - iPathLen = strlen((char*) szPath); - if((szPath[iPathLen - 1] != '/')) { - if((iPathLen <= sizeof(szPath) - 2)) { - szPath[iPathLen++] = '/'; - szPath[iPathLen] = '\0'; - } else { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName); - ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); - } - } - } - - /* ... add actual name ... */ - strncat((char *) szPath, (char *) pModName, sizeof(szPath) - iPathLen - 1); - - /* now see if we have an extension and, if not, append ".so" */ - if(!bHasExtension) { - /* we do not have an extension and so need to add ".so" - * TODO: I guess this is highly importable, so we should change the - * algo over time... -- rgerhards, 2008-03-05 - */ - /* ... so now add the extension */ - strncat((char *) szPath, ".so", sizeof(szPath) - strlen((char*) szPath) - 1); - iPathLen += 3; - } - - if(iPathLen + strlen((char*) pModName) >= sizeof(szPath)) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName); - ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); - } - - /* complete load path constructed, so ... GO! */ - dbgprintf("loading module '%s'\n", szPath); - if(!(pModHdlr = dlopen((char *) szPath, RTLD_NOW))) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlopen: %s\n", szPath, dlerror()); - ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_DLOPEN); - } - if(!(pModInit = dlsym(pModHdlr, "modInit"))) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlsym: %s\n", szPath, dlerror()); - dlclose(pModHdlr); - ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_NO_INIT); - } - if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr)) != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "could not load module '%s', rsyslog error %d\n", szPath, iRet); - dlclose(pModHdlr); - ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_INIT_FAILED); - } - -finalize_it: - RETiRet; -} - - -/* set the default module load directory. A NULL value may be provided, in - * which case any previous value is deleted but no new one set. The caller-provided - * string is duplicated. If it needs to be freed, that's the caller's duty. - * rgerhards, 2008-03-07 - */ -static rsRetVal -SetModDir(uchar *pszModDir) -{ - DEFiRet; - - dbgprintf("setting default module load directory '%s'\n", pszModDir); - if(pModDir != NULL) { - free(pModDir); - } - - pModDir = (uchar*) strdup((char*)pszModDir); - - RETiRet; -} - - -/* Reference-Counting object access: add 1 to the current reference count. Must be - * called by anyone interested in using a module. -- rgerhards, 20080-03-10 - */ -static rsRetVal -Use(char *srcFile, modInfo_t *pThis) -{ - DEFiRet; - - assert(pThis != NULL); - pThis->uRefCnt++; - dbgprintf("source file %s requested reference for module '%s', reference count now %u\n", - srcFile, pThis->pszName, pThis->uRefCnt); - -# ifdef DEBUG - modUsrAdd(pThis, srcFile); -# endif - - RETiRet; - -} - - -/* Reference-Counting object access: subract one from the current refcount. Must - * by called by anyone who no longer needs a module. If count reaches 0, the - * module is unloaded. -- rgerhards, 20080-03-10 - */ -static rsRetVal -Release(char *srcFile, modInfo_t **ppThis) -{ - DEFiRet; - modInfo_t *pThis; - - assert(ppThis != NULL); - pThis = *ppThis; - assert(pThis != NULL); - if(pThis->uRefCnt == 0) { - /* oops, we are already at 0? */ - dbgprintf("internal error: module '%s' already has a refcount of 0 (released by %s)!\n", - pThis->pszName, srcFile); - } else { - --pThis->uRefCnt; - dbgprintf("file %s released module '%s', reference count now %u\n", - srcFile, pThis->pszName, pThis->uRefCnt); -# ifdef DEBUG - modUsrDel(pThis, srcFile); - modUsrPrint(pThis); -# endif - } - - if(pThis->uRefCnt == 0) { - /* we have a zero refcount, so we must unload the module */ - dbgprintf("module '%s' has zero reference count, unloading...\n", pThis->pszName); - modUnlinkAndDestroy(&pThis); - /* we must NOT do a *ppThis = NULL, because ppThis now points into freed memory! - * If in doubt, see obj.c::ReleaseObj() for how we are called. - */ - } - - RETiRet; - -} - - -/* exit our class - * rgerhards, 2008-03-11 - */ -BEGINObjClassExit(module, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ -CODESTARTObjClassExit(module) - /* release objects we no longer need */ - objRelease(errmsg, CORE_COMPONENT); - -# ifdef DEBUG - modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */ -# endif -ENDObjClassExit(module) - - -/* queryInterface function - * rgerhards, 2008-03-05 - */ -BEGINobjQueryInterface(module) -CODESTARTobjQueryInterface(module) - if(pIf->ifVersion != moduleCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->GetNxt = GetNxt; - pIf->GetNxtType = GetNxtType; - pIf->GetName = modGetName; - pIf->GetStateName = modGetStateName; - pIf->PrintList = modPrintList; - pIf->UnloadAndDestructAll = modUnloadAndDestructAll; - pIf->doModInit = doModInit; - pIf->SetModDir = SetModDir; - pIf->Load = Load; - pIf->Use = Use; - pIf->Release = Release; -finalize_it: -ENDobjQueryInterface(module) - - -/* Initialize our class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-03-05 - */ -BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */ - uchar *pModPath; - - /* use any module load path specified in the environment */ - if((pModPath = (uchar*) getenv("RSYSLOG_MODDIR")) != NULL) { - SetModDir(pModPath); - } - - /* now check if another module path was set via the command line (-M) - * if so, that overrides the environment. Please note that we must use - * a global setting here because the command line parser can NOT call - * into the module object, because it is not initialized at that point. So - * instead a global setting is changed and we pick it up as soon as we - * initialize -- rgerhards, 2008-04-04 - */ - if(glblModPath != NULL) { - SetModDir(glblModPath); - } - - /* request objects we use */ - CHKiRet(objUse(errmsg, CORE_COMPONENT)); -ENDObjClassInit(module) - -/* vi:set ai: - */ diff --git a/modules.h b/modules.h deleted file mode 100644 index a8371d05..00000000 --- a/modules.h +++ /dev/null @@ -1,150 +0,0 @@ -/* modules.h - * - * Definition for build-in and plug-ins module handler. This file is the base - * for all dynamically loadable module support. In theory, in v3 all modules - * are dynamically loaded, in practice we currently do have a few build-in - * once. This may become removed. - * - * The loader keeps track of what is loaded. For library modules, it is also - * used to find objects (libraries) and to obtain the queryInterface function - * for them. A reference count is maintened for libraries, so that they are - * unloaded only when nobody still accesses them. - * - * File begun on 2007-07-22 by RGerhards - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef MODULES_H_INCLUDED -#define MODULES_H_INCLUDED 1 - -#include "objomsr.h" -#include "threads.h" - - -/* the following define defines the current version of the module interface. - * It can be used by any module which want's to simply prevent version conflicts - * and does not intend to do specific old-version emulations. - * rgerhards, 2008-03-04 - * version 3 adds modInfo_t ptr to call of modInit -- rgerhards, 2008-03-10 - * version 4 removes needUDPSocket OM callback -- rgerhards, 2008-03-22 - */ -#define CURR_MOD_IF_VERSION 4 - -typedef enum eModType_ { - eMOD_IN, /* input module */ - eMOD_OUT, /* output module */ - eMOD_LIB /* library module - this module provides one or many interfaces */ -} eModType_t; - - -#ifdef DEBUG -typedef struct modUsr_s { - struct modUsr_s *pNext; - char *pszFile; -} modUsr_t; -#endif - - -/* how is this module linked? */ -typedef enum eModLinkType_ { - eMOD_LINK_STATIC, - eMOD_LINK_DYNAMIC_UNLOADED, /* dynalink module, currently not loaded */ - eMOD_LINK_DYNAMIC_LOADED, /* dynalink module, currently loaded */ - eMOD_LINK_ALL /* special: all linkage types, e.g. for unload */ -} eModLinkType_t; - -typedef struct modInfo_s { - struct modInfo_s *pPrev; /* support for creating a double linked module list */ - struct modInfo_s *pNext; /* support for creating a linked module list */ - int iIFVers; /* Interface version of module */ - eModType_t eType; /* type of this module */ - eModLinkType_t eLinkType; - uchar* pszName; /* printable module name, e.g. for dbgprintf */ - unsigned uRefCnt; /* reference count for this module; 0 -> may be unloaded */ - /* functions supported by all types of modules */ - rsRetVal (*modInit)(int, int*, rsRetVal(**)()); /* initialize the module */ - /* be sure to support version handshake! */ - rsRetVal (*modQueryEtryPt)(uchar *name, rsRetVal (**EtryPoint)()); /* query entry point addresses */ - rsRetVal (*isCompatibleWithFeature)(syslogFeature); - rsRetVal (*freeInstance)(void*);/* called before termination or module unload */ - rsRetVal (*dbgPrintInstInfo)(void*);/* called before termination or module unload */ - rsRetVal (*tryResume)(void*);/* called to see if module actin can be resumed now */ - rsRetVal (*modExit)(void); /* called before termination or module unload */ - rsRetVal (*modGetID)(void **); /* get its unique ID from module */ - /* below: parse a configuration line - return if processed - * or not. If not, must be parsed to next module. - */ - rsRetVal (*parseConfigLine)(uchar **pConfLine); - /* below: create an instance of this module. Most importantly the module - * can allocate instance memory in this call. - */ - rsRetVal (*createInstance)(); - /* TODO: pass pointer to msg submit function to IM rger, 2007-12-14 */ - union { - struct {/* data for input modules */ - rsRetVal (*runInput)(thrdInfo_t*); /* function to gather input and submit to queue */ - rsRetVal (*willRun)(void); /* function to gather input and submit to queue */ - rsRetVal (*afterRun)(thrdInfo_t*); /* function to gather input and submit to queue */ - } im; - struct {/* data for output modules */ - /* below: perform the configured action - */ - rsRetVal (*doAction)(uchar**, unsigned, void*); - rsRetVal (*parseSelectorAct)(uchar**, void**,omodStringRequest_t**); - } om; - struct { /* data for library modules */ - } fm; - } mod; - void *pModHdlr; /* handler to the dynamic library holding the module */ -# ifdef DEBUG - /* we add some home-grown support to track our users (and detect who does not free us). In - * the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11 - */ - modUsr_t *pModUsrRoot; -# endif -} modInfo_t; - -/* interfaces */ -BEGINinterface(module) /* name must also be changed in ENDinterface macro! */ - modInfo_t *(*GetNxt)(modInfo_t *pThis); - modInfo_t *(*GetNxtType)(modInfo_t *pThis, eModType_t rqtdType); - uchar *(*GetName)(modInfo_t *pThis); - uchar *(*GetStateName)(modInfo_t *pThis); - rsRetVal (*Use)(char *srcFile, modInfo_t *pThis); /**< must be called before a module is used (ref counting) */ - rsRetVal (*Release)(char *srcFile, modInfo_t **ppThis); /**< release a module (ref counting) */ - void (*PrintList)(void); - rsRetVal (*UnloadAndDestructAll)(eModLinkType_t modLinkTypesToUnload); - rsRetVal (*doModInit)(rsRetVal (*modInit)(), uchar *name, void *pModHdlr); - rsRetVal (*Load)(uchar *name); - rsRetVal (*SetModDir)(uchar *name); -ENDinterface(module) -#define moduleCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ - -/* prototypes */ -PROTOTYPEObj(module); - -/* TODO: remove them below (means move the config init code) -- rgerhards, 2008-02-19 */ -extern uchar *pModDir; /* read-only after startup */ - - -#endif /* #ifndef MODULES_H_INCLUDED */ -/* - * vi:set ai: - */ diff --git a/msg.c b/msg.c deleted file mode 100644 index 9a12d572..00000000 --- a/msg.c +++ /dev/null @@ -1,2293 +0,0 @@ -/* msg.c - * The msg object. Implementation of all msg-related functions - * - * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) - * This file is under development and has not yet arrived at being fully - * self-contained and a real object. So far, it is mostly an excerpt - * of the "old" message code without any modifications. However, it - * helps to have things at the right place one we go to the meat of it. - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" -#include -#include -#include -#define SYSLOG_NAMES -#include -#include -#include -#include "rsyslog.h" -#include "syslogd.h" -#include "srUtils.h" -#include "stringbuf.h" -#include "template.h" -#include "msg.h" -#include "var.h" -#include "datetime.h" -#include "regexp.h" -#include "atomic.h" - -/* static data */ -DEFobjStaticHelpers -DEFobjCurrIf(var) -DEFobjCurrIf(datetime) -DEFobjCurrIf(regexp) - -static syslogCODE rs_prioritynames[] = - { - { "alert", LOG_ALERT }, - { "crit", LOG_CRIT }, - { "debug", LOG_DEBUG }, - { "emerg", LOG_EMERG }, - { "err", LOG_ERR }, - { "error", LOG_ERR }, /* DEPRECATED */ - { "info", LOG_INFO }, - { "none", INTERNAL_NOPRI }, /* INTERNAL */ - { "notice", LOG_NOTICE }, - { "panic", LOG_EMERG }, /* DEPRECATED */ - { "warn", LOG_WARNING }, /* DEPRECATED */ - { "warning", LOG_WARNING }, - { NULL, -1 } - }; - -#ifndef LOG_AUTHPRIV -# define LOG_AUTHPRIV LOG_AUTH -#endif -static syslogCODE rs_facilitynames[] = - { - { "auth", LOG_AUTH }, - { "authpriv", LOG_AUTHPRIV }, - { "cron", LOG_CRON }, - { "daemon", LOG_DAEMON }, -#if defined(LOG_FTP) - {"ftp", LOG_FTP}, -#endif - { "kern", LOG_KERN }, - { "lpr", LOG_LPR }, - { "mail", LOG_MAIL }, - //{ "mark", INTERNAL_MARK }, /* INTERNAL */ - { "news", LOG_NEWS }, - { "security", LOG_AUTH }, /* DEPRECATED */ - { "syslog", LOG_SYSLOG }, - { "user", LOG_USER }, - { "uucp", LOG_UUCP }, - { "local0", LOG_LOCAL0 }, - { "local1", LOG_LOCAL1 }, - { "local2", LOG_LOCAL2 }, - { "local3", LOG_LOCAL3 }, - { "local4", LOG_LOCAL4 }, - { "local5", LOG_LOCAL5 }, - { "local6", LOG_LOCAL6 }, - { "local7", LOG_LOCAL7 }, - { NULL, -1 } - }; - -/* some forward declarations */ -static int getAPPNAMELen(msg_t *pM); - -/* The following functions will support advanced output module - * multithreading, once this is implemented. Currently, we - * include them as hooks only. The idea is that we need to guard - * some msg objects data fields against concurrent access if - * we run on multiple threads. Please note that in any case this - * is not necessary for calls from INPUT modules, because they - * construct the message object and do this serially. Only when - * the message is in the processing queue, multiple threads may - * access a single object. Consequently, there are no guard functions - * for "set" methods, as these are called during input. Only "get" - * functions that modify important structures have them. - * rgerhards, 2007-07-20 - * We now support locked and non-locked operations, depending on - * the configuration of rsyslog. To support this, we use function - * pointers. Initially, we start in non-locked mode. There, all - * locking operations call into dummy functions. When locking is - * enabled, the function pointers are changed to functions doing - * actual work. We also introduced another MsgPrepareEnqueue() function - * which initializes the locking structures, if needed. This is - * necessary because internal messages during config file startup - * processing are always created in non-locking mode. So we can - * not initialize locking structures during constructions. We now - * postpone this until when the message is fully constructed and - * enqueued. Then we know the status of locking. This has a nice - * side effect, and that is that during the initial creation of - * the Msg object no locking needs to be done, which results in better - * performance. -- rgerhards, 2008-01-05 - */ -static void (*funcLock)(msg_t *pMsg); -static void (*funcUnlock)(msg_t *pMsg); -static void (*funcDeleteMutex)(msg_t *pMsg); -void (*funcMsgPrepareEnqueue)(msg_t *pMsg); -#if 1 /* This is a debug aid */ -#define MsgLock(pMsg) funcLock(pMsg) -#define MsgUnlock(pMsg) funcUnlock(pMsg) -#else -#define MsgLock(pMsg) {dbgprintf("line %d\n - ", __LINE__); funcLock(pMsg);; } -#define MsgUnlock(pMsg) {dbgprintf("line %d - ", __LINE__); funcUnlock(pMsg); } -#endif - -/* the next function is a dummy to be used by the looking functions - * when the class is not yet running in an environment where locking - * is necessary. Please note that the need to lock can (and will) change - * during a single run. Typically, this is depending on the operation mode - * of the message queues (which is operator-configurable). -- rgerhards, 2008-01-05 - */ -static void MsgLockingDummy(msg_t __attribute__((unused)) *pMsg) -{ - /* empty be design */ -} - - -/* The following function prepares a message for enqueue into the queue. This is - * where a message may be accessed by multiple threads. This implementation here - * is the version for multiple concurrent acces. It initializes the locking - * structures. - */ -static void MsgPrepareEnqueueLockingCase(msg_t *pThis) -{ - assert(pThis != NULL); - pthread_mutexattr_settype(&pThis->mutAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&pThis->mut, &pThis->mutAttr); -} - -/* ... and now the locking and unlocking implementations: */ -static void MsgLockLockingCase(msg_t *pThis) -{ - /* DEV debug only! dbgprintf("MsgLock(0x%lx)\n", (unsigned long) pThis); */ - assert(pThis != NULL); - pthread_mutex_lock(&pThis->mut); -} - -static void MsgUnlockLockingCase(msg_t *pThis) -{ - /* DEV debug only! dbgprintf("MsgUnlock(0x%lx)\n", (unsigned long) pThis); */ - assert(pThis != NULL); - pthread_mutex_unlock(&pThis->mut); -} - -/* delete the mutex object on message destruction (locking case) - */ -static void MsgDeleteMutexLockingCase(msg_t *pThis) -{ - assert(pThis != NULL); - pthread_mutex_destroy(&pThis->mut); -} - -/* enable multiple concurrent access on the message object - * This works on a class-wide basis and can bot be undone. - * That is, if it is once enabled, it can not be disabled during - * the same run. When this function is called, no other thread - * must manipulate message objects. Then we would have race conditions, - * but guarding against this is counter-productive because it - * would cost additional time. Plus, it would be a programming error. - * rgerhards, 2008-01-05 - */ -rsRetVal MsgEnableThreadSafety(void) -{ - funcLock = MsgLockLockingCase; - funcUnlock = MsgUnlockLockingCase; - funcMsgPrepareEnqueue = MsgPrepareEnqueueLockingCase; - funcDeleteMutex = MsgDeleteMutexLockingCase; - return RS_RET_OK; -} - -/* end locking functions */ - - -/* "Constructor" for a msg "object". Returns a pointer to - * the new object or NULL if no such object could be allocated. - * An object constructed via this function should only be destroyed - * via "msgDestruct()". - */ -rsRetVal msgConstruct(msg_t **ppThis) -{ - DEFiRet; - msg_t *pM; - - assert(ppThis != NULL); - if((pM = calloc(1, sizeof(msg_t))) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - - /* initialize members that are non-zero */ - pM->iRefCount = 1; - pM->iSeverity = -1; - pM->iFacility = -1; - datetime.getCurrTime(&(pM->tRcvdAt)); - objConstructSetObjInfo(pM); - - /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/ - - *ppThis = pM; - -finalize_it: - RETiRet; -} - - -BEGINobjDestruct(msg) /* be sure to specify the object type also in END and CODESTART macros! */ - int currRefCount; -CODESTARTobjDestruct(msg) - /* DEV Debugging only ! dbgprintf("msgDestruct\t0x%lx, Ref now: %d\n", (unsigned long)pM, pM->iRefCount - 1); */ -# ifdef DO_HAVE_ATOMICS - currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount); -# else - currRefCount = --pThis->iRefCount; -# endif - if(currRefCount == 0) - { - /* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */ - if(pThis->pszUxTradMsg != NULL) - free(pThis->pszUxTradMsg); - if(pThis->pszRawMsg != NULL) - free(pThis->pszRawMsg); - if(pThis->pszTAG != NULL) - free(pThis->pszTAG); - if(pThis->pszHOSTNAME != NULL) - free(pThis->pszHOSTNAME); - if(pThis->pszRcvFrom != NULL) - free(pThis->pszRcvFrom); - if(pThis->pszMSG != NULL) - free(pThis->pszMSG); - if(pThis->pszFacility != NULL) - free(pThis->pszFacility); - if(pThis->pszFacilityStr != NULL) - free(pThis->pszFacilityStr); - if(pThis->pszSeverity != NULL) - free(pThis->pszSeverity); - if(pThis->pszSeverityStr != NULL) - free(pThis->pszSeverityStr); - if(pThis->pszRcvdAt3164 != NULL) - free(pThis->pszRcvdAt3164); - if(pThis->pszRcvdAt3339 != NULL) - free(pThis->pszRcvdAt3339); - if(pThis->pszRcvdAt_MySQL != NULL) - free(pThis->pszRcvdAt_MySQL); - if(pThis->pszRcvdAt_PgSQL != NULL) - free(pThis->pszRcvdAt_PgSQL); - if(pThis->pszTIMESTAMP3164 != NULL) - free(pThis->pszTIMESTAMP3164); - if(pThis->pszTIMESTAMP3339 != NULL) - free(pThis->pszTIMESTAMP3339); - if(pThis->pszTIMESTAMP_MySQL != NULL) - free(pThis->pszTIMESTAMP_MySQL); - if(pThis->pszTIMESTAMP_PgSQL != NULL) - free(pThis->pszTIMESTAMP_PgSQL); - if(pThis->pszPRI != NULL) - free(pThis->pszPRI); - if(pThis->pCSProgName != NULL) - rsCStrDestruct(&pThis->pCSProgName); - if(pThis->pCSStrucData != NULL) - rsCStrDestruct(&pThis->pCSStrucData); - if(pThis->pCSAPPNAME != NULL) - rsCStrDestruct(&pThis->pCSAPPNAME); - if(pThis->pCSPROCID != NULL) - rsCStrDestruct(&pThis->pCSPROCID); - if(pThis->pCSMSGID != NULL) - rsCStrDestruct(&pThis->pCSMSGID); - funcDeleteMutex(pThis); - } else { - pThis = NULL; /* tell framework not to destructing the object! */ - } -ENDobjDestruct(msg) - - -/* The macros below are used in MsgDup(). I use macros - * to keep the fuction code somewhat more readyble. It is my - * replacement for inline functions in CPP - */ -#define tmpCOPYSZ(name) \ - if(pOld->psz##name != NULL) { \ - if((pNew->psz##name = srUtilStrDup(pOld->psz##name, pOld->iLen##name)) == NULL) {\ - msgDestruct(&pNew);\ - return NULL;\ - }\ - pNew->iLen##name = pOld->iLen##name;\ - } - -/* copy the CStr objects. - * if the old value is NULL, we do not need to do anything because we - * initialized the new value to NULL via calloc(). - */ -#define tmpCOPYCSTR(name) \ - if(pOld->pCS##name != NULL) {\ - if(rsCStrConstructFromCStr(&(pNew->pCS##name), pOld->pCS##name) != RS_RET_OK) {\ - msgDestruct(&pNew);\ - return NULL;\ - }\ - } -/* Constructs a message object by duplicating another one. - * Returns NULL if duplication failed. We do not need to lock the - * message object here, because a fully-created msg object is never - * allowed to be manipulated. For this, MsgDup() must be used, so MsgDup() - * can never run into a situation where the message object is being - * modified while its content is copied - it's forbidden by definition. - * rgerhards, 2007-07-10 - */ -msg_t* MsgDup(msg_t* pOld) -{ - msg_t* pNew; - - assert(pOld != NULL); - - BEGINfunc - if(msgConstruct(&pNew) != RS_RET_OK) { - return NULL; - } - - /* now copy the message properties */ - pNew->iRefCount = 1; - pNew->iSeverity = pOld->iSeverity; - pNew->iFacility = pOld->iFacility; - pNew->bParseHOSTNAME = pOld->bParseHOSTNAME; - pNew->msgFlags = pOld->msgFlags; - pNew->iProtocolVersion = pOld->iProtocolVersion; - memcpy(&pNew->tRcvdAt, &pOld->tRcvdAt, sizeof(struct syslogTime)); - memcpy(&pNew->tTIMESTAMP, &pOld->tTIMESTAMP, sizeof(struct syslogTime)); - tmpCOPYSZ(Severity); - tmpCOPYSZ(SeverityStr); - tmpCOPYSZ(Facility); - tmpCOPYSZ(FacilityStr); - tmpCOPYSZ(PRI); - tmpCOPYSZ(RawMsg); - tmpCOPYSZ(MSG); - tmpCOPYSZ(UxTradMsg); - tmpCOPYSZ(TAG); - tmpCOPYSZ(HOSTNAME); - tmpCOPYSZ(RcvFrom); - - tmpCOPYCSTR(ProgName); - tmpCOPYCSTR(StrucData); - tmpCOPYCSTR(APPNAME); - tmpCOPYCSTR(PROCID); - tmpCOPYCSTR(MSGID); - - /* we do not copy all other cache properties, as we do not even know - * if they are needed once again. So we let them re-create if needed. - */ - - ENDfunc - return pNew; -} -#undef tmpCOPYSZ -#undef tmpCOPYCSTR - - -/* This method serializes a message object. That means the whole - * object is modified into text form. That text form is suitable for - * later reconstruction of the object by calling MsgDeSerialize(). - * The most common use case for this method is the creation of an - * on-disk representation of the message object. - * We do not serialize the cache properties. We re-create them when needed. - * This saves us a lot of memory. Performance is no concern, as serializing - * is a so slow operation that recration of the caches does not count. Also, - * we do not serialize bParseHOSTNAME, as this is only a helper variable - * during msg construction - and never again used later. - * rgerhards, 2008-01-03 - */ -static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) -{ - DEFiRet; - - assert(pThis != NULL); - assert(pStrm != NULL); - - CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis)); - objSerializeSCALAR(pStrm, iProtocolVersion, SHORT); - objSerializeSCALAR(pStrm, iSeverity, SHORT); - objSerializeSCALAR(pStrm, iFacility, SHORT); - objSerializeSCALAR(pStrm, msgFlags, INT); - objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME); - objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME); - - objSerializePTR(pStrm, pszRawMsg, PSZ); - objSerializePTR(pStrm, pszMSG, PSZ); - objSerializePTR(pStrm, pszUxTradMsg, PSZ); - objSerializePTR(pStrm, pszTAG, PSZ); - objSerializePTR(pStrm, pszHOSTNAME, PSZ); - objSerializePTR(pStrm, pszRcvFrom, PSZ); - - objSerializePTR(pStrm, pCSStrucData, CSTR); - objSerializePTR(pStrm, pCSAPPNAME, CSTR); - objSerializePTR(pStrm, pCSPROCID, CSTR); - objSerializePTR(pStrm, pCSMSGID, CSTR); - - CHKiRet(obj.EndSerialize(pStrm)); - -finalize_it: - RETiRet; -} - - -/* Increment reference count - see description of the "msg" - * structure for details. As a convenience to developers, - * this method returns the msg pointer that is passed to it. - * It is recommended that it is called as follows: - * - * pSecondMsgPointer = MsgAddRef(pOrgMsgPointer); - */ -msg_t *MsgAddRef(msg_t *pM) -{ - assert(pM != NULL); -# ifdef DO_HAVE_ATOMICS - ATOMIC_INC(pM->iRefCount); -# else - MsgLock(pM); - pM->iRefCount++; - MsgUnlock(pM); -# endif - /* DEV debugging only! dbgprintf("MsgAddRef\t0x%x done, Ref now: %d\n", (int)pM, pM->iRefCount);*/ - return(pM); -} - - -/* This functions tries to aquire the PROCID from TAG. Its primary use is - * when a legacy syslog message has been received and should be forwarded as - * syslog-protocol (or the PROCID is requested for any other reason). - * In legacy syslog, the PROCID is considered to be the character sequence - * between the first [ and the first ]. This usually are digits only, but we - * do not check that. However, if there is no closing ], we do not assume we - * can obtain a PROCID. Take in mind that not every legacy syslog message - * actually has a PROCID. - * rgerhards, 2005-11-24 - */ -static rsRetVal aquirePROCIDFromTAG(msg_t *pM) -{ - register int i; - DEFiRet; - - assert(pM != NULL); - if(pM->pCSPROCID != NULL) - return RS_RET_OK; /* we are already done ;) */ - - if(getProtocolVersion(pM) != 0) - return RS_RET_OK; /* we can only emulate if we have legacy format */ - - /* find first '['... */ - i = 0; - while((i < pM->iLenTAG) && (pM->pszTAG[i] != '[')) - ++i; - if(!(i < pM->iLenTAG)) - return RS_RET_OK; /* no [, so can not emulate... */ - - ++i; /* skip '[' */ - - /* now obtain the PROCID string... */ - CHKiRet(rsCStrConstruct(&pM->pCSPROCID)); - rsCStrSetAllocIncrement(pM->pCSPROCID, 16); - while((i < pM->iLenTAG) && (pM->pszTAG[i] != ']')) { - CHKiRet(rsCStrAppendChar(pM->pCSPROCID, pM->pszTAG[i])); - ++i; - } - - if(!(i < pM->iLenTAG)) { - /* oops... it looked like we had a PROCID, but now it has - * turned out this is not true. In this case, we need to free - * the buffer and simply return. Note that this is NOT an error - * case! - */ - rsCStrDestruct(&pM->pCSPROCID); - FINALIZE; - } - - /* OK, finaally we could obtain a PROCID. So let's use it ;) */ - CHKiRet(rsCStrFinish(pM->pCSPROCID)); - -finalize_it: - RETiRet; -} - - -/* Parse and set the "programname" for a given MSG object. Programname - * is a BSD concept, it is the tag without any instance-specific information. - * Precisely, the programname is terminated by either (whichever occurs first): - * - end of tag - * - nonprintable character - * - ':' - * - '[' - * - '/' - * The above definition has been taken from the FreeBSD syslogd sources. - * - * The program name is not parsed by default, because it is infrequently-used. - * If it is needed, this function should be called first. It checks if it is - * already set and extracts it, if not. - * A message object must be provided, else a crash will occur. - * rgerhards, 2005-10-19 - */ -static rsRetVal aquireProgramName(msg_t *pM) -{ - DEFiRet; - register int i; - - assert(pM != NULL); - if(pM->pCSProgName == NULL) { - /* ok, we do not yet have it. So let's parse the TAG - * to obtain it. - */ - CHKiRet(rsCStrConstruct(&pM->pCSProgName)); - rsCStrSetAllocIncrement(pM->pCSProgName, 33); - for( i = 0 - ; (i < pM->iLenTAG) && isprint((int) pM->pszTAG[i]) - && (pM->pszTAG[i] != '\0') && (pM->pszTAG[i] != ':') - && (pM->pszTAG[i] != '[') && (pM->pszTAG[i] != '/') - ; ++i) { - CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i])); - } - CHKiRet(rsCStrFinish(pM->pCSProgName)); - } -finalize_it: - RETiRet; -} - - -/* This function moves the HOSTNAME inside the message object to the - * TAG. It is a specialised function used to handle the condition when - * a message without HOSTNAME is being processed. The missing HOSTNAME - * is only detected at a later stage, during TAG processing, so that - * we already had set the HOSTNAME property and now need to move it to - * the TAG. Of course, we could do this via a couple of get/set methods, - * but it is far more efficient to do it via this specialised method. - * This is especially important as this can be a very common case, e.g. - * when BSD syslog is acting as a sender. - * rgerhards, 2005-11-10. - */ -void moveHOSTNAMEtoTAG(msg_t *pM) -{ - assert(pM != NULL); - if(pM->pszTAG != NULL) - free(pM->pszTAG); - pM->pszTAG = pM->pszHOSTNAME; - pM->iLenTAG = pM->iLenHOSTNAME; - pM->pszHOSTNAME = NULL; - pM->iLenHOSTNAME = 0; -} - -/* Access methods - dumb & easy, not a comment for each ;) - */ -void setProtocolVersion(msg_t *pM, int iNewVersion) -{ - assert(pM != NULL); - if(iNewVersion != 0 && iNewVersion != 1) { - dbgprintf("Tried to set unsupported protocol version %d - changed to 0.\n", iNewVersion); - iNewVersion = 0; - } - pM->iProtocolVersion = iNewVersion; -} - -int getProtocolVersion(msg_t *pM) -{ - assert(pM != NULL); - return(pM->iProtocolVersion); -} - -/* note: string is taken from constant pool, do NOT free */ -char *getProtocolVersionString(msg_t *pM) -{ - assert(pM != NULL); - return(pM->iProtocolVersion ? "1" : "0"); -} - -int getMSGLen(msg_t *pM) -{ - return((pM == NULL) ? 0 : pM->iLenMSG); -} - - -char *getRawMsg(msg_t *pM) -{ - if(pM == NULL) - return ""; - else - if(pM->pszRawMsg == NULL) - return ""; - else - return (char*)pM->pszRawMsg; -} - -char *getUxTradMsg(msg_t *pM) -{ - if(pM == NULL) - return ""; - else - if(pM->pszUxTradMsg == NULL) - return ""; - else - return (char*)pM->pszUxTradMsg; -} - -char *getMSG(msg_t *pM) -{ - if(pM == NULL) - return ""; - else - if(pM->pszMSG == NULL) - return ""; - else - return (char*)pM->pszMSG; -} - - -/* Get PRI value in text form */ -char *getPRI(msg_t *pM) -{ - if(pM == NULL) - return ""; - - MsgLock(pM); - if(pM->pszPRI == NULL) { - /* OK, we need to construct it... - * we use a 5 byte buffer - as of - * RFC 3164, it can't be longer. Should it - * still be, snprintf will truncate... - */ - if((pM->pszPRI = malloc(5)) == NULL) return ""; - pM->iLenPRI = snprintf((char*)pM->pszPRI, 5, "%d", - LOG_MAKEPRI(pM->iFacility, pM->iSeverity)); - } - MsgUnlock(pM); - - return (char*)pM->pszPRI; -} - - -/* Get PRI value as integer */ -int getPRIi(msg_t *pM) -{ - assert(pM != NULL); - return (pM->iFacility << 3) + (pM->iSeverity); -} - - -char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) -{ - if(pM == NULL) - return ""; - - switch(eFmt) { - case tplFmtDefault: - MsgLock(pM); - if(pM->pszTIMESTAMP3164 == NULL) { - if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16); - } - MsgUnlock(pM); - return(pM->pszTIMESTAMP3164); - case tplFmtMySQLDate: - MsgLock(pM); - if(pM->pszTIMESTAMP_MySQL == NULL) { - if((pM->pszTIMESTAMP_MySQL = malloc(15)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL, 15); - } - MsgUnlock(pM); - return(pM->pszTIMESTAMP_MySQL); - case tplFmtPgSQLDate: - MsgLock(pM); - if(pM->pszTIMESTAMP_PgSQL == NULL) { - if((pM->pszTIMESTAMP_PgSQL = malloc(21)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL, 21); - } - MsgUnlock(pM); - return(pM->pszTIMESTAMP_PgSQL); - case tplFmtRFC3164Date: - MsgLock(pM); - if(pM->pszTIMESTAMP3164 == NULL) { - if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16); - } - MsgUnlock(pM); - return(pM->pszTIMESTAMP3164); - case tplFmtRFC3339Date: - MsgLock(pM); - if(pM->pszTIMESTAMP3339 == NULL) { - if((pM->pszTIMESTAMP3339 = malloc(33)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; /* TODO: check this: can it cause a free() of constant memory?) */ - } - datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339, 33); - } - MsgUnlock(pM); - return(pM->pszTIMESTAMP3339); - } - return "INVALID eFmt OPTION!"; -} - -char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) -{ - if(pM == NULL) - return ""; - - switch(eFmt) { - case tplFmtDefault: - MsgLock(pM); - if(pM->pszRcvdAt3164 == NULL) { - if((pM->pszRcvdAt3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16); - } - MsgUnlock(pM); - return(pM->pszRcvdAt3164); - case tplFmtMySQLDate: - MsgLock(pM); - if(pM->pszRcvdAt_MySQL == NULL) { - if((pM->pszRcvdAt_MySQL = malloc(15)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL, 15); - } - MsgUnlock(pM); - return(pM->pszRcvdAt_MySQL); - case tplFmtPgSQLDate: - MsgLock(pM); - if(pM->pszRcvdAt_PgSQL == NULL) { - if((pM->pszRcvdAt_PgSQL = malloc(21)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL, 21); - } - MsgUnlock(pM); - return(pM->pszRcvdAt_PgSQL); - case tplFmtRFC3164Date: - MsgLock(pM); - if(pM->pszRcvdAt3164 == NULL) { - if((pM->pszRcvdAt3164 = malloc(16)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16); - } - MsgUnlock(pM); - return(pM->pszRcvdAt3164); - case tplFmtRFC3339Date: - MsgLock(pM); - if(pM->pszRcvdAt3339 == NULL) { - if((pM->pszRcvdAt3339 = malloc(33)) == NULL) { - glblHadMemShortage = 1; - MsgUnlock(pM); - return ""; - } - datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339, 33); - } - MsgUnlock(pM); - return(pM->pszRcvdAt3339); - } - return "INVALID eFmt OPTION!"; -} - - -char *getSeverity(msg_t *pM) -{ - if(pM == NULL) - return ""; - - MsgLock(pM); - if(pM->pszSeverity == NULL) { - /* we use a 2 byte buffer - can only be one digit */ - if((pM->pszSeverity = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; } - pM->iLenSeverity = - snprintf((char*)pM->pszSeverity, 2, "%d", pM->iSeverity); - } - MsgUnlock(pM); - return((char*)pM->pszSeverity); -} - - -char *getSeverityStr(msg_t *pM) -{ - syslogCODE *c; - int val; - char *name = NULL; - - if(pM == NULL) - return ""; - - MsgLock(pM); - if(pM->pszSeverityStr == NULL) { - for(c = rs_prioritynames, val = pM->iSeverity; c->c_name; c++) - if(c->c_val == val) { - name = c->c_name; - break; - } - if(name == NULL) { - /* we use a 2 byte buffer - can only be one digit */ - if((pM->pszSeverityStr = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; } - pM->iLenSeverityStr = - snprintf((char*)pM->pszSeverityStr, 2, "%d", pM->iSeverity); - } else { - if((pM->pszSeverityStr = (uchar*) strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; } - pM->iLenSeverityStr = strlen((char*)name); - } - } - MsgUnlock(pM); - return((char*)pM->pszSeverityStr); -} - -char *getFacility(msg_t *pM) -{ - if(pM == NULL) - return ""; - - MsgLock(pM); - if(pM->pszFacility == NULL) { - /* we use a 12 byte buffer - as of - * syslog-protocol, facility can go - * up to 2^32 -1 - */ - if((pM->pszFacility = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; } - pM->iLenFacility = - snprintf((char*)pM->pszFacility, 12, "%d", pM->iFacility); - } - MsgUnlock(pM); - return((char*)pM->pszFacility); -} - -char *getFacilityStr(msg_t *pM) -{ - syslogCODE *c; - int val; - char *name = NULL; - - if(pM == NULL) - return ""; - - MsgLock(pM); - if(pM->pszFacilityStr == NULL) { - for(c = rs_facilitynames, val = pM->iFacility << 3; c->c_name; c++) - if(c->c_val == val) { - name = c->c_name; - break; - } - if(name == NULL) { - /* we use a 12 byte buffer - as of - * syslog-protocol, facility can go - * up to 2^32 -1 - */ - if((pM->pszFacilityStr = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; } - pM->iLenFacilityStr = - snprintf((char*)pM->pszFacilityStr, 12, "%d", val >> 3); - } else { - if((pM->pszFacilityStr = (uchar*)strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; } - pM->iLenFacilityStr = strlen((char*)name); - } - } - MsgUnlock(pM); - return((char*)pM->pszFacilityStr); -} - - -/* set flow control state (if not called, the default - NO_DELAY - is used) - * This needs no locking because it is only done while the object is - * not fully constructed (which also means you must not call this - * method after the msg has been handed over to a queue). - * rgerhards, 2008-03-14 - */ -rsRetVal -MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl) -{ - DEFiRet; - assert(pMsg != NULL); - assert(eFlowCtl == eFLOWCTL_NO_DELAY || eFlowCtl == eFLOWCTL_LIGHT_DELAY || eFlowCtl == eFLOWCTL_FULL_DELAY); - - pMsg->flowCtlType = eFlowCtl; - - RETiRet; -} - - -/* rgerhards 2004-11-24: set APP-NAME in msg object - * TODO: revisit msg locking code! - */ -rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME) -{ - DEFiRet; - assert(pMsg != NULL); - if(pMsg->pCSAPPNAME == NULL) { - /* we need to obtain the object first */ - CHKiRet(rsCStrConstruct(&pMsg->pCSAPPNAME)); - rsCStrSetAllocIncrement(pMsg->pCSAPPNAME, 128); - } - /* if we reach this point, we have the object */ - iRet = rsCStrSetSzStr(pMsg->pCSAPPNAME, (uchar*) pszAPPNAME); - -finalize_it: - RETiRet; -} - - -static void tryEmulateAPPNAME(msg_t *pM); /* forward reference */ -/* rgerhards, 2005-11-24 - */ -char *getAPPNAME(msg_t *pM) -{ - assert(pM != NULL); - MsgLock(pM); - if(pM->pCSAPPNAME == NULL) - tryEmulateAPPNAME(pM); - MsgUnlock(pM); - return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME); -} - - -/* rgerhards 2004-11-24: set PROCID in msg object - */ -rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID) -{ - DEFiRet; - ISOBJ_TYPE_assert(pMsg, msg); - if(pMsg->pCSPROCID == NULL) { - /* we need to obtain the object first */ - CHKiRet(rsCStrConstruct(&pMsg->pCSPROCID)); - rsCStrSetAllocIncrement(pMsg->pCSPROCID, 128); - } - /* if we reach this point, we have the object */ - iRet = rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID); - -finalize_it: - RETiRet; -} - -/* rgerhards, 2005-11-24 - */ -int getPROCIDLen(msg_t *pM) -{ - assert(pM != NULL); - MsgLock(pM); - if(pM->pCSPROCID == NULL) - aquirePROCIDFromTAG(pM); - MsgUnlock(pM); - return (pM->pCSPROCID == NULL) ? 1 : rsCStrLen(pM->pCSPROCID); -} - - -/* rgerhards, 2005-11-24 - */ -char *getPROCID(msg_t *pM) -{ - char* pszRet; - - ISOBJ_TYPE_assert(pM, msg); - MsgLock(pM); - if(pM->pCSPROCID == NULL) - aquirePROCIDFromTAG(pM); - pszRet = (pM->pCSPROCID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSPROCID); - MsgUnlock(pM); - return pszRet; -} - - -/* rgerhards 2004-11-24: set MSGID in msg object - */ -rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID) -{ - DEFiRet; - ISOBJ_TYPE_assert(pMsg, msg); - if(pMsg->pCSMSGID == NULL) { - /* we need to obtain the object first */ - CHKiRet(rsCStrConstruct(&pMsg->pCSMSGID)); - rsCStrSetAllocIncrement(pMsg->pCSMSGID, 128); - } - /* if we reach this point, we have the object */ - iRet = rsCStrSetSzStr(pMsg->pCSMSGID, (uchar*) pszMSGID); - -finalize_it: - RETiRet; -} - -/* rgerhards, 2005-11-24 - */ -#if 0 /* This method is currently not called, be we like to preserve it */ -static int getMSGIDLen(msg_t *pM) -{ - return (pM->pCSMSGID == NULL) ? 1 : rsCStrLen(pM->pCSMSGID); -} -#endif - - -/* rgerhards, 2005-11-24 - */ -char *getMSGID(msg_t *pM) -{ - return (pM->pCSMSGID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID); -} - - -/* Set the TAG to a caller-provided string. This is thought - * to be a heap buffer that the caller will no longer use. This - * function is a performance optimization over MsgSetTAG(). - * rgerhards 2004-11-19 - */ -void MsgAssignTAG(msg_t *pMsg, uchar *pBuf) -{ - assert(pMsg != NULL); - pMsg->iLenTAG = (pBuf == NULL) ? 0 : strlen((char*)pBuf); - pMsg->pszTAG = (uchar*) pBuf; -} - - -/* rgerhards 2004-11-16: set TAG in msg object - */ -void MsgSetTAG(msg_t *pMsg, char* pszTAG) -{ - assert(pMsg != NULL); - if(pMsg->pszTAG != NULL) - free(pMsg->pszTAG); - pMsg->iLenTAG = strlen(pszTAG); - if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL) - memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1); - else - dbgprintf("Could not allocate memory in MsgSetTAG()\n"); -} - - -/* This function tries to emulate the TAG if none is - * set. Its primary purpose is to provide an old-style TAG - * when a syslog-protocol message has been received. Then, - * the tag is APP-NAME "[" PROCID "]". The function first checks - * if there is a TAG and, if not, if it can emulate it. - * rgerhards, 2005-11-24 - */ -static void tryEmulateTAG(msg_t *pM) -{ - int iTAGLen; - uchar *pBuf; - assert(pM != NULL); - - if(pM->pszTAG != NULL) - return; /* done, no need to emulate */ - - if(getProtocolVersion(pM) == 1) { - if(!strcmp(getPROCID(pM), "-")) { - /* no process ID, use APP-NAME only */ - MsgSetTAG(pM, getAPPNAME(pM)); - } else { - /* now we can try to emulate */ - iTAGLen = getAPPNAMELen(pM) + getPROCIDLen(pM) + 3; - if((pBuf = malloc(iTAGLen * sizeof(char))) == NULL) - return; /* nothing we can do */ - snprintf((char*)pBuf, iTAGLen, "%s[%s]", getAPPNAME(pM), getPROCID(pM)); - MsgAssignTAG(pM, pBuf); - } - } -} - - -#if 0 /* This method is currently not called, be we like to preserve it */ -static int getTAGLen(msg_t *pM) -{ - if(pM == NULL) - return 0; - else { - tryEmulateTAG(pM); - if(pM->pszTAG == NULL) - return 0; - else - return pM->iLenTAG; - } -} -#endif - - -char *getTAG(msg_t *pM) -{ - char *ret; - - if(pM == NULL) - ret = ""; - else { - MsgLock(pM); - tryEmulateTAG(pM); - if(pM->pszTAG == NULL) - ret = ""; - else - ret = (char*) pM->pszTAG; - MsgUnlock(pM); - } - return(ret); -} - - -int getHOSTNAMELen(msg_t *pM) -{ - if(pM == NULL) - return 0; - else - if(pM->pszHOSTNAME == NULL) - return 0; - else - return pM->iLenHOSTNAME; -} - - -char *getHOSTNAME(msg_t *pM) -{ - if(pM == NULL) - return ""; - else - if(pM->pszHOSTNAME == NULL) - return ""; - else - return (char*) pM->pszHOSTNAME; -} - - -char *getRcvFrom(msg_t *pM) -{ - if(pM == NULL) - return ""; - else - if(pM->pszRcvFrom == NULL) - return ""; - else - return (char*) pM->pszRcvFrom; -} - -/* rgerhards 2004-11-24: set STRUCTURED DATA in msg object - */ -rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData) -{ - DEFiRet; - ISOBJ_TYPE_assert(pMsg, msg); - if(pMsg->pCSStrucData == NULL) { - /* we need to obtain the object first */ - CHKiRet(rsCStrConstruct(&pMsg->pCSStrucData)); - rsCStrSetAllocIncrement(pMsg->pCSStrucData, 128); - } - /* if we reach this point, we have the object */ - iRet = rsCStrSetSzStr(pMsg->pCSStrucData, (uchar*) pszStrucData); - -finalize_it: - RETiRet; -} - -/* get the length of the "STRUCTURED-DATA" sz string - * rgerhards, 2005-11-24 - */ -#if 0 /* This method is currently not called, be we like to preserve it */ -static int getStructuredDataLen(msg_t *pM) -{ - return (pM->pCSStrucData == NULL) ? 1 : rsCStrLen(pM->pCSStrucData); -} -#endif - - -/* get the "STRUCTURED-DATA" as sz string - * rgerhards, 2005-11-24 - */ -char *getStructuredData(msg_t *pM) -{ - return (pM->pCSStrucData == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSStrucData); -} - - - -/* get the length of the "programname" sz string - * rgerhards, 2005-10-19 - */ -int getProgramNameLen(msg_t *pM) -{ - int iRet; - - assert(pM != NULL); - MsgLock(pM); - if((iRet = aquireProgramName(pM)) != RS_RET_OK) { - dbgprintf("error %d returned by aquireProgramName() in getProgramNameLen()\n", iRet); - MsgUnlock(pM); - return 0; /* best we can do (consistent wiht what getProgramName() returns) */ - } - MsgUnlock(pM); - - return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName); -} - - -/* get the "programname" as sz string - * rgerhards, 2005-10-19 - */ -char *getProgramName(msg_t *pM) /* this is the non-locking version for internal use */ -{ - int iRet; - char *pszRet; - - assert(pM != NULL); - MsgLock(pM); - if((iRet = aquireProgramName(pM)) != RS_RET_OK) { - dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet); - pszRet = ""; /* best we can do */ - } else { - pszRet = (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName); - } - - MsgUnlock(pM); - return pszRet; -} -/* The code below was an approach without PTHREAD_MUTEX_RECURSIVE - * However, it turned out to be quite complex. So far, we use recursive - * locking, which is OK from a performance point of view, especially as - * we do not anticipate that multithreading msg objects is used often. - * However, we may re-think about using non-recursive locking and I leave this - * code in here to conserve the idea. -- rgerhards, 2008-01-05 - */ -#if 0 -static char *getProgramNameNoLock(msg_t *pM) /* this is the non-locking version for internal use */ -{ - int iRet; - - assert(pM != NULL); - if((iRet = aquireProgramName(pM)) != RS_RET_OK) { - dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet); - return ""; /* best we can do */ - } - - return (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName); -} -char *getProgramName(msg_t *pM) /* this is the external callable version */ -{ - char *pszRet; - - MsgLock(pM); - pszRet = getProgramNameNoLock(pM); - MsgUnlock(pM); - return pszRet; -} -/* an alternative approach has been: */ -/* The macro below is used to generate external function definitions - * for such functions that may also be called internally (and thus have - * both a locking and non-locking implementation. Over time, we could - * reconsider how we handle that. -- rgerhards, 2008-01-05 - */ -#define EXT_LOCKED_FUNC(fName, ret) \ -ret fName(msg_t *pM) \ -{ \ - ret valRet; \ - MsgLock(pM); \ - valRet = fName##NoLock(pM); \ - MsgUnlock(pM); \ - return(valRet); \ -} -EXT_LOCKED_FUNC(getProgramName, char*) -/* in this approach, the external function is provided by the macro and - * needs not to be writen. - */ -#endif /* #if 0 -- saved code */ - - -/* This function tries to emulate APPNAME if it is not present. Its - * main use is when we have received a log record via legacy syslog and - * now would like to send out the same one via syslog-protocol. - */ -static void tryEmulateAPPNAME(msg_t *pM) -{ - assert(pM != NULL); - if(pM->pCSAPPNAME != NULL) - return; /* we are already done */ - - if(getProtocolVersion(pM) == 0) { - /* only then it makes sense to emulate */ - MsgSetAPPNAME(pM, getProgramName(pM)); - } -} - - -/* rgerhards, 2005-11-24 - */ -static int getAPPNAMELen(msg_t *pM) -{ - assert(pM != NULL); - if(pM->pCSAPPNAME == NULL) - tryEmulateAPPNAME(pM); - return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME); -} - - -/* rgerhards 2004-11-16: set pszRcvFrom in msg object - */ -void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom) -{ - assert(pMsg != NULL); - if(pMsg->pszRcvFrom != NULL) - free(pMsg->pszRcvFrom); - - pMsg->iLenRcvFrom = strlen(pszRcvFrom); - if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) { - memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1); - } -} - - -/* Set the HOSTNAME to a caller-provided string. This is thought - * to be a heap buffer that the caller will no longer use. This - * function is a performance optimization over MsgSetHOSTNAME(). - * rgerhards 2004-11-19 - */ -void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf) -{ - assert(pMsg != NULL); - assert(pBuf != NULL); - pMsg->iLenHOSTNAME = strlen(pBuf); - pMsg->pszHOSTNAME = (uchar*) pBuf; -} - - -/* rgerhards 2004-11-09: set HOSTNAME in msg object - * rgerhards, 2007-06-21: - * Does not return anything. If an error occurs, the hostname is - * simply not set. I have changed this behaviour. The only problem - * we can run into is memory shortage. If we have such, it is better - * to loose the hostname than the full message. So we silently ignore - * that problem and hope that memory will be available the next time - * we need it. The rest of the code already knows how to handle an - * unset HOSTNAME. - */ -void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME) -{ - assert(pMsg != NULL); - if(pMsg->pszHOSTNAME != NULL) - free(pMsg->pszHOSTNAME); - - pMsg->iLenHOSTNAME = strlen(pszHOSTNAME); - if((pMsg->pszHOSTNAME = malloc(pMsg->iLenHOSTNAME + 1)) != NULL) - memcpy(pMsg->pszHOSTNAME, pszHOSTNAME, pMsg->iLenHOSTNAME + 1); - else - dbgprintf("Could not allocate memory in MsgSetHOSTNAME()\n"); -} - - -/* Set the UxTradMsg to a caller-provided string. This is thought - * to be a heap buffer that the caller will no longer use. This - * function is a performance optimization over MsgSetUxTradMsg(). - * rgerhards 2004-11-19 - */ -#if 0 /* This method is currently not called, be we like to preserve it */ -static void MsgAssignUxTradMsg(msg_t *pMsg, char *pBuf) -{ - assert(pMsg != NULL); - assert(pBuf != NULL); - pMsg->iLenUxTradMsg = strlen(pBuf); - pMsg->pszUxTradMsg = pBuf; -} -#endif - - -/* rgerhards 2004-11-17: set the traditional Unix message in msg object - */ -int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg) -{ - assert(pMsg != NULL); - assert(pszUxTradMsg != NULL); - pMsg->iLenUxTradMsg = strlen(pszUxTradMsg); - if(pMsg->pszUxTradMsg != NULL) - free(pMsg->pszUxTradMsg); - if((pMsg->pszUxTradMsg = malloc(pMsg->iLenUxTradMsg + 1)) != NULL) - memcpy(pMsg->pszUxTradMsg, pszUxTradMsg, pMsg->iLenUxTradMsg + 1); - else - dbgprintf("Could not allocate memory for pszUxTradMsg buffer."); - - return(0); -} - - -/* rgerhards 2004-11-09: set MSG in msg object - */ -void MsgSetMSG(msg_t *pMsg, char* pszMSG) -{ - assert(pMsg != NULL); - assert(pszMSG != NULL); - - if(pMsg->pszMSG != NULL) - free(pMsg->pszMSG); - - pMsg->iLenMSG = strlen(pszMSG); - if((pMsg->pszMSG = (uchar*) malloc(pMsg->iLenMSG + 1)) != NULL) - memcpy(pMsg->pszMSG, pszMSG, pMsg->iLenMSG + 1); - else - dbgprintf("MsgSetMSG could not allocate memory for pszMSG buffer."); -} - -/* rgerhards 2004-11-11: set RawMsg in msg object - */ -void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg) -{ - assert(pMsg != NULL); - if(pMsg->pszRawMsg != NULL) - free(pMsg->pszRawMsg); - - pMsg->iLenRawMsg = strlen(pszRawMsg); - if((pMsg->pszRawMsg = (uchar*) malloc(pMsg->iLenRawMsg + 1)) != NULL) - memcpy(pMsg->pszRawMsg, pszRawMsg, pMsg->iLenRawMsg + 1); - else - dbgprintf("Could not allocate memory for pszRawMsg buffer."); -} - - -/* Decode a priority into textual information like auth.emerg. - * The variable pRes must point to a user-supplied buffer and - * pResLen must contain its size. The pointer to the buffer - * is also returned, what makes this functiona suitable for - * use in printf-like functions. - * Note: a buffer size of 20 characters is always sufficient. - * Interface to this function changed 2007-06-15 by RGerhards - */ -char *textpri(char *pRes, size_t pResLen, int pri) -{ - syslogCODE *c_pri, *c_fac; - - assert(pRes != NULL); - assert(pResLen > 0); - - for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++); - for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++); - - snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri); - - return pRes; -} - - -/* This function returns the current date in different - * variants. It is used to construct the $NOW series of - * system properties. The returned buffer must be freed - * by the caller when no longer needed. If the function - * can not allocate memory, it returns a NULL pointer. - * Added 2007-07-10 rgerhards - */ -typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_HHOUR, NOW_QHOUR, NOW_MINUTE } eNOWType; -#define tmpBUFSIZE 16 /* size of formatting buffer */ -static uchar *getNOW(eNOWType eNow) -{ - uchar *pBuf; - struct syslogTime t; - - if((pBuf = (uchar*) malloc(sizeof(uchar) * tmpBUFSIZE)) == NULL) { - glblHadMemShortage = 1; - return NULL; - } - - datetime.getCurrTime(&t); - switch(eNow) { - case NOW_NOW: - snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day); - break; - case NOW_YEAR: - snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d", t.year); - break; - case NOW_MONTH: - snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.month); - break; - case NOW_DAY: - snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.day); - break; - case NOW_HOUR: - snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.hour); - break; - case NOW_HHOUR: - snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute / 30); - break; - case NOW_QHOUR: - snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute / 15); - break; - case NOW_MINUTE: - snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute); - break; - } - - return(pBuf); -} -#undef tmpBUFSIZE /* clean up */ - - -/* This function returns a string-representation of the - * requested message property. This is a generic function used - * to abstract properties so that these can be easier - * queried. Returns NULL if property could not be found. - * Actually, this function is a big if..elseif. What it does - * is simply to map property names (from MonitorWare) to the - * message object data fields. - * - * In case we need string forms of propertis we do not - * yet have in string form, we do a memory allocation that - * is sufficiently large (in all cases). Once the string - * form has been obtained, it is saved until the Msg object - * is finally destroyed. This is so that we save the processing - * time in the (likely) case that this property is requested - * again. It also saves us a lot of dynamic memory management - * issues in the upper layers, because we so can guarantee that - * the buffer will remain static AND available during the lifetime - * of the object. Please note that both the max size allocation as - * well as keeping things in memory might like look like a - * waste of memory (some might say it actually is...) - we - * deliberately accept this because performance is more important - * to us ;) - * rgerhards 2004-11-18 - * Parameter "bMustBeFreed" is set by this function. It tells the - * caller whether or not the string returned must be freed by the - * caller itself. It is is 0, the caller MUST NOT free it. If it is - * 1, the caller MUST free 1. Handling this wrongly leads to either - * a memory leak of a program abort (do to double-frees or frees on - * the constant memory pool). So be careful to do it right. - * rgerhards 2004-11-23 - * regular expression support contributed by Andres Riancho merged - * on 2005-09-13 - * changed so that it now an be called without a template entry (NULL). - * In this case, only the (unmodified) property is returned. This will - * be used in selector line processing. - * rgerhards 2005-09-15 - */ -char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, - cstr_t *pCSPropName, unsigned short *pbMustBeFreed) -{ - uchar *pName; - char *pRes; /* result pointer */ - char *pBufStart; - char *pBuf; - int iLen; - -#ifdef FEATURE_REGEXP - /* Variables necessary for regular expression matching */ - size_t nmatch = 1; - regmatch_t pmatch[1]; -#endif - - assert(pMsg != NULL); - assert(pbMustBeFreed != NULL); - - if(pCSPropName == NULL) { - assert(pTpe != NULL); - pName = pTpe->data.field.pPropRepl; - } else { - pName = rsCStrGetSzStrNoNULL(pCSPropName); - } - *pbMustBeFreed = 0; - - /* sometimes there are aliases to the original MonitoWare - * property names. These come after || in the ifs below. */ - if(!strcmp((char*) pName, "msg")) { - pRes = getMSG(pMsg); - } else if(!strcmp((char*) pName, "rawmsg")) { - pRes = getRawMsg(pMsg); - } else if(!strcmp((char*) pName, "uxtradmsg")) { - pRes = getUxTradMsg(pMsg); - } else if(!strcmp((char*) pName, "fromhost")) { - pRes = getRcvFrom(pMsg); - } else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) { - pRes = getHOSTNAME(pMsg); - } else if(!strcmp((char*) pName, "syslogtag")) { - pRes = getTAG(pMsg); - } else if(!strcmp((char*) pName, "pri")) { - pRes = getPRI(pMsg); - } else if(!strcmp((char*) pName, "pri-text")) { - pBuf = malloc(20 * sizeof(char)); - if(pBuf == NULL) { - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } else { - *pbMustBeFreed = 1; - pRes = textpri(pBuf, 20, getPRIi(pMsg)); - } - } else if(!strcmp((char*) pName, "iut")) { - pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */ - } else if(!strcmp((char*) pName, "syslogfacility")) { - pRes = getFacility(pMsg); - } else if(!strcmp((char*) pName, "syslogfacility-text")) { - pRes = getFacilityStr(pMsg); - } else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) { - pRes = getSeverity(pMsg); - } else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) { - pRes = getSeverityStr(pMsg); - } else if(!strcmp((char*) pName, "timegenerated")) { - pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat); - } else if(!strcmp((char*) pName, "timereported") - || !strcmp((char*) pName, "timestamp")) { - pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat); - } else if(!strcmp((char*) pName, "programname")) { - pRes = getProgramName(pMsg); - } else if(!strcmp((char*) pName, "protocol-version")) { - pRes = getProtocolVersionString(pMsg); - } else if(!strcmp((char*) pName, "structured-data")) { - pRes = getStructuredData(pMsg); - } else if(!strcmp((char*) pName, "app-name")) { - pRes = getAPPNAME(pMsg); - } else if(!strcmp((char*) pName, "procid")) { - pRes = getPROCID(pMsg); - } else if(!strcmp((char*) pName, "msgid")) { - pRes = getMSGID(pMsg); - /* here start system properties (those, that do not relate to the message itself */ - } else if(!strcmp((char*) pName, "$now")) { - if((pRes = (char*) getNOW(NOW_NOW)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$year")) { - if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$month")) { - if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$day")) { - if((pRes = (char*) getNOW(NOW_DAY)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$hour")) { - if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$hhour")) { - if((pRes = (char*) getNOW(NOW_HHOUR)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$qhour")) { - if((pRes = (char*) getNOW(NOW_QHOUR)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else if(!strcmp((char*) pName, "$minute")) { - if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) { - return "***OUT OF MEMORY***"; - } else - *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ - } else { - /* there is no point in continuing, we may even otherwise render the - * error message unreadable. rgerhards, 2007-07-10 - */ - dbgprintf("invalid property name: '%s'\n", pName); - return "**INVALID PROPERTY NAME**"; - } - - /* If we did not receive a template pointer, we are already done... */ - if(pTpe == NULL) { - return pRes; - } - - /* Now check if we need to make "temporary" transformations (these - * are transformations that do not go back into the message - - * memory must be allocated for them!). - */ - - /* substring extraction */ - /* first we check if we need to extract by field number - * rgerhards, 2005-12-22 - */ - if(pTpe->data.field.has_fields == 1) { - size_t iCurrFld; - char *pFld; - char *pFldEnd; - /* first, skip to the field in question. The field separator - * is always one character and is stored in the template entry. - */ - iCurrFld = 1; - pFld = pRes; - while(*pFld && iCurrFld < pTpe->data.field.iToPos) { - /* skip fields until the requested field or end of string is found */ - while(*pFld && (uchar) *pFld != pTpe->data.field.field_delim) - ++pFld; /* skip to field terminator */ - if(*pFld == pTpe->data.field.field_delim) { - ++pFld; /* eat it */ - ++iCurrFld; - } - } - dbgprintf("field requested %d, field found %d\n", pTpe->data.field.iToPos, (int) iCurrFld); - - if(iCurrFld == pTpe->data.field.iToPos) { - /* field found, now extract it */ - /* first of all, we need to find the end */ - pFldEnd = pFld; - while(*pFldEnd && *pFldEnd != pTpe->data.field.field_delim) - ++pFldEnd; - --pFldEnd; /* we are already at the delimiter - so we need to - * step back a little not to copy it as part of the field. */ - /* we got our end pointer, now do the copy */ - /* TODO: code copied from below, this is a candidate for a separate function */ - iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */ - pBufStart = pBuf = malloc((iLen + 1) * sizeof(char)); - if(pBuf == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - /* now copy */ - memcpy(pBuf, pFld, iLen); - pBuf[iLen] = '\0'; /* terminate it */ - if(*pbMustBeFreed == 1) - free(pRes); - pRes = pBufStart; - *pbMustBeFreed = 1; - if(*(pFldEnd+1) != '\0') - ++pFldEnd; /* OK, skip again over delimiter char */ - } else { - /* field not found, return error */ - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**FIELD NOT FOUND**"; - } - } else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) { - /* we need to obtain a private copy */ - int iFrom, iTo; - char *pSb; - iFrom = pTpe->data.field.iFromPos; - iTo = pTpe->data.field.iToPos; - /* need to zero-base to and from (they are 1-based!) */ - if(iFrom > 0) - --iFrom; - if(iTo > 0) - --iTo; - iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */ - pBufStart = pBuf = malloc((iLen + 1) * sizeof(char)); - if(pBuf == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - pSb = pRes; - if(iFrom) { - /* skip to the start of the substring (can't do pointer arithmetic - * because the whole string might be smaller!!) - */ - while(*pSb && iFrom) { - --iFrom; - ++pSb; - } - } - /* OK, we are at the begin - now let's copy... */ - while(*pSb && iLen) { - *pBuf++ = *pSb; - ++pSb; - --iLen; - } - *pBuf = '\0'; - if(*pbMustBeFreed == 1) - free(pRes); - pRes = pBufStart; - *pbMustBeFreed = 1; -#ifdef FEATURE_REGEXP - } else { - /* Check for regular expressions */ - if (pTpe->data.field.has_regex != 0) { - if (pTpe->data.field.has_regex == 2) - /* Could not compile regex before! */ - return "**NO MATCH** **BAD REGULAR EXPRESSION**"; - - dbgprintf("debug: String to match for regex is: %s\n", pRes); - - if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) { - if (0 != regexp.regexec(&pTpe->data.field.re, pRes, nmatch, pmatch, 0)) { - /* we got no match! */ - if (*pbMustBeFreed == 1) { - free(pRes); - *pbMustBeFreed = 0; - } - return "**NO MATCH**"; - } else { - /* Match! */ - /* I need to malloc pB */ - int iLenBuf; - char *pB; - - iLenBuf = pmatch[0].rm_eo - pmatch[0].rm_so; - pB = (char *) malloc((iLenBuf + 1) * sizeof(char)); - - if (pB == NULL) { - if (*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY ALLOCATING pBuf**"; - } - - /* Lets copy the matched substring to the buffer */ - memcpy(pB, pRes + pmatch[0].rm_so, iLenBuf); - pB[iLenBuf] = '\0';/* terminate string, did not happen before */ - - if (*pbMustBeFreed == 1) - free(pRes); - pRes = pB; - *pbMustBeFreed = 1; - } - } else { - /* we could not load regular expression support. This is quite unexpected at - * this stage of processing (after all, the config parser found it), but so - * it is. We return an error in that case. -- rgerhards, 2008-03-07 - */ - dbgprintf("could not get regexp object pointer, so regexp can not be evaluated\n"); - if (*pbMustBeFreed == 1) { - free(pRes); - *pbMustBeFreed = 0; - } - return "***REGEXP NOT AVAILABLE***"; - } - } -#endif /* #ifdef FEATURE_REGEXP */ - } - - if(*pRes) { - /* case conversations (should go after substring, because so we are able to - * work on the smallest possible buffer). - */ - if(pTpe->data.field.eCaseConv != tplCaseConvNo) { - /* we need to obtain a private copy */ - int iBufLen = strlen(pRes); - char *pBStart; - char *pB; - char *pSrc; - pBStart = pB = malloc((iBufLen + 1) * sizeof(char)); - if(pB == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - pSrc = pRes; - while(*pSrc) { - *pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ? - (char)toupper((int)*pSrc) : (char)tolower((int)*pSrc); - /* currently only these two exist */ - ++pSrc; - } - *pB = '\0'; - if(*pbMustBeFreed == 1) - free(pRes); - pRes = pBStart; - *pbMustBeFreed = 1; - } - - /* now do control character dropping/escaping/replacement - * Only one of these can be used. If multiple options are given, the - * result is random (though currently there obviously is an order of - * preferrence, see code below. But this is NOT guaranteed. - * RGerhards, 2006-11-17 - * We must copy the strings if we modify them, because they may either - * point to static memory or may point into the message object, in which - * case we would actually modify the original property (which of course - * is wrong). - * This was found and fixed by varmojefkoj on 2007-09-11 - */ - if(pTpe->data.field.options.bDropCC) { - int iLenBuf = 0; - char *pSrc = pRes; - char *pDstStart; - char *pDst; - char bDropped = 0; - - while(*pSrc) { - if(!iscntrl((int) *pSrc++)) - iLenBuf++; - else - bDropped = 1; - } - - if(bDropped) { - pDst = pDstStart = malloc(iLenBuf + 1); - if(pDst == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - for(pSrc = pRes; *pSrc; pSrc++) { - if(!iscntrl((int) *pSrc)) - *pDst++ = *pSrc; - } - *pDst = '\0'; - if(*pbMustBeFreed == 1) - free(pRes); - pRes = pDstStart; - *pbMustBeFreed = 1; - } - } else if(pTpe->data.field.options.bSpaceCC) { - char *pSrc; - char *pDstStart; - char *pDst; - - if(*pbMustBeFreed == 1) { - /* in this case, we already work on dynamic - * memory, so there is no need to copy it - we can - * modify it in-place without any harm. This is a - * performance optiomization. - */ - for(pDst = pRes; *pDst; pDst++) { - if(iscntrl((int) *pDst)) - *pDst = ' '; - } - } else { - pDst = pDstStart = malloc(strlen(pRes) + 1); - if(pDst == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - for(pSrc = pRes; *pSrc; pSrc++) { - if(iscntrl((int) *pSrc)) - *pDst++ = ' '; - else - *pDst++ = *pSrc; - } - *pDst = '\0'; - pRes = pDstStart; - *pbMustBeFreed = 1; - } - } else if(pTpe->data.field.options.bEscapeCC) { - /* we must first count how many control charactes are - * present, because we need this to compute the new string - * buffer length. While doing so, we also compute the string - * length. - */ - int iNumCC = 0; - int iLenBuf = 0; - char *pB; - - for(pB = pRes ; *pB ; ++pB) { - ++iLenBuf; - if(iscntrl((int) *pB)) - ++iNumCC; - } - - if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */ - /* OK, let's do the escaping... */ - char *pBStart; - char szCCEsc[8]; /* buffer for escape sequence */ - int i; - - iLenBuf += iNumCC * 4; - pBStart = pB = malloc((iLenBuf + 1) * sizeof(char)); - if(pB == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - while(*pRes) { - if(iscntrl((int) *pRes)) { - snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes); - for(i = 0 ; i < 4 ; ++i) - *pB++ = szCCEsc[i]; - } else { - *pB++ = *pRes; - } - ++pRes; - } - *pB = '\0'; - if(*pbMustBeFreed == 1) - free(pRes); - pRes = pBStart; - *pbMustBeFreed = 1; - } - } - } - - /* Take care of spurious characters to make the property safe - * for a path definition - */ - if(pTpe->data.field.options.bSecPathDrop || pTpe->data.field.options.bSecPathReplace) { - if(pTpe->data.field.options.bSecPathDrop) { - int iLenBuf = 0; - char *pSrc = pRes; - char *pDstStart; - char *pDst; - char bDropped = 0; - - while(*pSrc) { - if(*pSrc++ != '/') - iLenBuf++; - else - bDropped = 1; - } - - if(bDropped) { - pDst = pDstStart = malloc(iLenBuf + 1); - if(pDst == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - for(pSrc = pRes; *pSrc; pSrc++) { - if(*pSrc != '/') - *pDst++ = *pSrc; - } - *pDst = '\0'; - if(*pbMustBeFreed == 1) - free(pRes); - pRes = pDstStart; - *pbMustBeFreed = 1; - } - } else { - char *pSrc; - char *pDstStart; - char *pDst; - - if(*pbMustBeFreed == 1) { - /* here, again, we can modify the string as we already obtained - * a private buffer. As we do not change the size of that buffer, - * in-place modification is possible. This is a performance - * enhancement. - */ - for(pDst = pRes; *pDst; pDst++) { - if(*pDst == '/') - *pDst++ = '_'; - } - } else { - pDst = pDstStart = malloc(strlen(pRes) + 1); - if(pDst == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - for(pSrc = pRes; *pSrc; pSrc++) { - if(*pSrc == '/') - *pDst++ = '_'; - else - *pDst++ = *pSrc; - } - *pDst = '\0'; - /* we must NOT check if it needs to be freed, because we have done - * this in the if above. So if we come to hear, the pSrc string needs - * not to be freed (and we do not need to care about it). - */ - pRes = pDstStart; - *pbMustBeFreed = 1; - } - } - - /* check for "." and ".." (note the parenthesis in the if condition!) */ - if((*pRes == '.') && (*(pRes + 1) == '\0' || (*(pRes + 1) == '.' && *(pRes + 2) == '\0'))) { - char *pTmp = pRes; - - if(*(pRes + 1) == '\0') - pRes = "_"; - else - pRes = "_.";; - if(*pbMustBeFreed == 1) - free(pTmp); - *pbMustBeFreed = 0; - } else if(*pRes == '\0') { - if(*pbMustBeFreed == 1) - free(pRes); - pRes = "_"; - *pbMustBeFreed = 0; - } - } - - /* Now drop last LF if present (pls note that this must not be done - * if bEscapeCC was set! - */ - if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) { - int iLn = strlen(pRes); - char *pB; - if(iLn > 0 && *(pRes + iLn - 1) == '\n') { - /* we have a LF! */ - /* check if we need to obtain a private copy */ - if(*pbMustBeFreed == 0) { - /* ok, original copy, need a private one */ - pB = malloc((iLn + 1) * sizeof(char)); - if(pB == NULL) { - *pbMustBeFreed = 0; - return "**OUT OF MEMORY**"; - } - memcpy(pB, pRes, iLn - 1); - pRes = pB; - *pbMustBeFreed = 1; - } - *(pRes + iLn - 1) = '\0'; /* drop LF ;) */ - } - } - - /*dbgprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */ - return(pRes); -} - - -/* The returns a message variable suitable for use with RainerScript. Most importantly, this means - * that the value is returned in a var_t object. The var_t is constructed inside this function and - * MUST be freed by the caller. - * rgerhards, 2008-02-25 - */ -rsRetVal -msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar) -{ - DEFiRet; - var_t *pVar; - uchar *pszProp = NULL; - cstr_t *pstrProp; - unsigned short bMustBeFreed = 0; - - ISOBJ_TYPE_assert(pThis, msg); - ASSERT(pstrPropName != NULL); - ASSERT(ppVar != NULL); - - /* make sure we have a var_t instance */ - CHKiRet(var.Construct(&pVar)); - CHKiRet(var.ConstructFinalize(pVar)); - - /* always call MsgGetProp() without a template specifier */ - pszProp = (uchar*) MsgGetProp(pThis, NULL, pstrPropName, &bMustBeFreed); - - /* now create a string object out of it and hand that over to the var */ - CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp)); - CHKiRet(var.SetString(pVar, pstrProp)); - - /* finally store var */ - *ppVar = pVar; - -finalize_it: - if(bMustBeFreed) - free(pszProp); - - RETiRet; -} - - -/* This function can be used as a generic way to set properties. - * We have to handle a lot of legacy, so our return value is not always - * 100% correct (called functions do not always provide one, should - * change over time). - * rgerhards, 2008-01-07 - */ -#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1) -rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pThis, msg); - assert(pProp != NULL); - - if(isProp("iProtocolVersion")) { - setProtocolVersion(pThis, pProp->val.num); - } else if(isProp("iSeverity")) { - pThis->iSeverity = pProp->val.num; - } else if(isProp("iFacility")) { - pThis->iFacility = pProp->val.num; - } else if(isProp("msgFlags")) { - pThis->msgFlags = pProp->val.num; - } else if(isProp("pszRawMsg")) { - MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pszMSG")) { - MsgSetMSG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pszUxTradMsg")) { - MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pszTAG")) { - MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pszRcvFrom")) { - MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pszHOSTNAME")) { - MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pCSStrucData")) { - MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pCSAPPNAME")) { - MsgSetAPPNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pCSPROCID")) { - MsgSetPROCID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("pCSMSGID")) { - MsgSetMSGID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); - } else if(isProp("tRcvdAt")) { - memcpy(&pThis->tRcvdAt, &pProp->val.vSyslogTime, sizeof(struct syslogTime)); - } else if(isProp("tTIMESTAMP")) { - memcpy(&pThis->tTIMESTAMP, &pProp->val.vSyslogTime, sizeof(struct syslogTime)); - } - - RETiRet; -} -#undef isProp - - -/* This is a construction finalizer that must be called after all properties - * have been set. It does some final work on the message object. After this - * is done, the object is considered ready for full processing. - * rgerhards, 2008-07-08 - */ -static rsRetVal msgConstructFinalizer(msg_t *pThis) -{ - MsgPrepareEnqueue(pThis); - return RS_RET_OK; -} - - -/* get the severity - this is an entry point that - * satisfies the base object class getSeverity semantics. - * rgerhards, 2008-01-14 - */ -static rsRetVal -MsgGetSeverity(obj_t *pThis, int *piSeverity) -{ - ISOBJ_TYPE_assert(pThis, msg); - assert(piSeverity != NULL); - *piSeverity = ((msg_t*) pThis)->iSeverity; - return RS_RET_OK; -} - - -/* dummy */ -rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } - -/* Initialize the message class. Must be called as the very first method - * before anything else is called inside this class. - * rgerhards, 2008-01-04 - */ -BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE) - /* request objects we use */ - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(datetime, CORE_COMPONENT)); - - /* set our own handlers */ - OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize); - OBJSetMethodHandler(objMethod_SETPROPERTY, MsgSetProperty); - OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, msgConstructFinalizer); - OBJSetMethodHandler(objMethod_GETSEVERITY, MsgGetSeverity); - /* initially, we have no need to lock message objects */ - funcLock = MsgLockingDummy; - funcUnlock = MsgLockingDummy; - funcDeleteMutex = MsgLockingDummy; - funcMsgPrepareEnqueue = MsgLockingDummy; -ENDObjClassInit(msg) - -/* - * vi:set ai: - */ diff --git a/msg.h b/msg.h deleted file mode 100644 index 61feaddb..00000000 --- a/msg.h +++ /dev/null @@ -1,177 +0,0 @@ -/* msg.h - * Header file for all msg-related functions. - * - * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "template.h" /* this is a quirk, but these two are too interdependant... */ - -#ifndef MSG_H_INCLUDED -#define MSG_H_INCLUDED 1 - -#include -#include "obj.h" -#include "syslogd-types.h" -#include "template.h" - -/* rgerhards 2004-11-08: The following structure represents a - * syslog message. - * - * Important Note: - * The message object is used for multiple purposes (once it - * has been created). Once created, it actully is a read-only - * object (though we do not specifically express this). In order - * to avoid multiple copies of the same object, we use a - * reference counter. This counter is set to 1 by the constructer - * and increased by 1 with a call to MsgAddRef(). The destructor - * checks the reference count. If it is more than 1, only the counter - * will be decremented. If it is 1, however, the object is actually - * destroyed. To make this work, it is vital that MsgAddRef() is - * called each time a "copy" is stored somewhere. - */ -struct msg { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ - pthread_mutexattr_t mutAttr; - pthread_mutex_t mut; - int iRefCount; /* reference counter (0 = unused) */ - short bParseHOSTNAME; /* should the hostname be parsed from the message? */ - /* background: the hostname is not present on "regular" messages - * received via UNIX domain sockets from the same machine. However, - * it is available when we have a forwarder (e.g. rfc3195d) using local - * sockets. All in all, the parser would need parse templates, that would - * resolve all these issues... rgerhards, 2005-10-06 - */ - flowControl_t flowCtlType; /**< type of flow control we can apply, for enqueueing, needs not to be persisted because - once data has entered the queue, this property is no longer needed. */ - short iSeverity; /* the severity 0..7 */ - uchar *pszSeverity; /* severity as string... */ - int iLenSeverity; /* ... and its length. */ - uchar *pszSeverityStr; /* severity name... */ - int iLenSeverityStr; /* ... and its length. */ - short iFacility; /* Facility code 0 .. 23*/ - uchar *pszFacility; /* Facility as string... */ - int iLenFacility; /* ... and its length. */ - uchar *pszFacilityStr; /* facility name... */ - int iLenFacilityStr; /* ... and its length. */ - uchar *pszPRI; /* the PRI as a string */ - int iLenPRI; /* and its length */ - uchar *pszRawMsg; /* message as it was received on the - * wire. This is important in case we - * need to preserve cryptographic verifiers. - */ - int iLenRawMsg; /* length of raw message */ - uchar *pszMSG; /* the MSG part itself */ - int iLenMSG; /* Length of the MSG part */ - uchar *pszUxTradMsg; /* the traditional UNIX message */ - int iLenUxTradMsg;/* Length of the traditional UNIX message */ - uchar *pszTAG; /* pointer to tag value */ - int iLenTAG; /* Length of the TAG part */ - uchar *pszHOSTNAME; /* HOSTNAME from syslog message */ - int iLenHOSTNAME; /* Length of HOSTNAME */ - uchar *pszRcvFrom; /* System message was received from */ - int iLenRcvFrom; /* Length of pszRcvFrom */ - short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */ - cstr_t *pCSProgName; /* the (BSD) program name */ - cstr_t *pCSStrucData;/* STRUCTURED-DATA */ - cstr_t *pCSAPPNAME; /* APP-NAME */ - cstr_t *pCSPROCID; /* PROCID */ - cstr_t *pCSMSGID; /* MSGID */ - struct syslogTime tRcvdAt;/* time the message entered this program */ - char *pszRcvdAt3164; /* time as RFC3164 formatted string (always 15 charcters) */ - char *pszRcvdAt3339; /* time as RFC3164 formatted string (32 charcters at most) */ - char *pszRcvdAt_MySQL; /* rcvdAt as MySQL formatted string (always 14 charcters) */ - char *pszRcvdAt_PgSQL; /* rcvdAt as PgSQL formatted string (always 21 characters) */ - struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */ - char *pszTIMESTAMP3164; /* TIMESTAMP as RFC3164 formatted string (always 15 charcters) */ - char *pszTIMESTAMP3339; /* TIMESTAMP as RFC3339 formatted string (32 charcters at most) */ - char *pszTIMESTAMP_MySQL;/* TIMESTAMP as MySQL formatted string (always 14 charcters) */ - char *pszTIMESTAMP_PgSQL;/* TIMESTAMP as PgSQL formatted string (always 21 characters) */ - int msgFlags; /* flags associated with this message */ -}; -typedef struct msg msg_t; /* new name */ - -/* function prototypes - */ -PROTOTYPEObjClassInit(msg); -char* getProgramName(msg_t*); -rsRetVal msgConstruct(msg_t **ppThis); -rsRetVal msgDestruct(msg_t **ppM); -msg_t* MsgDup(msg_t* pOld); -msg_t *MsgAddRef(msg_t *pM); -void setProtocolVersion(msg_t *pM, int iNewVersion); -int getProtocolVersion(msg_t *pM); -char *getProtocolVersionString(msg_t *pM); -int getMSGLen(msg_t *pM); -char *getRawMsg(msg_t *pM); -char *getUxTradMsg(msg_t *pM); -char *getMSG(msg_t *pM); -char *getPRI(msg_t *pM); -int getPRIi(msg_t *pM); -char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt); -char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt); -char *getSeverity(msg_t *pM); -char *getSeverityStr(msg_t *pM); -char *getFacility(msg_t *pM); -char *getFacilityStr(msg_t *pM); -rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME); -char *getAPPNAME(msg_t *pM); -rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID); -int getPROCIDLen(msg_t *pM); -char *getPROCID(msg_t *pM); -rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID); -void MsgAssignTAG(msg_t *pMsg, uchar *pBuf); -void MsgSetTAG(msg_t *pMsg, char* pszTAG); -rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl); -char *getTAG(msg_t *pM); -int getHOSTNAMELen(msg_t *pM); -char *getHOSTNAME(msg_t *pM); -char *getRcvFrom(msg_t *pM); -rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData); -char *getStructuredData(msg_t *pM); -int getProgramNameLen(msg_t *pM); -char *getProgramName(msg_t *pM); -void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom); -void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf); -void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME); -int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg); -void MsgSetMSG(msg_t *pMsg, char* pszMSG); -void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg); -void moveHOSTNAMEtoTAG(msg_t *pM); -char *getMSGID(msg_t *pM); -char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, - cstr_t *pCSPropName, unsigned short *pbMustBeFreed); -char *textpri(char *pRes, size_t pResLen, int pri); -rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar); -rsRetVal MsgEnableThreadSafety(void); - -/* The MsgPrepareEnqueue() function is a macro for performance reasons. - * It needs one global variable to work. This is acceptable, as it gains - * us quite some performance and is fully abstracted using this header file. - * The important thing is that no other module is permitted to actually - * access that global variable! -- rgerhards, 2008-01-05 - */ -extern void (*funcMsgPrepareEnqueue)(msg_t *pMsg); -#define MsgPrepareEnqueue(pMsg) funcMsgPrepareEnqueue(pMsg) - -#endif /* #ifndef MSG_H_INCLUDED */ -/* - * vi:set ai: - */ diff --git a/obj-types.h b/obj-types.h deleted file mode 100644 index 4cd45153..00000000 --- a/obj-types.h +++ /dev/null @@ -1,405 +0,0 @@ -/* Some type definitions and macros for the obj object. - * I needed to move them out of the main obj.h, because obj.h's - * prototypes use other data types. However, their .h's rely - * on some of the obj.h data types and macros. So I needed to break - * that loop somehow and I've done that by moving the typedefs - * into this file here. - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#ifndef OBJ_TYPES_H_INCLUDED -#define OBJ_TYPES_H_INCLUDED - -#include "stringbuf.h" -#include "syslogd-types.h" - -/* property types for obj[De]Serialize() */ -typedef enum { - PROPTYPE_NONE = 0, /* currently no value set */ - PROPTYPE_PSZ = 1, - PROPTYPE_SHORT = 2, - PROPTYPE_INT = 3, - PROPTYPE_LONG = 4, - PROPTYPE_INT64 = 5, - PROPTYPE_CSTR = 6, - PROPTYPE_SYSLOGTIME = 7 -} propType_t; - -typedef unsigned objID_t; - -typedef enum { /* IDs of base methods supported by all objects - used for jump table, so - * they must start at zero and be incremented. -- rgerhards, 2008-01-04 - */ - objMethod_CONSTRUCT = 0, - objMethod_DESTRUCT = 1, - objMethod_SERIALIZE = 2, - objMethod_DESERIALIZE = 3, - objMethod_SETPROPERTY = 4, - objMethod_CONSTRUCTION_FINALIZER = 5, - objMethod_GETSEVERITY = 6, - objMethod_DEBUGPRINT = 7 -} objMethod_t; -#define OBJ_NUM_METHODS 8 /* must be updated to contain the max number of methods supported */ - - -/* the base data type for interfaces - * This MUST be in sync with the ifBEGIN macro - */ -typedef struct interface_s { - int ifVersion; /* must be set to version requested */ - int ifIsLoaded; /* is the interface loaded? (0-no, 1-yes, 2-load failed; if not 1, functions can NOT be called! */ -} interface_t; - - -typedef struct objInfo_s { - uchar *pszID; /* the object ID as a string */ - size_t lenID; /* length of the ID string */ - int iObjVers; - uchar *pszName; - rsRetVal (*objMethods[OBJ_NUM_METHODS])(); - rsRetVal (*QueryIF)(interface_t*); - struct modInfo_s *pModInfo; -} objInfo_t; - - -typedef struct obj { /* the dummy struct that each derived class can be casted to */ - objInfo_t *pObjInfo; -#ifndef NDEBUG /* this means if debug... */ - unsigned int iObjCooCKiE; /* must always be 0xBADEFEE for a valid object */ -#endif - uchar *pszName; /* the name of *this* specific object instance */ -} obj_t; - - -/* macros which must be gloablly-visible (because they are used during definition of - * other objects. - */ -#ifndef NDEBUG /* this means if debug... */ -#include -# define BEGINobjInstance \ - obj_t objData -# define ISOBJ_assert(pObj) \ - do { \ - ASSERT((pObj) != NULL); \ - ASSERT((unsigned) ((obj_t*)(pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \ - } while(0); -# define ISOBJ_TYPE_assert(pObj, objType) \ - do { \ - ASSERT(pObj != NULL); \ - ASSERT((unsigned) ((obj_t*) (pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \ - ASSERT(!strcmp((char*)(((obj_t*)pObj)->pObjInfo->pszID), #objType)); \ - } while(0); -#else /* non-debug mode, no checks but much faster */ -# define BEGINobjInstance obj_t objData -# define ISOBJ_TYPE_assert(pObj, objType) -# define ISOBJ_assert(pObj) -#endif - -#define DEFpropSetMethPTR(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType *pVal)\ - { \ - /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ - pThis->prop = pVal; \ - return RS_RET_OK; \ - } -#define PROTOTYPEpropSetMethPTR(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType*) -#define DEFpropSetMeth(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType pVal)\ - { \ - /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ - pThis->prop = pVal; \ - return RS_RET_OK; \ - } -#define DEFpropSetMethFP(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType)\ - { \ - /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ - pThis->prop = pVal; \ - return RS_RET_OK; \ - } -#define PROTOTYPEpropSetMethFP(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType) -#define DEFpropSetMeth(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType pVal)\ - { \ - /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ - pThis->prop = pVal; \ - return RS_RET_OK; \ - } -#define PROTOTYPEpropSetMeth(obj, prop, dataType)\ - rsRetVal obj##Set##prop(obj##_t *pThis, dataType pVal) -#define INTERFACEpropSetMeth(obj, prop, dataType)\ - rsRetVal (*Set##prop)(obj##_t *pThis, dataType) -/* class initializer */ -#define PROTOTYPEObjClassInit(objName) rsRetVal objName##ClassInit(struct modInfo_s*) -/* below: objName must be the object name (e.g. vm, strm, ...) and ISCORE must be - * 1 if the module is a statically linked core module and 0 if it is a - * dynamically loaded one. -- rgerhards, 2008-02-29 - */ -#define OBJ_IS_CORE_MODULE 1 /* This should better be renamed to something like "OBJ_IS_NOT_LIBHEAD" or so... ;) */ -#define OBJ_IS_LOADABLE_MODULE 0 -#define BEGINObjClassInit(objName, objVers, objType) \ -rsRetVal objName##ClassInit(struct modInfo_s *pModInfo) \ -{ \ - DEFiRet; \ - if(objType == OBJ_IS_CORE_MODULE) { /* are we a core module? */ \ - CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ \ - } \ - CHKiRet(obj.InfoConstruct(&pObjInfoOBJ, (uchar*) #objName, objVers, \ - (rsRetVal (*)(void*))objName##Construct,\ - (rsRetVal (*)(void*))objName##Destruct,\ - (rsRetVal (*)(interface_t*))objName##QueryInterface, pModInfo)); \ - -#define ENDObjClassInit(objName) \ - iRet = obj.RegisterObj((uchar*)#objName, pObjInfoOBJ); \ -finalize_it: \ - RETiRet; \ -} - -/* ... and now the same for abstract classes. - * TODO: consolidate the two -- rgerhards, 2008-02-29 - */ -#define BEGINAbstractObjClassInit(objName, objVers, objType) \ -rsRetVal objName##ClassInit(struct modInfo_s *pModInfo) \ -{ \ - DEFiRet; \ - if(objType == OBJ_IS_CORE_MODULE) { /* are we a core module? */ \ - CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ \ - } \ - CHKiRet(obj.InfoConstruct(&pObjInfoOBJ, (uchar*) #objName, objVers, \ - NULL,\ - NULL,\ - (rsRetVal (*)(interface_t*))objName##QueryInterface, pModInfo)); - -#define ENDObjClassInit(objName) \ - iRet = obj.RegisterObj((uchar*)#objName, pObjInfoOBJ); \ -finalize_it: \ - RETiRet; \ -} - - -/* now come the class exit. This is to be called immediately before the class is - * unloaded (actual unload for plugins, program termination for core modules) - * gerhards, 2008-03-10 - */ -#define PROTOTYPEObjClassExit(objName) rsRetVal objName##ClassExit(void) -#define BEGINObjClassExit(objName, objType) \ -rsRetVal objName##ClassExit(void) \ -{ \ - DEFiRet; - -#define CODESTARTObjClassExit(objName) - -#define ENDObjClassExit(objName) \ - iRet = obj.UnregisterObj((uchar*)#objName); \ - RETiRet; \ -} - -/* this defines both the constructor and initializer - * rgerhards, 2008-01-10 - */ -#define BEGINobjConstruct(obj) \ - rsRetVal obj##Initialize(obj##_t __attribute__((unused)) *pThis) \ - { \ - DEFiRet; - -#define ENDobjConstruct(obj) \ - /* use finalize_it: before calling the macro (if you need it)! */ \ - RETiRet; \ - } \ - rsRetVal obj##Construct(obj##_t **ppThis) \ - { \ - DEFiRet; \ - obj##_t *pThis; \ - \ - ASSERT(ppThis != NULL); \ - \ - if((pThis = (obj##_t *)calloc(1, sizeof(obj##_t))) == NULL) { \ - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); \ - } \ - objConstructSetObjInfo(pThis); \ - \ - obj##Initialize(pThis); \ - \ - finalize_it: \ - OBJCONSTRUCT_CHECK_SUCCESS_AND_CLEANUP \ - RETiRet; \ - } - - -/* this defines the destructor. The important point is that the base object - * destructor is called. The upper-level class shall destruct all of its - * properties, but not the instance itself. This is freed here by the - * framework (we need an intact pointer because we need to free the - * obj_t structures inside it). A pointer to the object pointer must be - * parse, because it is re-set to NULL (this, for example, is important in - * cancellation handlers). The object pointer is always named pThis. - * The object is always freed, even if there is some error while - * Cancellation is blocked during destructors, as this could have fatal - * side-effects. However, this also means the upper-level object should - * not perform any lenghty processing. - * IMPORTANT: if the upper level object requires some situations where the - * object shall not be destructed (e.g. via reference counting), then - * it shall set pThis to NULL, which prevents destruction of the - * object. - * processing. - * rgerhards, 2008-01-30 - */ -#define BEGINobjDestruct(OBJ) \ - rsRetVal OBJ##Destruct(OBJ##_t **ppThis) \ - { \ - DEFiRet; \ - int iCancelStateSave; \ - OBJ##_t *pThis; - -#define CODESTARTobjDestruct(OBJ) \ - ASSERT(ppThis != NULL); \ - pThis = *ppThis; \ - ISOBJ_TYPE_assert(pThis, OBJ); \ - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); - -#define ENDobjDestruct(OBJ) \ - goto finalize_it; /* prevent compiler warning ;) */ \ - /* no more code here! */ \ - finalize_it: \ - if(pThis != NULL) { \ - obj.DestructObjSelf((obj_t*) pThis); \ - free(pThis); \ - *ppThis = NULL; \ - } \ - pthread_setcancelstate(iCancelStateSave, NULL); \ - RETiRet; \ - } - - -/* this defines the debug print entry point. DebugPrint is optional. If - * it is provided, the object should output some meaningful information - * via the debug system. - * rgerhards, 2008-02-20 - */ -#define PROTOTYPEObjDebugPrint(obj) rsRetVal obj##DebugPrint(obj##_t *pThis) -#define INTERFACEObjDebugPrint(obj) rsRetVal (*DebugPrint)(obj##_t *pThis) -#define BEGINobjDebugPrint(obj) \ - rsRetVal obj##DebugPrint(obj##_t *pThis) \ - { \ - DEFiRet; \ - -#define CODESTARTobjDebugPrint(obj) \ - ASSERT(pThis != NULL); \ - ISOBJ_TYPE_assert(pThis, obj); \ - -#define ENDobjDebugPrint(obj) \ - RETiRet; \ - } - -/* ------------------------------ object loader system ------------------------------ * - * The following code is the early beginning of a dynamic object loader system. The - * root idea is that all objects will become dynamically loadable libraries over time, - * which is necessary to get a clean plug-in interface where every plugin can access - * rsyslog's rich object model via simple and quite portable methods. - * - * To do so, each object defines one or more interfaces. They are essentially structures - * with function (method) pointers. Anyone interested in calling an object must first - * obtain the interface and can then call through it. - * - * The interface data type must always be called _if_t, as this is expected - * by the macros. Having consitent naming is also easier for the programmer. By default, - * macros create a static variable named like the object in each calling objects - * static data block. - * - * To facilitate moving to this system, I begin to implement some hooks, which - * allows to use interfaces today (when the rest of the infrastructure is not yet - * there). This is in the hope that it will ease migration to the full-fledged system - * once we are ready to work on that. - * rgerhards, 2008-02-21 - */ - -/* this defines the QueryInterface print entry point. Over time, it should be - * present in all objects. - */ -//#define PROTOTYPEObjQueryInterface(obj) rsRetVal obj##QueryInterface(obj##_if_t *pThis) -#define BEGINobjQueryInterface(obj) \ - rsRetVal obj##QueryInterface(obj##_if_t *pIf) \ - { \ - DEFiRet; \ - -#define CODESTARTobjQueryInterface(obj) \ - ASSERT(pIf != NULL); - -#define ENDobjQueryInterface(obj) \ - RETiRet; \ - } - - -/* the following macros should be used to define interfaces inside the - * header files. - */ -#define BEGINinterface(obj) \ - typedef struct obj##_if_s {\ - ifBEGIN; /* This MUST always be the first interface member */ -#define ENDinterface(obj) \ - } obj##_if_t; - -/* the following macro is used to get access to an object (not an instance, - * just the class itself!). It must be called before any of the object's - * methods can be accessed. The MYLIB part is the name of my library, or NULL if - * the caller is a core module. Using the right value here is important to get - * the reference counting correct (object accesses from the same library must - * not be counted because that would cause a library plugin to never unload, as - * its ClassExit() entry points are only called if no object is referenced, which - * would never happen as the library references itself. - * rgerhards, 2008-03-11 - */ -#define CORE_COMPONENT NULL /* use this to indicate this is a core component */ -#define DONT_LOAD_LIB NULL /* do not load a library to obtain object interface (currently same as CORE_COMPONENT) */ -/*#define objUse(objName, MYLIB, FILENAME) \ - obj.UseObj(__FILE__, (uchar*)#objName, MYLIB, (uchar*)FILENAME, (void*) &objName) -*/ -#define objUse(objName, FILENAME) \ - obj.UseObj(__FILE__, (uchar*)#objName, (uchar*)FILENAME, (void*) &objName) -#define objRelease(objName, FILENAME) \ - obj.ReleaseObj(__FILE__, (uchar*)#objName, (uchar*) FILENAME, (void*) &objName) - -/* defines data that must always be present at the very begin of the interface structure */ -#define ifBEGIN \ - int ifVersion; /* must be set to version requested */ \ - int ifIsLoaded; /* is the interface loaded? (0-no, 1-yes; if no, functions can NOT be called! */ - - -/* use the following define some place in your static data (suggested right at - * the beginning - */ -#define DEFobjCurrIf(obj) \ - static obj##_if_t obj = { .ifVersion = obj##CURR_IF_VERSION, .ifIsLoaded = 0 }; - -/* define the prototypes for a class - when we use interfaces, we just have few - * functions that actually need to be non-static. - */ -#define PROTOTYPEObj(obj) \ - PROTOTYPEObjClassInit(obj); \ - PROTOTYPEObjClassExit(obj); - -/* ------------------------------ end object loader system ------------------------------ */ - - -#include "modules.h" -#endif /* #ifndef OBJ_TYPES_H_INCLUDED */ diff --git a/obj.c b/obj.c deleted file mode 100644 index 7a4435ea..00000000 --- a/obj.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* obj.c - * - * This file implements a generic object "class". All other classes can - * use the service of this base class here to include auto-destruction and - * other capabilities in a generic manner. - * - * As of 2008-02-29, I (rgerhards) am adding support for dynamically loadable - * objects. In essence, each object will soon be available via its interface, - * only. Before any object's code is accessed (including global static methods), - * the caller needs to obtain an object interface. To do so, it needs to provide - * the object name and the file where the object is expected to reside in. A - * file may not be given, in which case the object is expected to reside in - * the rsyslog core. The caller than receives an interface pointer which can - * be utilized to access all the object's methods. This method enables rsyslog - * to load library modules on demand. In order to keep overhead low, callers - * should request object interface only once in the object Init function and - * free them when they exit. The only exception is when a caller needs to - * access an object only conditional, in which case a pointer to its interface - * shall be aquired as need first arises but still be released only on exit - * or when there definitely is no further need. The whole idea is to limit - * the very performance-intense act of dynamically loading an objects library. - * Of course, it is possible to violate this suggestion, but than you should - * have very good reasoning to do so. - * - * Please note that there is one trick we need to do. Each object queries - * the object interfaces and it does so via objUse(). objUse, however, is - * part of the obj object's interface (implemented via the file you are - * just reading). So in order to obtain a pointer to objUse, we need to - * call it - obviously not possible. One solution would be that objUse is - * hardcoded into all callers. That, however, would bring us into slight - * trouble with actually dynamically loaded modules, as we should NOT - * rely on the OS loader to resolve symbols back to the caller (this - * is a feature not universally available and highly importable). Of course, - * we can solve this with a pHostQueryEtryPoint() call. It still sounds - * somewhat unnatural to call a regular interface function via a special - * method. So what we do instead is define a special function called - * objGetObjInterface() which delivers our own interface. That function - * than will be defined global and be queriable via pHostQueryEtryPoint(). - * I agree, technically this is much the same, but from an architecture - * point of view it looks cleaner (at least to me). - * - * Please note that there is another egg-hen problem: we use a linked list, - * which is provided by the linkedList object. However, we need to - * initialize the linked list before we can provide the UseObj() - * functionality. That, in turn, would probably be required by the - * linkedList object. So the solution is to use a backdoor just to - * init the linked list and from then on use the usual interfaces. - * - * File begun on 2008-01-04 by RGerhards - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" - -#include -#include -#include -#include -#include - -/* how many objects are supported by rsyslogd? */ -#define OBJ_NUM_IDS 100 /* TODO change to a linked list? info: 16 were currently in use 2008-02-29 */ - -#include "rsyslog.h" -#include "syslogd-types.h" -#include "srUtils.h" -#include "obj.h" -#include "stream.h" -#include "modules.h" -#include "errmsg.h" -#include "cfsysline.h" - -/* static data */ -DEFobjCurrIf(obj) /* we define our own interface, as this is expected by some macros! */ -DEFobjCurrIf(var) -DEFobjCurrIf(module) -DEFobjCurrIf(errmsg) -static objInfo_t *arrObjInfo[OBJ_NUM_IDS]; /* array with object information pointers */ - - -/* cookies for serialized lines */ -#define COOKIE_OBJLINE '<' -#define COOKIE_PROPLINE '+' -#define COOKIE_ENDLINE '>' -#define COOKIE_BLANKLINE '.' - -/* forward definitions */ -static rsRetVal FindObjInfo(cstr_t *pszObjName, objInfo_t **ppInfo); - -/* methods */ - -/* This is a dummy method to be used when a standard method has not been - * implemented by an object. Having it allows us to simply call via the - * jump table without any NULL pointer checks - which gains quite - * some performance. -- rgerhards, 2008-01-04 - */ -static rsRetVal objInfoNotImplementedDummy(void __attribute__((unused)) *pThis) -{ - return RS_RET_NOT_IMPLEMENTED; -} - -/* and now the macro to check if something is not implemented - * must be provided an objInfo_t pointer. - */ -#define objInfoIsImplemented(pThis, method) \ - (pThis->objMethods[method] != objInfoNotImplementedDummy) - -/* construct an object Info object. Each class shall do this on init. The - * resulting object shall be cached during the lifetime of the class and each - * object shall receive a reference. A constructor and destructor MUST be provided for all - * objects, thus they are in the parameter list. - * pszID is the identifying object name and must point to constant pool memory. It is never freed. - */ -static rsRetVal -InfoConstruct(objInfo_t **ppThis, uchar *pszID, int iObjVers, - rsRetVal (*pConstruct)(void *), rsRetVal (*pDestruct)(void *), - rsRetVal (*pQueryIF)(interface_t*), modInfo_t *pModInfo) -{ - DEFiRet; - int i; - objInfo_t *pThis; - - assert(ppThis != NULL); - - if((pThis = calloc(1, sizeof(objInfo_t))) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - - pThis->pszID = pszID; - pThis->lenID = strlen((char*)pszID); - pThis->pszName = (uchar*)strdup((char*)pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */ - pThis->iObjVers = iObjVers; - pThis->QueryIF = pQueryIF; - pThis->pModInfo = pModInfo; - - pThis->objMethods[0] = pConstruct; - pThis->objMethods[1] = pDestruct; - for(i = 2 ; i < OBJ_NUM_METHODS ; ++i) { - pThis->objMethods[i] = objInfoNotImplementedDummy; - } - - *ppThis = pThis; - -finalize_it: - RETiRet; -} - - -/* destruct the objInfo object - must be done only when no more instances exist. - * rgerhards, 2008-03-10 - */ -static rsRetVal -InfoDestruct(objInfo_t **ppThis) -{ - DEFiRet; - objInfo_t *pThis; - - assert(ppThis != NULL); - pThis = *ppThis; - assert(pThis != NULL); - - if(pThis->pszName != NULL) - free(pThis->pszName); - free(pThis); - *ppThis = NULL; - - RETiRet; -} - - -/* set a method handler */ -static rsRetVal -InfoSetMethod(objInfo_t *pThis, objMethod_t objMethod, rsRetVal (*pHandler)(void*)) -{ - assert(pThis != NULL); - assert(objMethod > 0 && objMethod < OBJ_NUM_METHODS); - pThis->objMethods[objMethod] = pHandler; - - return RS_RET_OK; -} - -/* destruct the base object properties. - * rgerhards, 2008-01-29 - */ -static rsRetVal -DestructObjSelf(obj_t *pThis) -{ - DEFiRet; - - ISOBJ_assert(pThis); - if(pThis->pszName != NULL) { - free(pThis->pszName); - } - - RETiRet; -} - - -/* --------------- object serializiation / deserialization support --------------- */ - - -/* serialize the header of an object - * pszRecType must be either "Obj" (Object) or "OPB" (Object Property Bag) - */ -static rsRetVal objSerializeHeader(strm_t *pStrm, obj_t *pObj, uchar *pszRecType) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pStrm, strm); - ISOBJ_assert(pObj); - assert(!strcmp((char*) pszRecType, "Obj") || !strcmp((char*) pszRecType, "OPB")); - - /* object cookie and serializer version (so far always 1) */ - CHKiRet(strmWriteChar(pStrm, COOKIE_OBJLINE)); - CHKiRet(strmWrite(pStrm, (uchar*) pszRecType, 3)); /* record types are always 3 octets */ - CHKiRet(strmWriteChar(pStrm, ':')); - CHKiRet(strmWriteChar(pStrm, '1')); - - /* object type, version and string length */ - CHKiRet(strmWriteChar(pStrm, ':')); - CHKiRet(strmWrite(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)); - CHKiRet(strmWriteChar(pStrm, ':')); - CHKiRet(strmWriteLong(pStrm, objGetVersion(pObj))); - - /* record trailer */ - CHKiRet(strmWriteChar(pStrm, ':')); - CHKiRet(strmWriteChar(pStrm, '\n')); - -finalize_it: - RETiRet; -} - - -/* begin serialization of an object - * rgerhards, 2008-01-06 - */ -static rsRetVal -BeginSerialize(strm_t *pStrm, obj_t *pObj) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pStrm, strm); - ISOBJ_assert(pObj); - - CHKiRet(strmRecordBegin(pStrm)); - CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "Obj")); - -finalize_it: - RETiRet; -} - - -/* begin serialization of an object's property bag - * Note: a property bag is used to serialize some of an objects - * properties, but not necessarily all. A good example is the queue - * object, which at some stage needs to serialize a number of its - * properties, but not the queue data itself. From the object point - * of view, a property bag can not be used to re-instantiate an object. - * Otherwise, the serialization is exactly the same. - * rgerhards, 2008-01-11 - */ -static rsRetVal -BeginSerializePropBag(strm_t *pStrm, obj_t *pObj) -{ - DEFiRet; - - ISOBJ_TYPE_assert(pStrm, strm); - ISOBJ_assert(pObj); - - CHKiRet(strmRecordBegin(pStrm)); - CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "OPB")); - -finalize_it: - RETiRet; -} - - -/* append a property - */ -static rsRetVal -SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr) -{ - DEFiRet; - uchar *pszBuf = NULL; - size_t lenBuf = 0; - uchar szBuf[64]; - varType_t vType = VARTYPE_NONE; - - ISOBJ_TYPE_assert(pStrm, strm); - assert(pszPropName != NULL); - - /*dbgprintf("objSerializeProp: strm %p, propName '%s', type %d, pUsr %p\n", pStrm, pszPropName, propType, pUsr);*/ - /* if we have no user pointer, there is no need to write this property. - * TODO: think if that's the righ point of view - * rgerhards, 2008-01-06 - */ - if(pUsr == NULL) { - ABORT_FINALIZE(RS_RET_OK); - } - - /* TODO: use the stream functions for data conversion here - should be quicker */ - - switch(propType) { - case PROPTYPE_PSZ: - pszBuf = (uchar*) pUsr; - lenBuf = strlen((char*) pszBuf); - vType = VARTYPE_STR; - break; - case PROPTYPE_SHORT: - CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((short*) pUsr))); - pszBuf = szBuf; - lenBuf = strlen((char*) szBuf); - vType = VARTYPE_NUMBER; - break; - case PROPTYPE_INT: - CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((int*) pUsr))); - pszBuf = szBuf; - lenBuf = strlen((char*) szBuf); - vType = VARTYPE_NUMBER; - break; - case PROPTYPE_LONG: - CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((long*) pUsr))); - pszBuf = szBuf; - lenBuf = strlen((char*) szBuf); - vType = VARTYPE_NUMBER; - break; - case PROPTYPE_INT64: - CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((int64*) pUsr))); - pszBuf = szBuf; - lenBuf = strlen((char*) szBuf); - vType = VARTYPE_NUMBER; - break; - case PROPTYPE_CSTR: - pszBuf = rsCStrGetSzStrNoNULL((cstr_t *) pUsr); - lenBuf = rsCStrLen((cstr_t*) pUsr); - vType = VARTYPE_STR; - break; - case PROPTYPE_SYSLOGTIME: - lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%d:%d:%d:%d:%d:%d:%d:%d:%d:%c:%d:%d", - ((syslogTime_t*)pUsr)->timeType, - ((syslogTime_t*)pUsr)->year, - ((syslogTime_t*)pUsr)->month, - ((syslogTime_t*)pUsr)->day, - ((syslogTime_t*)pUsr)->hour, - ((syslogTime_t*)pUsr)->minute, - ((syslogTime_t*)pUsr)->second, - ((syslogTime_t*)pUsr)->secfrac, - ((syslogTime_t*)pUsr)->secfracPrecision, - ((syslogTime_t*)pUsr)->OffsetMode, - ((syslogTime_t*)pUsr)->OffsetHour, - ((syslogTime_t*)pUsr)->OffsetMinute); - if(lenBuf > sizeof(szBuf) - 1) - ABORT_FINALIZE(RS_RET_PROVIDED_BUFFER_TOO_SMALL); - vType = VARTYPE_SYSLOGTIME; - pszBuf = szBuf; - break; - default: - dbgprintf("invalid PROPTYPE %d\n", propType); - break; - } - - /* cookie */ - CHKiRet(strmWriteChar(pStrm, COOKIE_PROPLINE)); - /* name */ - CHKiRet(strmWrite(pStrm, pszPropName, strlen((char*)pszPropName))); - CHKiRet(strmWriteChar(pStrm, ':')); - /* type */ - CHKiRet(strmWriteLong(pStrm, (int) vType)); - CHKiRet(strmWriteChar(pStrm, ':')); - /* length */ - CHKiRet(strmWriteLong(pStrm, lenBuf)); - CHKiRet(strmWriteChar(pStrm, ':')); - - /* data */ - CHKiRet(strmWrite(pStrm, (uchar*) pszBuf, lenBuf)); - - /* trailer */ - CHKiRet(strmWriteChar(pStrm, ':')); - CHKiRet(strmWriteChar(pStrm, '\n')); - -finalize_it: - RETiRet; -} - - -/* end serialization of an object. The caller receives a - * standard C string, which he must free when no longer needed. - */ -static rsRetVal -EndSerialize(strm_t *pStrm) -{ - DEFiRet; - - assert(pStrm != NULL); - - CHKiRet(strmWriteChar(pStrm, COOKIE_ENDLINE)); - CHKiRet(strmWrite(pStrm, (uchar*) "End\n", sizeof("END\n") - 1)); - CHKiRet(strmWriteChar(pStrm, COOKIE_BLANKLINE)); - CHKiRet(strmWriteChar(pStrm, '\n')); - - CHKiRet(strmRecordEnd(pStrm)); - -finalize_it: - RETiRet; -} - - -/* define a helper to make code below a bit cleaner (and quicker to write) */ -#define NEXTC CHKiRet(strmReadChar(pStrm, &c))//;dbgprintf("c: %c\n", c); - - -/* de-serialize an embedded, non-octect-counted string. This is useful - * for deserializing the object name inside the header. The string is - * terminated by the first occurence of the ':' character. - * rgerhards, 2008-02-29 - */ -static rsRetVal -objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm) -{ - DEFiRet; - uchar c; - cstr_t *pStr = NULL; - - assert(ppStr != NULL); - - CHKiRet(rsCStrConstruct(&pStr)); - - NEXTC; - while(c != ':') { - CHKiRet(rsCStrAppendChar(pStr, c)); - NEXTC; - } - CHKiRet(rsCStrFinish(pStr)); - - *ppStr = pStr; - -finalize_it: - if(iRet != RS_RET_OK && pStr != NULL) - rsCStrDestruct(&pStr); - - RETiRet; -} - - -/* de-serialize a number */ -static rsRetVal objDeserializeNumber(number_t *pNum, strm_t *pStrm) -{ - DEFiRet; - number_t i; - int bIsNegative; - uchar c; - - assert(pNum != NULL); - - NEXTC; - if(c == '-') { - bIsNegative = 1; - NEXTC; - } else { - bIsNegative = 0; - } - - /* we check this so that we get more meaningful error codes */ - if(!isdigit(c)) ABORT_FINALIZE(RS_RET_INVALID_NUMBER); - - i = 0; - while(isdigit(c)) { - i = i * 10 + c - '0'; - NEXTC; - } - - if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER); - - if(bIsNegative) - i *= -1; - - *pNum = i; -finalize_it: - RETiRet; -} - - -/* de-serialize a string, length must be provided but may be 0 */ -static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm) -{ - DEFiRet; - int i; - uchar c; - cstr_t *pCStr = NULL; - - assert(ppCStr != NULL); - assert(iLen >= 0); - - CHKiRet(rsCStrConstruct(&pCStr)); - - NEXTC; - for(i = 0 ; i < iLen ; ++i) { - CHKiRet(rsCStrAppendChar(pCStr, c)); - NEXTC; - } - CHKiRet(rsCStrFinish(pCStr)); - - /* check terminator */ - if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER); - - *ppCStr = pCStr; - -finalize_it: - if(iRet != RS_RET_OK && pCStr != NULL) - rsCStrDestruct(&pCStr); - - RETiRet; -} - - -/* de-serialize a syslogTime -- rgerhards,2008-01-08 */ -#define GETVAL(var) \ - CHKiRet(objDeserializeNumber(&l, pStrm)); \ - pTime->var = l; -static rsRetVal objDeserializeSyslogTime(syslogTime_t *pTime, strm_t *pStrm) -{ - DEFiRet; - number_t l; - uchar c; - - assert(pTime != NULL); - - GETVAL(timeType); - GETVAL(year); - GETVAL(month); - GETVAL(day); - GETVAL(hour); - GETVAL(minute); - GETVAL(second); - GETVAL(secfrac); - GETVAL(secfracPrecision); - /* OffsetMode is a single character! */ - NEXTC; pTime->OffsetMode = c; - NEXTC; if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER); - GETVAL(OffsetHour); - GETVAL(OffsetMinute); - -finalize_it: - RETiRet; -} -#undef GETVAL - -/* de-serialize an object header - * rgerhards, 2008-01-07 - */ -static rsRetVal objDeserializeHeader(uchar *pszRecType, cstr_t **ppstrID, int* poVers, strm_t *pStrm) -{ - DEFiRet; - number_t oVers; - uchar c; - - assert(ppstrID != NULL); - assert(poVers != NULL); - assert(!strcmp((char*) pszRecType, "Obj") || !strcmp((char*) pszRecType, "OPB")); - - /* check header cookie */ - NEXTC; if(c != COOKIE_OBJLINE) ABORT_FINALIZE(RS_RET_INVALID_HEADER); - NEXTC; if(c != pszRecType[0]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE); - NEXTC; if(c != pszRecType[1]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE); - NEXTC; if(c != pszRecType[2]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE); - NEXTC; if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_HEADER); - NEXTC; if(c != '1') ABORT_FINALIZE(RS_RET_INVALID_HEADER_VERS); - NEXTC; if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_HEADER_VERS); - - /* object type and version */ - CHKiRet(objDeserializeEmbedStr(ppstrID, pStrm)); - CHKiRet(objDeserializeNumber(&oVers, pStrm)); - - /* and now we skip over the rest until the delemiting \n */ - NEXTC; - while(c != '\n') { - NEXTC; - } - - *poVers = oVers; - -finalize_it: - RETiRet; -} - - -/* Deserialize a single property. Pointer must be positioned at begin of line. Whole line - * up until the \n is read. - */ -static rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm) -{ - DEFiRet; - number_t i; - number_t iLen; - uchar c; - - assert(pProp != NULL); - - /* check cookie */ - NEXTC; - if(c != COOKIE_PROPLINE) { - /* oops, we've read one char that does not belong to use - unget it first */ - CHKiRet(strmUnreadChar(pStrm, c)); - ABORT_FINALIZE(RS_RET_NO_PROPLINE); - } - - /* get the property name first */ - CHKiRet(rsCStrConstruct(&pProp->pcsName)); - - NEXTC; - while(c != ':') { - CHKiRet(rsCStrAppendChar(pProp->pcsName, c)); - NEXTC; - } - CHKiRet(rsCStrFinish(pProp->pcsName)); - - /* property type */ - CHKiRet(objDeserializeNumber(&i, pStrm)); - pProp->varType = i; - - /* size (needed for strings) */ - CHKiRet(objDeserializeNumber(&iLen, pStrm)); - - /* we now need to deserialize the value */ - switch(pProp->varType) { - case VARTYPE_STR: - CHKiRet(objDeserializeStr(&pProp->val.pStr, iLen, pStrm)); - break; - case VARTYPE_NUMBER: - CHKiRet(objDeserializeNumber(&pProp->val.num, pStrm)); - break; - case VARTYPE_SYSLOGTIME: - CHKiRet(objDeserializeSyslogTime(&pProp->val.vSyslogTime, pStrm)); - break; - default: - dbgprintf("invalid VARTYPE %d\n", pProp->varType); - break; - } - - /* we should now be at the end of the line. So the next char must be \n */ - NEXTC; - if(c != '\n') ABORT_FINALIZE(RS_RET_INVALID_PROPFRAME); - -finalize_it: - RETiRet; -} - - -/* de-serialize an object trailer. This does not get any data but checks if the - * format is ok. - * rgerhards, 2008-01-07 - */ -static rsRetVal objDeserializeTrailer(strm_t *pStrm) -{ - DEFiRet; - uchar c; - - /* check header cookie */ - NEXTC; if(c != COOKIE_ENDLINE) ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - NEXTC; if(c != 'E') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - NEXTC; if(c != 'n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - NEXTC; if(c != 'd') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - NEXTC; if(c != '\n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - NEXTC; if(c != COOKIE_BLANKLINE) ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - NEXTC; if(c != '\n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); - -finalize_it: - RETiRet; -} - - - -/* This method tries to recover a serial store if it got out of sync. - * To do so, it scans the line beginning cookies and waits for the object - * cookie. If that is found, control is returned. If the store is exhausted, - * we will receive an RS_RET_EOF error as part of NEXTC, which will also - * terminate this function. So we may either return with somehting that - * looks like a valid object or end of store. - * rgerhards, 2008-01-07 - */ -static rsRetVal objDeserializeTryRecover(strm_t *pStrm) -{ - DEFiRet; - uchar c; - int bWasNL; - int bRun; - - assert(pStrm != NULL); - bRun = 1; - bWasNL = 0; - - while(bRun) { - NEXTC; - if(c == '\n') - bWasNL = 1; - else { - if(bWasNL == 1 && c == COOKIE_OBJLINE) - bRun = 0; /* we found it! */ - else - bWasNL = 0; - } - } - - CHKiRet(strmUnreadChar(pStrm, c)); - -finalize_it: - dbgprintf("deserializer has possibly been able to re-sync and recover, state %d\n", iRet); - RETiRet; -} - - -/* De-serialize the properties of an object. This includes processing - * of the trailer. Header must already have been processed. - * rgerhards, 2008-01-11 - */ -static rsRetVal objDeserializeProperties(obj_t *pObj, objInfo_t *pObjInfo, strm_t *pStrm) -{ - DEFiRet; - var_t *pVar = NULL; - - ISOBJ_assert(pObj); - ISOBJ_TYPE_assert(pStrm, strm); - ASSERT(pObjInfo != NULL); - - CHKiRet(var.Construct(&pVar)); - CHKiRet(var.ConstructFinalize(pVar)); - - iRet = objDeserializeProperty(pVar, pStrm); - while(iRet == RS_RET_OK) { - CHKiRet(pObjInfo->objMethods[objMethod_SETPROPERTY](pObj, pVar)); - /* re-init var object - TODO: method of var! */ - rsCStrDestruct(&pVar->pcsName); /* no longer needed */ - if(pVar->varType == VARTYPE_STR) { - if(pVar->val.pStr != NULL) - rsCStrDestruct(&pVar->val.pStr); - } - iRet = objDeserializeProperty(pVar, pStrm); - } - - if(iRet != RS_RET_NO_PROPLINE) - FINALIZE; - - CHKiRet(objDeserializeTrailer(pStrm)); /* do trailer checks */ -finalize_it: - if(pVar != NULL) - var.Destruct(&pVar); - - RETiRet; -} - - -/* De-Serialize an object. - * Params: Pointer to object Pointer (pObj) (like a obj_t**, but can not do that due to compiler warning) - * expected object ID (to check against), a fixup function that can modify the object before it is finalized - * and a user pointer that is to be passed to that function in addition to the object. The fixup function - * pointer may be NULL, in which case none is called. - * The caller must destruct the created object. - * rgerhards, 2008-01-07 - */ -static rsRetVal -Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixup)(obj_t*,void*), void *pUsr) -{ - DEFiRet; - rsRetVal iRetLocal; - obj_t *pObj = NULL; - int oVers = 0; /* after all, it is totally useless but takes up some execution time... */ - cstr_t *pstrID = NULL; - objInfo_t *pObjInfo; - - assert(ppObj != NULL); - assert(pszTypeExpected != NULL); - ISOBJ_TYPE_assert(pStrm, strm); - - /* we de-serialize the header. if all goes well, we are happy. However, if - * we experience a problem, we try to recover. We do this by skipping to - * the next object header. This is defined via the line-start cookies. In - * worst case, we exhaust the queue, but then we receive EOF return state, - * from objDeserializeTryRecover(), what will cause us to ultimately give up. - * rgerhards, 2008-07-08 - */ - do { - iRetLocal = objDeserializeHeader((uchar*) "Obj", &pstrID, &oVers, pStrm); - if(iRetLocal != RS_RET_OK) { - dbgprintf("objDeserialize error %d during header processing - trying to recover\n", iRetLocal); - CHKiRet(objDeserializeTryRecover(pStrm)); - } - } while(iRetLocal != RS_RET_OK); - - if(rsCStrSzStrCmp(pstrID, pszTypeExpected, strlen((char*)pszTypeExpected))) // TODO: optimize strlen() - caller shall provide - ABORT_FINALIZE(RS_RET_INVALID_OID); - - CHKiRet(FindObjInfo(pstrID, &pObjInfo)); - - CHKiRet(pObjInfo->objMethods[objMethod_CONSTRUCT](&pObj)); - - /* we got the object, now we need to fill the properties */ - CHKiRet(objDeserializeProperties(pObj, pObjInfo, pStrm)); - - /* check if we need to call a fixup function that modifies the object - * before it is finalized. -- rgerhards, 2008-01-13 - */ - if(fFixup != NULL) - CHKiRet(fFixup(pObj, pUsr)); - - /* we have a valid object, let's finalize our work and return */ - if(objInfoIsImplemented(pObjInfo, objMethod_CONSTRUCTION_FINALIZER)) - CHKiRet(pObjInfo->objMethods[objMethod_CONSTRUCTION_FINALIZER](pObj)); - - *((obj_t**) ppObj) = pObj; - -finalize_it: - if(iRet != RS_RET_OK && pObj != NULL) - free(pObj); // TODO: check if we can call destructor 2008-01-13 rger - - if(pstrID != NULL) - rsCStrDestruct(&pstrID); - - RETiRet; -} - - -/* De-Serialize an object, but treat it as property bag. - * rgerhards, 2008-01-11 - */ -rsRetVal -objDeserializeObjAsPropBag(obj_t *pObj, strm_t *pStrm) -{ - DEFiRet; - rsRetVal iRetLocal; - cstr_t *pstrID = NULL; - int oVers = 0; /* after all, it is totally useless but takes up some execution time... */ - objInfo_t *pObjInfo; - - ISOBJ_assert(pObj); - ISOBJ_TYPE_assert(pStrm, strm); - - /* we de-serialize the header. if all goes well, we are happy. However, if - * we experience a problem, we try to recover. We do this by skipping to - * the next object header. This is defined via the line-start cookies. In - * worst case, we exhaust the queue, but then we receive EOF return state - * from objDeserializeTryRecover(), what will cause us to ultimately give up. - * rgerhards, 2008-07-08 - */ - do { - iRetLocal = objDeserializeHeader((uchar*) "Obj", &pstrID, &oVers, pStrm); - if(iRetLocal != RS_RET_OK) { - dbgprintf("objDeserializeObjAsPropBag error %d during header - trying to recover\n", iRetLocal); - CHKiRet(objDeserializeTryRecover(pStrm)); - } - } while(iRetLocal != RS_RET_OK); - - if(rsCStrSzStrCmp(pstrID, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)) - ABORT_FINALIZE(RS_RET_INVALID_OID); - - CHKiRet(FindObjInfo(pstrID, &pObjInfo)); - - /* we got the object, now we need to fill the properties */ - CHKiRet(objDeserializeProperties(pObj, pObjInfo, pStrm)); - -finalize_it: - if(pstrID != NULL) - rsCStrDestruct(&pstrID); - - RETiRet; -} - - - -/* De-Serialize an object property bag. As a property bag contains only partial properties, - * it is not instanciable. Thus, the caller must provide a pointer of an already-instanciated - * object of the correct type. - * Params: Pointer to object (pObj) - * Pointer to be passed to the function - * The caller must destruct the created object. - * rgerhards, 2008-01-07 - */ -static rsRetVal -DeserializePropBag(obj_t *pObj, strm_t *pStrm) -{ - DEFiRet; - rsRetVal iRetLocal; - cstr_t *pstrID = NULL; - int oVers; - objInfo_t *pObjInfo; - - ISOBJ_assert(pObj); - ISOBJ_TYPE_assert(pStrm, strm); - - /* we de-serialize the header. if all goes well, we are happy. However, if - * we experience a problem, we try to recover. We do this by skipping to - * the next object header. This is defined via the line-start cookies. In - * worst case, we exhaust the queue, but then we receive EOF return state - * from objDeserializeTryRecover(), what will cause us to ultimately give up. - * rgerhards, 2008-07-08 - */ - do { - iRetLocal = objDeserializeHeader((uchar*) "OPB", &pstrID, &oVers, pStrm); - if(iRetLocal != RS_RET_OK) { - dbgprintf("objDeserializePropBag error %d during header - trying to recover\n", iRetLocal); - CHKiRet(objDeserializeTryRecover(pStrm)); - } - } while(iRetLocal != RS_RET_OK); - - if(rsCStrSzStrCmp(pstrID, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)) - ABORT_FINALIZE(RS_RET_INVALID_OID); - - CHKiRet(FindObjInfo(pstrID, &pObjInfo)); - - /* we got the object, now we need to fill the properties */ - CHKiRet(objDeserializeProperties(pObj, pObjInfo, pStrm)); - -finalize_it: - if(pstrID != NULL) - rsCStrDestruct(&pstrID); - - RETiRet; -} - -#undef NEXTC /* undef helper macro */ - - -/* --------------- end object serializiation / deserialization support --------------- */ - - -/* set the object (instance) name - * rgerhards, 2008-01-29 - * TODO: change the naming to a rsCStr obj! (faster) - */ -static rsRetVal -SetName(obj_t *pThis, uchar *pszName) -{ - DEFiRet; - - if(pThis->pszName != NULL) - free(pThis->pszName); - - pThis->pszName = (uchar*) strdup((char*) pszName); - - if(pThis->pszName == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - -finalize_it: - RETiRet; -} - - -/* get the object (instance) name - * Note that we use a non-standard calling convention. Thus function must never - * fail, else we run into real big problems. So it must make sure that at least someting - * is returned. - * rgerhards, 2008-01-30 - */ -static uchar * -GetName(obj_t *pThis) -{ - uchar *ret; - uchar szName[128]; - - BEGINfunc - ISOBJ_assert(pThis); - - if(pThis->pszName == NULL) { - snprintf((char*)szName, sizeof(szName)/sizeof(uchar), "%s %p", objGetClassName(pThis), pThis); - SetName(pThis, szName); - /* looks strange, but we NEED to re-check because if there was an - * error in objSetName(), the pointer may still be NULL - */ - if(pThis->pszName == NULL) { - ret = objGetClassName(pThis); - } else { - ret = pThis->pszName; - } - } else { - ret = pThis->pszName; - } - - ENDfunc - return ret; -} - - -/* Find the objInfo object for the current object - * rgerhards, 2008-02-29 - */ -static rsRetVal -FindObjInfo(cstr_t *pstrOID, objInfo_t **ppInfo) -{ - DEFiRet; - int bFound; - int i; - - assert(pstrOID != NULL); - assert(ppInfo != NULL); - - bFound = 0; - i = 0; - while(!bFound && i < OBJ_NUM_IDS) { -#if 0 -RUNLOG_VAR("%d", i); -if(arrObjInfo[i] != NULL) { -RUNLOG_VAR("%p", arrObjInfo[i]->pszID); -RUNLOG_VAR("%s", arrObjInfo[i]->pszID); -} -#endif - if(arrObjInfo[i] != NULL && !rsCStrSzStrCmp(pstrOID, arrObjInfo[i]->pszID, arrObjInfo[i]->lenID)) { - bFound = 1; - break; - } - ++i; - } - - if(!bFound) - ABORT_FINALIZE(RS_RET_NOT_FOUND); - - *ppInfo = arrObjInfo[i]; - -finalize_it: - if(iRet == RS_RET_OK) { - /* DEV DEBUG ONLY dbgprintf("caller requested object '%s', found at index %d\n", (*ppInfo)->pszID, i);*/ - /*EMPTY BY INTENSION*/; - } else { - dbgprintf("caller requested object '%s', not found (iRet %d)\n", rsCStrGetSzStr(pstrOID), iRet); - } - - RETiRet; -} - - -/* register a classes' info pointer, so that we can reference it later, if needed to - * (e.g. for de-serialization support). - * rgerhards, 2008-01-07 - * In this function, we look for a free space in the object table. While we do so, we - * also detect if the same object has already been registered, which is not valid. - * rgerhards, 2008-02-29 - */ -static rsRetVal -RegisterObj(uchar *pszObjName, objInfo_t *pInfo) -{ - DEFiRet; - int bFound; - int i; - - assert(pszObjName != NULL); - assert(pInfo != NULL); - - bFound = 0; - i = 0; - while(!bFound && i < OBJ_NUM_IDS && arrObjInfo[i] != NULL) { - if( arrObjInfo[i] != NULL - && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) { - bFound = 1; - break; - } - ++i; - } - - if(bFound) ABORT_FINALIZE(RS_RET_OBJ_ALREADY_REGISTERED); - if(i >= OBJ_NUM_IDS) ABORT_FINALIZE(RS_RET_OBJ_REGISTRY_OUT_OF_SPACE); - - arrObjInfo[i] = pInfo; - /* DEV debug only: dbgprintf("object '%s' successfully registered with index %d, qIF %p\n", pszObjName, i, pInfo->QueryIF); */ - -finalize_it: - if(iRet != RS_RET_OK) { - errmsg.LogError(NO_ERRCODE, "registering object '%s' failed with error code %d", pszObjName, iRet); - } - - RETiRet; -} - - -/* deregister a classes' info pointer, usually called because the class is unloaded. - * After deregistration, the class can no longer be accessed, except if it is reloaded. - * rgerhards, 2008-03-10 - */ -static rsRetVal -UnregisterObj(uchar *pszObjName) -{ - DEFiRet; - int bFound; - int i; - - assert(pszObjName != NULL); - - bFound = 0; - i = 0; - while(!bFound && i < OBJ_NUM_IDS) { - if( arrObjInfo[i] != NULL - && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) { - bFound = 1; - break; - } - ++i; - } - - if(!bFound) - ABORT_FINALIZE(RS_RET_OBJ_NOT_REGISTERED); - - InfoDestruct(&arrObjInfo[i]); - /* DEV debug only: dbgprintf("object '%s' successfully unregistered with index %d\n", pszObjName, i); */ - -finalize_it: - if(iRet != RS_RET_OK) { - dbgprintf("unregistering object '%s' failed with error code %d\n", pszObjName, iRet); - } - - RETiRet; -} - - -/* This function shall be called by anyone who would like to use an object. It will - * try to locate the object, load it into memory if not already present and return - * a pointer to the objects interface. - * rgerhards, 2008-02-29 - */ -static rsRetVal -UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) -{ - DEFiRet; - cstr_t *pStr = NULL; - objInfo_t *pObjInfo; - - - /* DEV debug only: dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */ - - if(pIf->ifIsLoaded == 1) { - ABORT_FINALIZE(RS_RET_OK); /* we are already set */ - } - if(pIf->ifIsLoaded == 2) { - ABORT_FINALIZE(RS_RET_LOAD_ERROR); /* we had a load error and can not continue */ - } - - /* we must be careful that we do not enter in infinite loop if an error occurs during - * loading a module. ModLoad emits an error message in such cases and that potentially - * can trigger the same code here. So we initially set the module state to "load error" - * and set it to "fully initialized" when the load succeeded. It's a bit hackish, but - * looks like a good solution. -- rgerhards, 2008-03-07 - */ - pIf->ifIsLoaded = 2; - - CHKiRet(rsCStrConstructFromszStr(&pStr, pObjName)); - iRet = FindObjInfo(pStr, &pObjInfo); - if(iRet == RS_RET_NOT_FOUND) { - /* in this case, we need to see if we can dynamically load the object */ - if(pObjFile == NULL) { - FINALIZE; /* no chance, we have lost... */ - } else { - CHKiRet(module.Load(pObjFile)); - /* NOW, we must find it or we have a problem... */ - CHKiRet(FindObjInfo(pStr, &pObjInfo)); - } - } else if(iRet != RS_RET_OK) { - FINALIZE; /* give up */ - } - - /* if we reach this point, we have a valid pObjInfo */ - if(pObjFile != NULL) { /* NULL means core module */ - module.Use(srcFile, pObjInfo->pModInfo); /* increase refcount */ - } - - CHKiRet(pObjInfo->QueryIF(pIf)); - pIf->ifIsLoaded = 1; /* we are happy */ - -finalize_it: - if(pStr != NULL) - rsCStrDestruct(&pStr); - - RETiRet; -} - - -/* This function shall be called when a caller is done with an object. Its primary - * purpose is to keep the reference count correct, which is highly important for - * modules residing in loadable modules. - * rgerhards, 2008-03-10 - */ -static rsRetVal -ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) -{ - DEFiRet; - cstr_t *pStr = NULL; - objInfo_t *pObjInfo; - - - dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); - - if(pObjFile == NULL) - FINALIZE; /* if it is not a lodable module, we do not need to do anything... */ - - if(pIf->ifIsLoaded == 0) { - ABORT_FINALIZE(RS_RET_OK); /* we are already set */ /* TODO: flag an error? */ - } - if(pIf->ifIsLoaded == 2) { - pIf->ifIsLoaded = 0; /* clean up */ - ABORT_FINALIZE(RS_RET_OK); /* we had a load error and can not continue */ - } - - CHKiRet(rsCStrConstructFromszStr(&pStr, pObjName)); - CHKiRet(FindObjInfo(pStr, &pObjInfo)); - - /* if we reach this point, we have a valid pObjInfo */ - //if(pObjInfo->pModInfo != NULL) { /* NULL means core module */ - module.Release(srcFile, &pObjInfo->pModInfo); /* decrease refcount */ - - pIf->ifIsLoaded = 0; /* indicated "no longer valid" */ - -finalize_it: - if(pStr != NULL) - rsCStrDestruct(&pStr); - - RETiRet; -} - - -/* queryInterface function - * rgerhards, 2008-02-29 - */ -BEGINobjQueryInterface(obj) -CODESTARTobjQueryInterface(obj) - if(pIf->ifVersion != objCURR_IF_VERSION) { /* check for current version, increment on each change */ - ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); - } - - /* ok, we have the right interface, so let's fill it - * Please note that we may also do some backwards-compatibility - * work here (if we can support an older interface version - that, - * of course, also affects the "if" above). - */ - pIf->UseObj = UseObj; - pIf->ReleaseObj = ReleaseObj; - pIf->InfoConstruct = InfoConstruct; - pIf->DestructObjSelf = DestructObjSelf; - pIf->BeginSerializePropBag = BeginSerializePropBag; - pIf->InfoSetMethod = InfoSetMethod; - pIf->BeginSerialize = BeginSerialize; - pIf->SerializeProp = SerializeProp; - pIf->EndSerialize = EndSerialize; - pIf->RegisterObj = RegisterObj; - pIf->UnregisterObj = UnregisterObj; - pIf->Deserialize = Deserialize; - pIf->DeserializePropBag = DeserializePropBag; - pIf->SetName = SetName; - pIf->GetName = GetName; -finalize_it: -ENDobjQueryInterface(obj) - - -/* This function returns a pointer to our own interface. It is used as the - * hook that every object (including dynamically loaded ones) can use to - * obtain a pointer to our interface which than can be used to obtain - * pointers to any other interface in the system. This function must be - * externally visible because of its special nature. - * rgerhards, 2008-02-29 [nice - will have that date the next time in 4 years ;)] - */ -rsRetVal -objGetObjInterface(obj_if_t *pIf) -{ - DEFiRet; - assert(pIf != NULL); - objQueryInterface(pIf); - RETiRet; -} - - -/* exit our class - * rgerhards, 2008-03-11 - */ -rsRetVal -objClassExit(void) -{ - DEFiRet; - /* release objects we no longer need */ - objRelease(var, CORE_COMPONENT); - objRelease(module, CORE_COMPONENT); - objRelease(errmsg, CORE_COMPONENT); - - /* TODO: implement the class exits! */ -#if 0 - errmsgClassInit(pModInfo); - cfsyslineInit(pModInfo); - varClassInit(pModInfo); -#endif - moduleClassExit(); - RETiRet; -} - - -/* initialize our own class - * Please note that this also initializes those classes that we rely on. - * Though this is a bit dirty, we need to do it - otherwise we can't get - * around that bootstrap problem. We need to face the fact the the obj - * class is a little different from the rest of the system, as it provides - * the core class loader functionality. - * rgerhards, 2008-02-29 - */ -rsRetVal -objClassInit(modInfo_t *pModInfo) -{ - DEFiRet; - int i; - - /* first, initialize the object system itself. This must be done - * before any other object is created. - */ - for(i = 0 ; i < OBJ_NUM_IDS ; ++i) { - arrObjInfo[i] = NULL; - } - - /* request objects we use */ - CHKiRet(objGetObjInterface(&obj)); /* get ourselves ;) */ - - /* init classes we use (limit to as few as possible!) */ - CHKiRet(errmsgClassInit(pModInfo)); - CHKiRet(cfsyslineInit()); - CHKiRet(varClassInit(pModInfo)); - CHKiRet(moduleClassInit(pModInfo)); - CHKiRet(objUse(var, CORE_COMPONENT)); - CHKiRet(objUse(module, CORE_COMPONENT)); - CHKiRet(objUse(errmsg, CORE_COMPONENT)); - -finalize_it: - RETiRet; -} - -/* vi:set ai: - */ diff --git a/obj.h b/obj.h deleted file mode 100644 index 29ad2ae4..00000000 --- a/obj.h +++ /dev/null @@ -1,124 +0,0 @@ -/* Definition of the generic obj class module. - * - * This module relies heavily on preprocessor macros in order to - * provide fast execution time AND ease of use. - * - * Each object that uses this base class MUST provide a constructor with - * the following interface: - * - * Destruct(pThis); - * - * A constructor is not necessary (except for some features, e.g. de-serialization). - * If it is provided, it is a three-part constructor (to handle all cases with a - * generic interface): - * - * Construct(&pThis); - * SetProperty(pThis, property_t *); - * ConstructFinalize(pThis); - * - * SetProperty() and ConstructFinalize() may also be called on an object - * instance which has been Construct()'ed outside of this module. - * - * pThis always references to a pointer of the object. - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#ifndef OBJ_H_INCLUDED -#define OBJ_H_INCLUDED - -#include "obj-types.h" -#include "var.h" -#include "stream.h" - -/* macros */ -/* the following one is a helper that prevents us from writing the - * ever-same code at the end of Construct() - */ -#define OBJCONSTRUCT_CHECK_SUCCESS_AND_CLEANUP \ - if(iRet == RS_RET_OK) { \ - *ppThis = pThis; \ - } else { \ - if(pThis != NULL) \ - free(pThis); \ - } - -#define objSerializeSCALAR_VAR(strm, propName, propType, var) \ - CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) &var)); -#define objSerializeSCALAR(strm, propName, propType) \ - CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) &pThis->propName)); -#define objSerializePTR(strm, propName, propType) \ - CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) pThis->propName)); -#define DEFobjStaticHelpers \ - static objInfo_t *pObjInfoOBJ = NULL; \ - DEFobjCurrIf(obj) - - -#define objGetClassName(pThis) (((obj_t*) (pThis))->pObjInfo->pszID) -#define objGetVersion(pThis) (((obj_t*) (pThis))->pObjInfo->iObjVers) -/* the next macro MUST be called in Constructors: */ -#ifndef NDEBUG /* this means if debug... */ -# define objConstructSetObjInfo(pThis) \ - ASSERT(((obj_t*) (pThis))->pObjInfo == NULL); \ - ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ; \ - ((obj_t*) (pThis))->iObjCooCKiE = 0xBADEFEE -#else -# define objConstructSetObjInfo(pThis) ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ -#endif -#define objDestruct(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_DESTRUCT])(&pThis) -#define objSerialize(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_SERIALIZE]) -#define objGetSeverity(pThis, piSever) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_GETSEVERITY])(pThis, piSever) -#define objDebugPrint(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_DEBUGPRINT])(pThis) - -#define OBJSetMethodHandler(methodID, pHdlr) \ - CHKiRet(obj.InfoSetMethod(pObjInfoOBJ, methodID, (rsRetVal (*)(void*)) pHdlr)) - -/* interfaces */ -BEGINinterface(obj) /* name must also be changed in ENDinterface macro! */ - rsRetVal (*UseObj)(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf); - rsRetVal (*ReleaseObj)(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf); - rsRetVal (*InfoConstruct)(objInfo_t **ppThis, uchar *pszID, int iObjVers, - rsRetVal (*pConstruct)(void *), rsRetVal (*pDestruct)(void *), - rsRetVal (*pQueryIF)(interface_t*), modInfo_t*); - rsRetVal (*DestructObjSelf)(obj_t *pThis); - rsRetVal (*BeginSerializePropBag)(strm_t *pStrm, obj_t *pObj); - rsRetVal (*InfoSetMethod)(objInfo_t *pThis, objMethod_t objMethod, rsRetVal (*pHandler)(void*)); - rsRetVal (*BeginSerialize)(strm_t *pStrm, obj_t *pObj); - rsRetVal (*SerializeProp)(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr); - rsRetVal (*EndSerialize)(strm_t *pStrm); - rsRetVal (*RegisterObj)(uchar *pszObjName, objInfo_t *pInfo); - rsRetVal (*UnregisterObj)(uchar *pszObjName); - rsRetVal (*Deserialize)(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixup)(obj_t*,void*), void *pUsr); - rsRetVal (*DeserializePropBag)(obj_t *pObj, strm_t *pStrm); - rsRetVal (*SetName)(obj_t *pThis, uchar *pszName); - uchar * (*GetName)(obj_t *pThis); -ENDinterface(obj) -#define objCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ - - -/* prototypes */ -/* the following define *is* necessary, because it provides the root way of obtaining - * interfaces (at some place we need to start our query... - */ -rsRetVal objGetObjInterface(obj_if_t *pIf); -PROTOTYPEObjClassInit(obj); -PROTOTYPEObjClassExit(obj); - -#endif /* #ifndef OBJ_H_INCLUDED */ diff --git a/objomsr.c b/objomsr.c deleted file mode 100644 index 6a617ad1..00000000 --- a/objomsr.c +++ /dev/null @@ -1,145 +0,0 @@ -/* objomsr.c - * Implementation of the omsr (omodStringRequest) object. - * - * File begun on 2007-07-27 by RGerhards - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" - -#include -#include -#include -#include - -#include "rsyslog.h" -#include "objomsr.h" - - -/* destructor - */ -rsRetVal OMSRdestruct(omodStringRequest_t *pThis) -{ - int i; - - assert(pThis != NULL); - /* free the strings */ - if(pThis->ppTplName != NULL) { - for(i = 0 ; i < pThis->iNumEntries ; ++i) { - if(pThis->ppTplName[i] != NULL) { - free(pThis->ppTplName[i]); - } - } - free(pThis->ppTplName); - } - if(pThis->piTplOpts != NULL) - free(pThis->piTplOpts); - free(pThis); - - return RS_RET_OK; -} - - -/* constructor - */ -rsRetVal OMSRconstruct(omodStringRequest_t **ppThis, int iNumEntries) -{ - omodStringRequest_t *pThis; - DEFiRet; - - assert(ppThis != NULL); - assert(iNumEntries >= 0); - if((pThis = calloc(1, sizeof(omodStringRequest_t))) == NULL) { - iRet = RS_RET_OUT_OF_MEMORY; - goto abort_it; - } - - /* got the structure, so fill it */ - pThis->iNumEntries = iNumEntries; - /* allocate string for template name array. The individual strings will be - * allocated as the code progresses (we do not yet know the string sizes) - */ - if((pThis->ppTplName = calloc(iNumEntries, sizeof(uchar*))) == NULL) { - OMSRdestruct(pThis); - pThis = NULL; - iRet = RS_RET_OUT_OF_MEMORY; - goto abort_it; - } - /* allocate the template options array. */ - if((pThis->piTplOpts = calloc(iNumEntries, sizeof(int))) == NULL) { - OMSRdestruct(pThis); - pThis = NULL; - iRet = RS_RET_OUT_OF_MEMORY; - goto abort_it; - } - -abort_it: - *ppThis = pThis; - RETiRet; -} - -/* set a template name and option to the object. Index must be given. The pTplName must be - * pointing to memory that can be freed. If in doubt, the caller must strdup() the value. - */ -rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, int iTplOpts) -{ - assert(pThis != NULL); - assert(pTplName != NULL); - assert(iEntry < pThis->iNumEntries); - - if(pThis->ppTplName[iEntry] != NULL) - free(pThis->ppTplName[iEntry]); - pThis->ppTplName[iEntry] = pTplName; - pThis->piTplOpts[iEntry] = iTplOpts; - - return RS_RET_OK; -} - - -/* get number of entries for this object - */ -int OMSRgetEntryCount(omodStringRequest_t *pThis) -{ - assert(pThis != NULL); - return pThis->iNumEntries; -} - - -/* return data for a specific entry. All data returned is - * read-only and lasts only as long as the object lives. If the caller - * needs it for an extended period of time, the caller must copy the - * strings. Please note that the string pointer may be NULL, which is the - * case when it was never set. - */ -int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int *piTplOpts) -{ - assert(pThis != NULL); - assert(ppTplName != NULL); - assert(piTplOpts != NULL); - assert(iEntry < pThis->iNumEntries); - - *ppTplName = pThis->ppTplName[iEntry]; - *piTplOpts = pThis->piTplOpts[iEntry]; - - return RS_RET_OK; -} -/* - * vi:set ai: - */ diff --git a/objomsr.h b/objomsr.h deleted file mode 100644 index 9fdddf69..00000000 --- a/objomsr.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Definition of the omsr (omodStringRequest) object. - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ - -#ifndef OBJOMSR_H_INCLUDED -#define OBJOMSR_H_INCLUDED - -/* define flags for required template options */ -#define OMSR_NO_RQD_TPL_OPTS 0 -#define OMSR_RQD_TPL_OPT_SQL 1 -/* next option is 2, 4, 8, ... */ - -struct omodStringRequest_s { /* strings requested by output module for doAction() */ - int iNumEntries; /* number of array entries for data elements below */ - uchar **ppTplName; /* pointer to array of template names */ - int *piTplOpts;/* pointer to array of check-options when pulling template */ -}; -typedef struct omodStringRequest_s omodStringRequest_t; - -/* prototypes */ -rsRetVal OMSRdestruct(omodStringRequest_t *pThis); -rsRetVal OMSRconstruct(omodStringRequest_t **ppThis, int iNumEntries); -rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, int iTplOpts); -int OMSRgetEntryCount(omodStringRequest_t *pThis); -int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int *piTplOpts); - -#endif /* #ifndef OBJOMSR_H_INCLUDED */ diff --git a/parse.c b/parse.c index 171e5355..58458d62 100644 --- a/parse.c +++ b/parse.c @@ -3,7 +3,7 @@ * * begun 2005-09-15 rgerhards * - * Copyright 2005 + * Copyright 2005-2008 * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. * * This file is part of rsyslog. diff --git a/plugins/ommysql/Makefile.am b/plugins/ommysql/Makefile.am index 329c2119..d5433a40 100644 --- a/plugins/ommysql/Makefile.am +++ b/plugins/ommysql/Makefile.am @@ -1,7 +1,7 @@ pkglib_LTLIBRARIES = ommysql.la ommysql_la_SOURCES = ommysql.c ommysql.h -ommysql_la_CPPFLAGS = -I$(top_srcdir) $(mysql_cflags) $(pthreads_cflags) $(rsrt_cflags) +ommysql_la_CPPFLAGS = $(rsrt_cflags) $(mysql_cflags) $(pthreads_cflags) ommysql_la_LDFLAGS = -module -avoid-version ommysql_la_LIBADD = $(mysql_libs) diff --git a/rsyslog.h b/rsyslog.h deleted file mode 100644 index c73c659c..00000000 --- a/rsyslog.h +++ /dev/null @@ -1,270 +0,0 @@ -/* Header file with global definitions for the whole - * rsyslog project (including all subprojects like - * rfc3195d). - * Begun 2005-09-15 RGerhards - * - * Copyright (C) 2005 by 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. -*/ -#ifndef INCLUDED_RSYSLOG_H -#define INCLUDED_RSYSLOG_H - -/* ############################################################# * - * # Config Settings # * - * ############################################################# */ -#define RS_STRINGBUF_ALLOC_INCREMENT 128 - -/* ############################################################# * - * # End Config Settings # * - * ############################################################# */ - -#ifndef NOLARGEFILE -# undef _LARGEFILE_SOURCE -# undef _LARGEFILE64_SOURCE -# undef _FILE_OFFSET_BITS -# define _LARGEFILE_SOURCE -# define _LARGEFILE64_SOURCE -# define _FILE_OFFSET_BITS 64 -#endif - - -/* some universal 64 bit define... */ -typedef long long int64; -typedef long long unsigned uint64; -typedef int64 number_t; /* type to use for numbers - TODO: maybe an autoconf option? */ - -#ifdef __hpux -typedef unsigned int u_int32_t; /* TODO: is this correct? */ -typedef int socklen_t; -#endif - -/* settings for flow control - * TODO: is there a better place for them? -- rgerhards, 2008-03-14 - */ -typedef enum { - eFLOWCTL_NO_DELAY = 0, /**< UDP and other non-delayable sources */ - eFLOWCTL_LIGHT_DELAY = 1, /**< some light delay possible, but no extended period of time */ - eFLOWCTL_FULL_DELAY = 2 /**< delay possible for extended period of time */ -} flowControl_t; - - -/* The error codes below are orginally "borrowed" from - * liblogging. As such, we reserve values up to -2999 - * just in case we need to borrow something more ;) -*/ -enum rsRetVal_ /** return value. All methods return this if not specified otherwise */ -{ - RS_RET_NOT_IMPLEMENTED = -7, /**< implementation is missing (probably internal error or lazyness ;)) */ - RS_RET_OUT_OF_MEMORY = -6, /**< memory allocation failed */ - RS_RET_PROVIDED_BUFFER_TOO_SMALL = -50,/**< the caller provided a buffer, but the called function sees the size of this buffer is too small - operation not carried out */ - RS_RET_TRUE = -1, /**< to indicate a true state (can be used as TRUE, legacy) */ - RS_RET_FALSE = -2, /**< to indicate a false state (can be used as FALSE, legacy) */ - RS_RET_NO_IRET = -8, /**< This is a trick for the debuging system - it means no iRet is provided */ - RS_RET_ERR = -3000, /**< generic failure */ - RS_TRUNCAT_TOO_LARGE = -3001, /**< truncation operation where too many chars should be truncated */ - RS_RET_FOUND_AT_STRING_END = -3002, /**< some value found, but at the last pos of string */ - RS_RET_NOT_FOUND = -3003, /**< some requested value not found */ - RS_RET_MISSING_TRAIL_QUOTE = -3004, /**< an expected trailing quote is missing */ - RS_RET_NO_DIGIT = -3005, /**< an digit was expected, but none found (mostly parsing) */ - RS_RET_NO_MORE_DATA = -3006, /**< insufficient data, e.g. end of string during parsing */ - RS_RET_INVALID_IP = -3007, /**< invalid ip found where valid was expected */ - RS_RET_OBJ_CREATION_FAILED = - 3008, /**< the creation of an object failed (no details available) */ - RS_RET_PARAM_ERROR = -1000, /**< invalid parameter in call to function */ - RS_RET_MISSING_INTERFACE = -1001,/**< interface version mismatch, required missing */ - RS_RET_INVALID_CORE_INTERFACE = -1002,/**< interface provided by host invalid, can not be used */ - RS_RET_ENTRY_POINT_NOT_FOUND = -1003,/**< a requested entry point was not found */ - RS_RET_MODULE_ENTRY_POINT_NOT_FOUND = -1004,/**< a entry point requested from a module was not present in it */ - RS_RET_OBJ_NOT_AVAILABLE = -1005,/**< something could not be completed because the required object is not available*/ - RS_RET_LOAD_ERROR = -1006,/**< we had an error loading the object/interface and can not continue */ - RS_RET_MODULE_STILL_REFERENCED = -1007,/**< module could not be unloaded because it still is referenced by someone */ - RS_RET_OBJ_UNKNOWN = -1008,/**< object is unknown where required */ - RS_RET_OBJ_NOT_REGISTERED = -1009,/**< tried to unregister an object that is not registered */ - /* return states for config file processing */ - RS_RET_NONE = -2000, /**< some value is not available - not necessarily an error */ - RS_RET_CONFLINE_UNPROCESSED = -2001,/**< config line was not processed, pass to other module */ - RS_RET_DISCARDMSG = -2002, /**< discard message (no error state, processing request!) */ - RS_RET_INCOMPATIBLE = -2003, /**< function not compatible with requested feature */ - RS_RET_NOENTRY = -2004, /**< do not create an entry for (whatever) - not necessary an error */ - RS_RET_NO_SQL_STRING = -2005, /**< string is not suitable for use as SQL */ - RS_RET_DISABLE_ACTION = -2006, /**< action requests that it be disabled */ - RS_RET_SUSPENDED = -2007, /**< something was suspended, not neccesarily an error */ - RS_RET_RQD_TPLOPT_MISSING = -2008,/**< a required template option is missing */ - RS_RET_INVALID_VALUE = -2009,/**< some value is invalid (e.g. user-supplied data) */ - RS_RET_INVALID_INT = -2010,/**< invalid integer */ - RS_RET_INVALID_CMD = -2011,/**< invalid command */ - RS_RET_VAL_OUT_OF_RANGE = -2012, /**< value out of range */ - RS_RET_FOPEN_FAILURE = -2013, /**< failure during fopen, for example file not found - see errno */ - RS_RET_END_OF_LINKEDLIST = -2014, /**< end of linked list, not an error, but a status */ - RS_RET_CHAIN_NOT_PERMITTED = -2015, /**< chaining (e.g. of config command handlers) not permitted */ - RS_RET_INVALID_PARAMS = -2016,/**< supplied parameters are invalid */ - RS_RET_EMPTY_LIST = -2017, /**< linked list is empty */ - RS_RET_FINISHED = -2018, /**< some opertion is finished, not an error state */ - RS_RET_INVALID_SOURCE = -2019, /**< source (address) invalid for some reason */ - RS_RET_ADDRESS_UNKNOWN = -2020, /**< an address is unknown - not necessarily an error */ - RS_RET_MALICIOUS_ENTITY = -2021, /**< there is an malicious entity involved */ - RS_RET_NO_KERNEL_LOGSRC = -2022, /**< no source for kernel logs can be obtained */ - RS_RET_TCP_SEND_ERROR = -2023, /**< error during TCP send process */ - RS_RET_GSS_SEND_ERROR = -2024, /**< error during GSS (via TCP) send process */ - RS_RET_TCP_SOCKCREATE_ERR = -2025, /**< error during creation of TCP socket */ - RS_RET_GSS_SENDINIT_ERROR = -2024, /**< error during GSS (via TCP) send initialization process */ - RS_RET_QUEUE_FULL = -2025, /**< queue is full, operation could not be completed */ - RS_RET_EOF = -2026, /**< end of file reached, not necessarily an error */ - RS_RET_IO_ERROR = -2027, /**< some kind of IO error happened */ - RS_RET_INVALID_OID = -2028, /**< invalid object ID */ - RS_RET_INVALID_HEADER = -2029, /**< invalid header */ - RS_RET_INVALID_HEADER_VERS = -2030, /**< invalid header version */ - RS_RET_INVALID_DELIMITER = -2031, /**< invalid delimiter, e.g. between params */ - RS_RET_INVALID_PROPFRAME = -2032, /**< invalid framing in serialized property */ - RS_RET_NO_PROPLINE = -2033, /**< line is not a property line */ - RS_RET_INVALID_TRAILER = -2034, /**< invalid trailer */ - RS_RET_VALUE_TOO_LOW = -2035, /**< a provided value is too low */ - RS_RET_FILE_PREFIX_MISSING = -2036, /**< a required file prefix (parameter?) is missing */ - RS_RET_INVALID_HEADER_RECTYPE = -2037, /**< invalid record type in header or invalid header */ - RS_RET_QTYPE_MISMATCH = -2038, /**< different qType when reading back a property type */ - RS_RET_NO_FILE_ACCESS = -2039, /**< covers EACCES error on file open() */ - RS_RET_FILE_NOT_FOUND = -2040, /**< file not found */ - RS_RET_TIMED_OUT = -2041, /**< timeout occured (not necessarily an error) */ - RS_RET_QSIZE_ZERO = -2042, /**< queue size is zero where this is not supported */ - RS_RET_ALREADY_STARTING = -2043, /**< something (a thread?) is already starting - not necessarily an error */ - RS_RET_NO_MORE_THREADS = -2044, /**< no more threads available, not necessarily an error */ - RS_RET_NO_FILEPREFIX = -2045, /**< file prefix is not specified where one is needed */ - RS_RET_CONFIG_ERROR = -2046, /**< there is a problem with the user-provided config settigs */ - RS_RET_OUT_OF_DESRIPTORS = -2047, /**< a descriptor table's space has been exhausted */ - RS_RET_NO_DRIVERS = -2048, /**< a required drivers missing */ - RS_RET_NO_DRIVERNAME = -2049, /**< driver name missing where one was required */ - RS_RET_EOS = -2050, /**< end of stream (of whatever) */ - RS_RET_SYNTAX_ERROR = -2051, /**< syntax error, eg. during parsing */ - RS_RET_INVALID_OCTAL_DIGIT = -2052, /**< invalid octal digit during parsing */ - RS_RET_INVALID_HEX_DIGIT = -2053, /**< invalid hex digit during parsing */ - RS_RET_INTERFACE_NOT_SUPPORTED = -2054, /**< interface not supported */ - RS_RET_OUT_OF_STACKSPACE = -2055, /**< a stack data structure is exhausted and can not be grown */ - RS_RET_STACK_EMPTY = -2056, /**< a pop was requested on a stack, but the stack was already empty */ - RS_RET_INVALID_VMOP = -2057, /**< invalid virtual machine instruction */ - RS_RET_INVALID_VAR = -2058, /**< a var_t or its content is unsuitable, eg. VARTYPE_NONE */ - RS_RET_INVALID_NUMBER = -2059, /**< number invalid during parsing */ - RS_RET_NOT_A_NUMBER = -2060, /**< e.g. conversion impossible because the string is not a number */ - RS_RET_OBJ_ALREADY_REGISTERED = -2061, /**< object (name) is already registered */ - RS_RET_OBJ_REGISTRY_OUT_OF_SPACE = -2062, /**< the object registry has run out of space */ - RS_RET_HOST_NOT_PERMITTED = -2063, /**< a host is not permitted to perform an action it requested */ - RS_RET_MODULE_LOAD_ERR = -2064, /**< module could not be loaded */ - RS_RET_MODULE_LOAD_ERR_PATHLEN = -2065, /**< module could not be loaded - path to long */ - RS_RET_MODULE_LOAD_ERR_DLOPEN = -2066, /**< module could not be loaded - problem in dlopen() */ - RS_RET_MODULE_LOAD_ERR_NO_INIT = -2067, /**< module could not be loaded - init() missing */ - RS_RET_MODULE_LOAD_ERR_INIT_FAILED = -2068, /**< module could not be loaded - init() failed */ - RS_RET_NO_SOCKET = -2069, /**< socket could not be obtained or was not provided */ - RS_RET_SMTP_ERROR = -2070, /**< error during SMTP transation */ - RS_RET_MAIL_NO_TO = -2071, /**< recipient for mail destination is missing */ - RS_RET_MAIL_NO_FROM = -2072, /**< sender for mail destination is missing */ - RS_RET_INVALID_PRI = -2073, /**< PRI value is invalid */ - - /* RainerScript error messages (range 1000.. 1999) */ - RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ - - /* some generic error/status codes */ - RS_RET_OK_DELETE_LISTENTRY = 1, /**< operation successful, but callee requested the deletion of an entry (special state) */ - RS_RET_TERMINATE_NOW = 2, /**< operation successful, function is requested to terminate (mostly used with threads) */ - RS_RET_NO_RUN = 3, /**< operation successful, but function does not like to be executed */ - RS_RET_OK = 0 /**< operation successful */ -}; -typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */ - -/* some helpful macros to work with srRetVals. - * Be sure to call the to-be-returned variable always "iRet" and - * the function finalizer always "finalize_it". - */ -#define CHKiRet(code) if((iRet = code) != RS_RET_OK) goto finalize_it -/* macro below is to be used if we need our own handling, eg for cleanup */ -#define CHKiRet_Hdlr(code) if((iRet = code) != RS_RET_OK) -/* macro below is to handle failing malloc/calloc/strdup... which we almost always handle in the same way... */ -#define CHKmalloc(operation) if((operation) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY) -/* macro below is used in conjunction with CHKiRet_Hdlr, else use ABORT_FINALIZE */ -#define FINALIZE goto finalize_it; -#define DEFiRet BEGINfunc rsRetVal iRet = RS_RET_OK -#define RETiRet do{ ENDfuncIRet return iRet; }while(0) - -#define ABORT_FINALIZE(errCode) \ - do { \ - iRet = errCode; \ - goto finalize_it; \ - } while (0) - -/** Object ID. These are for internal checking. Each - * object is assigned a specific ID. This is contained in - * all Object structs (just like C++ RTTI). We can use - * this field to see if we have been passed a correct ID. - * Other than that, there is currently no other use for - * the object id. - */ -enum rsObjectID -{ - OIDrsFreed = -1, /**< assigned, when an object is freed. If this - * is seen during a method call, this is an - * invalid object pointer! - */ - OIDrsInvalid = 0, /**< value created by calloc(), so do not use ;) */ - /* The 0x3412 is a debug aid. It helps us find object IDs in memory - * dumps (on X86, this is 1234 in the dump ;) - * If you are on an embedded device and you would like to save space - * make them 1 byte only. - */ - OIDrsCStr = 0x34120001, - OIDrsPars = 0x34120002 -}; -typedef enum rsObjectID rsObjID; - -/* support to set object types */ -#ifdef NDEBUG -#define rsSETOBJTYPE(pObj, type) -#define rsCHECKVALIDOBJECT(x, type) -#else -#define rsSETOBJTYPE(pObj, type) pObj->OID = type; -#define rsCHECKVALIDOBJECT(x, type) {assert(x != NULL); assert(x->OID == type);} -#endif - -/** - * This macro should be used to free objects. - * It aids in interpreting dumps during debugging. - */ -#ifdef NDEBUG -#define RSFREEOBJ(x) free(x) -#else -#define RSFREEOBJ(x) {(x)->OID = OIDrsFreed; free(x);} -#endif - -/* get rid of the unhandy "unsigned char" - */ -typedef unsigned char uchar; - -/* for the time being, we do our own portability handling here. It - * looks like autotools either does not yet support checks for it, or - * I wasn't smart enough to find them ;) rgerhards, 2007-07-18 - */ -#ifndef __GNUC__ -# define __attribute__(x) /*NOTHING*/ -#endif - -/* The following prototype is convenient, even though it may not be the 100% correct place.. -- rgerhards 2008-01-07 */ -void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2))); - -#include "debug.h" - -#endif /* multi-include protection */ -/* - * vi:set ai: - */ diff --git a/runtime/Makefile.am b/runtime/Makefile.am index cd8a19c2..048ef411 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -1,9 +1,36 @@ sbin_PROGRAMS = man_MANS = noinst_LTLIBRARIES = librsyslog.la +pkglib_LTLIBRARIES = #pkglib_LTLIBRARIES = librsyslog.la librsyslog_la_SOURCES = \ + rsyslog.h \ + atomic.h \ + syslogd-types.h \ + module-template.h \ + obj-types.h \ + glbl.h \ + msg.c \ + msg.h \ + linkedlist.c \ + linkedlist.h \ + objomsr.c \ + objomsr.h \ + stringbuf.c \ + stringbuf.h \ + datetime.c \ + datetime.h \ + srutils.c \ + srUtils.h \ + errmsg.c \ + errmsg.h \ + debug.c \ + debug.h \ + obj.c \ + obj.h \ + modules.c \ + modules.h \ sync.c \ sync.h \ expr.c \ @@ -33,7 +60,7 @@ librsyslog_la_SOURCES = \ queue.c \ queue.h -librsyslog_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) +librsyslog_la_CPPFLAGS = -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(pthreads_cflags) #librsyslog_la_LDFLAGS = -module -avoid-version librsyslog_la_LIBADD = @@ -41,8 +68,7 @@ librsyslog_la_LIBADD = # regular expression support # if ENABLE_REGEXP -noinst_LTLIBRARIES += lmregexp.la -#pkglib_LTLIBRARIES += lmregexp.la +pkglib_LTLIBRARIES += lmregexp.la lmregexp_la_SOURCES = regexp.c regexp.h lmregexp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmregexp_la_LDFLAGS = -module -avoid-version $(rsrt_libs) diff --git a/runtime/atomic.h b/runtime/atomic.h new file mode 100644 index 00000000..430ae7f0 --- /dev/null +++ b/runtime/atomic.h @@ -0,0 +1,51 @@ +/* This header supplies atomic operations. So far, we rely on GCC's + * atomic builtins. I have no idea if we can check them via autotools, + * but I am making the necessary provisioning to live without them if + * they are not available. Please note that you should only use the macros + * here if you think you can actually live WITHOUT an explicit atomic operation, + * because in the non-presence of them, we simply do it without atomicitiy. + * Which, for word-aligned data types, usually (but only usually!) should work. + * + * We are using the functions described in + * http:/gcc.gnu.org/onlinedocs/gcc/Atomic-Builtins.html + * + * THESE MACROS MUST ONLY BE USED WITH WORD-SIZED DATA TYPES! + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" /* autotools! */ + +#ifndef INCLUDED_ATOMIC_H +#define INCLUDED_ATOMIC_H + +/* set the following to 1 if we have atomic operations (and #undef it otherwise) */ +/* #define DO_HAVE_ATOMICS 1 */ +/* for this release, we disable atomic calls because there seem to be some + * portability problems and we can not fix that without destabilizing the build. + * They simply came in too late. -- rgerhards, 2008-04-02 + */ +/* make sure they are not used! +#define ATOMIC_INC(data) ((void) __sync_fetch_and_add(&data, 1)) +#define ATOMIC_DEC_AND_FETCH(data) __sync_sub_and_fetch(&data, 1) +*/ +#define ATOMIC_INC(data) (++(data)) + +#endif /* #ifndef INCLUDED_ATOMIC_H */ diff --git a/runtime/datetime.c b/runtime/datetime.c new file mode 100644 index 00000000..d72cac3c --- /dev/null +++ b/runtime/datetime.c @@ -0,0 +1,630 @@ +/* The datetime object. It contains date and time related functions. + * + * Module begun 2008-03-05 by Rainer Gerhards, based on some code + * from syslogd.c. The main intension was to move code out of syslogd.c + * in a useful manner. It is still undecided if all functions will continue + * to stay here or some will be moved into parser modules (once we have them). + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include "rsyslog.h" +#include "obj.h" +#include "modules.h" +#include "datetime.h" +#include "sysvar.h" +#include "srUtils.h" +#include "stringbuf.h" +#include "errmsg.h" + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) + + +/* ------------------------------ methods ------------------------------ */ + + +/** + * Get the current date/time in the best resolution the operating + * system has to offer (well, actually at most down to the milli- + * second level. + * + * The date and time is returned in separate fields as this is + * most portable and removes the need for additional structures + * (but I have to admit it is somewhat "bulky";)). + * + * Obviously, all caller-provided pointers must not be NULL... + */ +static void getCurrTime(struct syslogTime *t) +{ + struct timeval tp; + struct tm *tm; + struct tm tmBuf; + long lBias; +# if defined(__hpux) + struct timezone tz; +# endif + + assert(t != NULL); +# if defined(__hpux) + /* TODO: check this: under HP UX, the tz information is actually valid + * data. So we need to obtain and process it there. + */ + gettimeofday(&tp, &tz); +# else + gettimeofday(&tp, NULL); +# endif + tm = localtime_r((time_t*) &(tp.tv_sec), &tmBuf); + + t->year = tm->tm_year + 1900; + t->month = tm->tm_mon + 1; + t->day = tm->tm_mday; + t->hour = tm->tm_hour; + t->minute = tm->tm_min; + t->second = tm->tm_sec; + t->secfrac = tp.tv_usec; + t->secfracPrecision = 6; + +# if __sun + /* Solaris uses a different method of exporting the time zone. + * It is UTC - localtime, which is the opposite sign of mins east of GMT. + */ + lBias = -(daylight ? altzone : timezone); +# elif defined(__hpux) + lBias = tz.tz_dsttime ? - tz.tz_minuteswest : 0; +# else + lBias = tm->tm_gmtoff; +# endif + if(lBias < 0) + { + t->OffsetMode = '-'; + lBias *= -1; + } + else + t->OffsetMode = '+'; + t->OffsetHour = lBias / 3600; + t->OffsetMinute = lBias % 3600; +} + + + + +/******************************************************************* + * BEGIN CODE-LIBLOGGING * + ******************************************************************* + * Code in this section is borrowed from liblogging. This is an + * interim solution. Once liblogging is fully integrated, this is + * to be removed (see http://www.monitorware.com/liblogging for + * more details. 2004-11-16 rgerhards + * + * Please note that the orginal liblogging code is modified so that + * it fits into the context of the current version of syslogd.c. + * + * DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!! + */ + +/** + * Parse a 32 bit integer number from a string. + * + * \param ppsz Pointer to the Pointer to the string being parsed. It + * must be positioned at the first digit. Will be updated + * so that on return it points to the first character AFTER + * the integer parsed. + * \retval The number parsed. + */ + +static int srSLMGParseInt32(char** ppsz) +{ + int i; + + i = 0; + while(isdigit((int) **ppsz)) + { + i = i * 10 + **ppsz - '0'; + ++(*ppsz); + } + + return i; +} + + +/** + * Parse a TIMESTAMP-3339. + * updates the parse pointer position. + */ +static int +ParseTIMESTAMP3339(struct syslogTime *pTime, char** ppszTS) +{ + char *pszTS = *ppszTS; + + assert(pTime != NULL); + assert(ppszTS != NULL); + assert(pszTS != NULL); + + pTime->year = srSLMGParseInt32(&pszTS); + + /* We take the liberty to accept slightly malformed timestamps e.g. in + * the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course, + * with the current state of affairs, we would never run into this code + * here because at postion 11, there is no "T" in such cases ;) + */ + if(*pszTS++ != '-') + return FALSE; + pTime->month = srSLMGParseInt32(&pszTS); + if(pTime->month < 1 || pTime->month > 12) + return FALSE; + + if(*pszTS++ != '-') + return FALSE; + pTime->day = srSLMGParseInt32(&pszTS); + if(pTime->day < 1 || pTime->day > 31) + return FALSE; + + if(*pszTS++ != 'T') + return FALSE; + + pTime->hour = srSLMGParseInt32(&pszTS); + if(pTime->hour < 0 || pTime->hour > 23) + return FALSE; + + if(*pszTS++ != ':') + return FALSE; + pTime->minute = srSLMGParseInt32(&pszTS); + if(pTime->minute < 0 || pTime->minute > 59) + return FALSE; + + if(*pszTS++ != ':') + return FALSE; + pTime->second = srSLMGParseInt32(&pszTS); + if(pTime->second < 0 || pTime->second > 60) + return FALSE; + + /* Now let's see if we have secfrac */ + if(*pszTS == '.') + { + char *pszStart = ++pszTS; + pTime->secfrac = srSLMGParseInt32(&pszTS); + pTime->secfracPrecision = (int) (pszTS - pszStart); + } + else + { + pTime->secfracPrecision = 0; + pTime->secfrac = 0; + } + + /* check the timezone */ + if(*pszTS == 'Z') + { + pszTS++; /* eat Z */ + pTime->OffsetMode = 'Z'; + pTime->OffsetHour = 0; + pTime->OffsetMinute = 0; + } + else if((*pszTS == '+') || (*pszTS == '-')) + { + pTime->OffsetMode = *pszTS; + pszTS++; + + pTime->OffsetHour = srSLMGParseInt32(&pszTS); + if(pTime->OffsetHour < 0 || pTime->OffsetHour > 23) + return FALSE; + + if(*pszTS++ != ':') + return FALSE; + pTime->OffsetMinute = srSLMGParseInt32(&pszTS); + if(pTime->OffsetMinute < 0 || pTime->OffsetMinute > 59) + return FALSE; + } + else + /* there MUST be TZ information */ + return FALSE; + + /* OK, we actually have a 3339 timestamp, so let's indicated this */ + if(*pszTS == ' ') + ++pszTS; + else + return FALSE; + + /* update parse pointer */ + *ppszTS = pszTS; + + return TRUE; +} + + +/** + * Parse a TIMESTAMP-3164. + * Returns TRUE on parse OK, FALSE on parse error. + */ +static int +ParseTIMESTAMP3164(struct syslogTime *pTime, char* pszTS) +{ + assert(pTime != NULL); + assert(pszTS != NULL); + + getCurrTime(pTime); /* obtain the current year and UTC offsets! */ + + /* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec), + * we may see the following character sequences occur: + * + * J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec + * + * We will use this for parsing, as it probably is the + * fastest way to parse it. + * + * 2005-07-18, well sometimes it pays to be a bit more verbose, even in C... + * Fixed a bug that lead to invalid detection of the data. The issue was that + * we had an if(++pszTS == 'x') inside of some of the consturcts below. However, + * there were also some elseifs (doing the same ++), which than obviously did not + * check the orginal character but the next one. Now removed the ++ and put it + * into the statements below. Was a really nasty bug... I didn't detect it before + * june, when it first manifested. This also lead to invalid parsing of the rest + * of the message, as the time stamp was not detected to be correct. - rgerhards + */ + switch(*pszTS++) + { + case 'J': + if(*pszTS == 'a') { + ++pszTS; + if(*pszTS == 'n') { + ++pszTS; + pTime->month = 1; + } else + return FALSE; + } else if(*pszTS == 'u') { + ++pszTS; + if(*pszTS == 'n') { + ++pszTS; + pTime->month = 6; + } else if(*pszTS == 'l') { + ++pszTS; + pTime->month = 7; + } else + return FALSE; + } else + return FALSE; + break; + case 'F': + if(*pszTS == 'e') { + ++pszTS; + if(*pszTS == 'b') { + ++pszTS; + pTime->month = 2; + } else + return FALSE; + } else + return FALSE; + break; + case 'M': + if(*pszTS == 'a') { + ++pszTS; + if(*pszTS == 'r') { + ++pszTS; + pTime->month = 3; + } else if(*pszTS == 'y') { + ++pszTS; + pTime->month = 5; + } else + return FALSE; + } else + return FALSE; + break; + case 'A': + if(*pszTS == 'p') { + ++pszTS; + if(*pszTS == 'r') { + ++pszTS; + pTime->month = 4; + } else + return FALSE; + } else if(*pszTS == 'u') { + ++pszTS; + if(*pszTS == 'g') { + ++pszTS; + pTime->month = 8; + } else + return FALSE; + } else + return FALSE; + break; + case 'S': + if(*pszTS == 'e') { + ++pszTS; + if(*pszTS == 'p') { + ++pszTS; + pTime->month = 9; + } else + return FALSE; + } else + return FALSE; + break; + case 'O': + if(*pszTS == 'c') { + ++pszTS; + if(*pszTS == 't') { + ++pszTS; + pTime->month = 10; + } else + return FALSE; + } else + return FALSE; + break; + case 'N': + if(*pszTS == 'o') { + ++pszTS; + if(*pszTS == 'v') { + ++pszTS; + pTime->month = 11; + } else + return FALSE; + } else + return FALSE; + break; + case 'D': + if(*pszTS == 'e') { + ++pszTS; + if(*pszTS == 'c') { + ++pszTS; + pTime->month = 12; + } else + return FALSE; + } else + return FALSE; + break; + default: + return FALSE; + } + + /* done month */ + + if(*pszTS++ != ' ') + return FALSE; + + /* we accept a slightly malformed timestamp when receiving. This is + * we accept one-digit days + */ + if(*pszTS == ' ') + ++pszTS; + + pTime->day = srSLMGParseInt32(&pszTS); + if(pTime->day < 1 || pTime->day > 31) + return FALSE; + + if(*pszTS++ != ' ') + return FALSE; + pTime->hour = srSLMGParseInt32(&pszTS); + if(pTime->hour < 0 || pTime->hour > 23) + return FALSE; + + if(*pszTS++ != ':') + return FALSE; + pTime->minute = srSLMGParseInt32(&pszTS); + if(pTime->minute < 0 || pTime->minute > 59) + return FALSE; + + if(*pszTS++ != ':') + return FALSE; + pTime->second = srSLMGParseInt32(&pszTS); + if(pTime->second < 0 || pTime->second > 60) + return FALSE; + if(*pszTS++ != ':') + + /* OK, we actually have a 3164 timestamp, so let's indicate this + * and fill the rest of the properties. */ + pTime->timeType = 1; + pTime->secfracPrecision = 0; + pTime->secfrac = 0; + return TRUE; +} + +/******************************************************************* + * END CODE-LIBLOGGING * + *******************************************************************/ + +/** + * Format a syslogTimestamp into format required by MySQL. + * We are using the 14 digits format. For example 20041111122600 + * is interpreted as '2004-11-11 12:26:00'. + * The caller must provide the timestamp as well as a character + * buffer that will receive the resulting string. The function + * returns the size of the timestamp written in bytes (without + * the string terminator). If 0 is returend, an error occured. + */ +int formatTimestampToMySQL(struct syslogTime *ts, char* pDst, size_t iLenDst) +{ + /* currently we do not consider localtime/utc. This may later be + * added. If so, I recommend using a property replacer option + * and/or a global configuration option. However, we should wait + * on user requests for this feature before doing anything. + * rgerhards, 2007-06-26 + */ + assert(ts != NULL); + assert(pDst != NULL); + + if (iLenDst < 15) /* we need at least 14 bytes + 14 digits for timestamp + '\n' */ + return(0); + + return(snprintf(pDst, iLenDst, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d", + ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); + +} + +int formatTimestampToPgSQL(struct syslogTime *ts, char *pDst, size_t iLenDst) +{ + /* see note in formatTimestampToMySQL, applies here as well */ + assert(ts != NULL); + assert(pDst != NULL); + + if (iLenDst < 21) /* we need 20 bytes + '\n' */ + return(0); + + return(snprintf(pDst, iLenDst, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", + ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second)); +} + +/** + * Format a syslogTimestamp to a RFC3339 timestamp string (as + * specified in syslog-protocol). + * The caller must provide the timestamp as well as a character + * buffer that will receive the resulting string. The function + * returns the size of the timestamp written in bytes (without + * the string terminator). If 0 is returend, an error occured. + */ +int formatTimestamp3339(struct syslogTime *ts, char* pBuf, size_t iLenBuf) +{ + int iRet; + char szTZ[7]; /* buffer for TZ information */ + + assert(ts != NULL); + assert(pBuf != NULL); + + if(iLenBuf < 20) + return(0); /* we NEED at least 20 bytes */ + + /* do TZ information first, this is easier to take care of "Z" zone in rfc3339 */ + if(ts->OffsetMode == 'Z') { + szTZ[0] = 'Z'; + szTZ[1] = '\0'; + } else { + snprintf(szTZ, sizeof(szTZ) / sizeof(char), "%c%2.2d:%2.2d", + ts->OffsetMode, ts->OffsetHour, ts->OffsetMinute); + } + + if(ts->secfracPrecision > 0) + { /* we now need to include fractional seconds. While doing so, we must look at + * the precision specified. For example, if we have millisec precision (3 digits), a + * secFrac value of 12 is not equivalent to ".12" but ".012". Obviously, this + * is a huge difference ;). To avoid this, we first create a format string with + * the specific precision and *then* use that format string to do the actual + * formating (mmmmhhh... kind of self-modifying code... ;)). + */ + char szFmtStr[64]; + /* be careful: there is ONE actual %d in the format string below ;) */ + snprintf(szFmtStr, sizeof(szFmtStr), + "%%04d-%%02d-%%02dT%%02d:%%02d:%%02d.%%0%dd%%s", + ts->secfracPrecision); + iRet = snprintf(pBuf, iLenBuf, szFmtStr, ts->year, ts->month, ts->day, + ts->hour, ts->minute, ts->second, ts->secfrac, szTZ); + } + else + iRet = snprintf(pBuf, iLenBuf, + "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d%s", + ts->year, ts->month, ts->day, + ts->hour, ts->minute, ts->second, szTZ); + return(iRet); +} + +/** + * Format a syslogTimestamp to a RFC3164 timestamp sring. + * The caller must provide the timestamp as well as a character + * buffer that will receive the resulting string. The function + * returns the size of the timestamp written in bytes (without + * the string termnator). If 0 is returend, an error occured. + */ +int formatTimestamp3164(struct syslogTime *ts, char* pBuf, size_t iLenBuf) +{ + static char* monthNames[13] = {"ERR", "Jan", "Feb", "Mar", + "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec"}; + assert(ts != NULL); + assert(pBuf != NULL); + + if(iLenBuf < 16) + return(0); /* we NEED 16 bytes */ + return(snprintf(pBuf, iLenBuf, "%s %2d %2.2d:%2.2d:%2.2d", + monthNames[ts->month], ts->day, ts->hour, + ts->minute, ts->second + )); +} + +/** + * Format a syslogTimestamp to a text format. + * The caller must provide the timestamp as well as a character + * buffer that will receive the resulting string. The function + * returns the size of the timestamp written in bytes (without + * the string termnator). If 0 is returend, an error occured. + */ +#if 0 /* This method is currently not called, be we like to preserve it */ +static int formatTimestamp(struct syslogTime *ts, char* pBuf, size_t iLenBuf) +{ + assert(ts != NULL); + assert(pBuf != NULL); + + if(ts->timeType == 1) { + return(formatTimestamp3164(ts, pBuf, iLenBuf)); + } + + if(ts->timeType == 2) { + return(formatTimestamp3339(ts, pBuf, iLenBuf)); + } + + return(0); +} +#endif +/* queryInterface function + * rgerhards, 2008-03-05 + */ +BEGINobjQueryInterface(datetime) +CODESTARTobjQueryInterface(datetime) + if(pIf->ifVersion != datetimeCURR_IF_VERSION) { /* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->getCurrTime = getCurrTime; + pIf->ParseTIMESTAMP3339 = ParseTIMESTAMP3339; + pIf->ParseTIMESTAMP3164 = ParseTIMESTAMP3164; + pIf->formatTimestampToMySQL = formatTimestampToMySQL; + pIf->formatTimestampToPgSQL = formatTimestampToPgSQL; + pIf->formatTimestamp3339 = formatTimestamp3339; + pIf->formatTimestamp3164 = formatTimestamp3164; +finalize_it: +ENDobjQueryInterface(datetime) + + +/* Initialize the datetime class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-02-19 + */ +BEGINAbstractObjClassInit(datetime, 1, OBJ_IS_CORE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + +ENDObjClassInit(datetime) + +/* vi:set ai: + */ diff --git a/runtime/datetime.h b/runtime/datetime.h new file mode 100644 index 00000000..fcb78172 --- /dev/null +++ b/runtime/datetime.h @@ -0,0 +1,52 @@ +/* The datetime object. Contains time-related functions. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef INCLUDED_DATETIME_H +#define INCLUDED_DATETIME_H + +#include "datetime.h" + +/* TODO: define error codes */ +#define NO_ERRCODE -1 + +/* the datetime object */ +typedef struct datetime_s { +} datetime_t; + + +/* interfaces */ +BEGINinterface(datetime) /* name must also be changed in ENDinterface macro! */ + void (*getCurrTime)(struct syslogTime *t); + //static int srSLMGParseInt32(char** ppsz); + int (*ParseTIMESTAMP3339)(struct syslogTime *pTime, char** ppszTS); + int (*ParseTIMESTAMP3164)(struct syslogTime *pTime, char* pszTS); + int (*formatTimestampToMySQL)(struct syslogTime *ts, char* pDst, size_t iLenDst); + int (*formatTimestampToPgSQL)(struct syslogTime *ts, char *pDst, size_t iLenDst); + int (*formatTimestamp3339)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); + int (*formatTimestamp3164)(struct syslogTime *ts, char* pBuf, size_t iLenBuf); +ENDinterface(datetime) +#define datetimeCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + +/* prototypes */ +PROTOTYPEObj(datetime); + +#endif /* #ifndef INCLUDED_DATETIME_H */ diff --git a/runtime/debug.c b/runtime/debug.c new file mode 100644 index 00000000..53624e38 --- /dev/null +++ b/runtime/debug.c @@ -0,0 +1,1332 @@ +/* debug.c + * + * This file proides debug and run time error analysis support. Some of the + * settings are very performance intense and my be turned off during a release + * build. + * + * File begun on 2008-01-22 by RGerhards + * + * Some functions are controlled by environment variables: + * + * RSYSLOG_DEBUGLOG if set, a debug log file is written to that location + * RSYSLOG_DEBUG specific debug options + * + * For details, visit doc/debug.html + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" /* autotools! */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rsyslog.h" +#include "debug.h" +#include "atomic.h" +#include "obj.h" + + +/* static data (some time to be replaced) */ +DEFobjCurrIf(obj) +int Debug; /* debug flag - read-only after startup */ +int debugging_on = 0; /* read-only, except on sig USR1 */ +static int bLogFuncFlow = 0; /* shall the function entry and exit be logged to the debug log? */ +static int bLogAllocFree = 0; /* shall calls to (m/c)alloc and free be logged to the debug log? */ +static int bPrintFuncDBOnExit = 0; /* shall the function entry and exit be logged to the debug log? */ +static int bPrintMutexAction = 0; /* shall mutex calls be printed to the debug log? */ +static int bPrintTime = 1; /* print a timestamp together with debug message */ +static int bPrintAllDebugOnExit = 0; +static int bAbortTrace = 1; /* print a trace after SIGABRT or SIGSEGV */ +static char *pszAltDbgFileName = NULL; /* if set, debug output is *also* sent to here */ +static FILE *altdbg = NULL; /* and the handle for alternate debug output */ +static FILE *stddbg; + +/* list of files/objects that should be printed */ +typedef struct dbgPrintName_s { + uchar *pName; + struct dbgPrintName_s *pNext; +} dbgPrintName_t; + + +/* forward definitions */ +static void dbgGetThrdName(char *pszBuf, size_t lenBuf, pthread_t thrd, int bIncludeNumID); +static dbgThrdInfo_t *dbgGetThrdInfo(void); +static int dbgPrintNameIsInList(const uchar *pName, dbgPrintName_t *pRoot); + + +/* This lists are single-linked and members are added at the top */ +static dbgPrintName_t *printNameFileRoot = NULL; + + +/* list of all known FuncDBs. We use a special list, because it must only be single-linked. As + * functions never disappear, we only need to add elements when we see a new one and never need + * to remove anything. For this, we simply add at the top, which saves us a Last pointer. The goal + * is to use as few memory as possible. + */ +typedef struct dbgFuncDBListEntry_s { + dbgFuncDB_t *pFuncDB; + struct dbgFuncDBListEntry_s *pNext; +} dbgFuncDBListEntry_t; +dbgFuncDBListEntry_t *pFuncDBListRoot; + +static pthread_mutex_t mutFuncDBList; + +typedef struct dbgMutLog_s { + struct dbgMutLog_s *pNext; + struct dbgMutLog_s *pPrev; + pthread_mutex_t *mut; + pthread_t thrd; + dbgFuncDB_t *pFuncDB; + int lockLn; /* the actual line where the mutex was locked */ + short mutexOp; +} dbgMutLog_t; +static dbgMutLog_t *dbgMutLogListRoot = NULL; +static dbgMutLog_t *dbgMutLogListLast = NULL; +static pthread_mutex_t mutMutLog; + + +static dbgThrdInfo_t *dbgCallStackListRoot = NULL; +static dbgThrdInfo_t *dbgCallStackListLast = NULL; +static pthread_mutex_t mutCallStack; + +static pthread_mutex_t mutdbgprintf; +static pthread_mutex_t mutdbgoprint; + +static pthread_key_t keyCallStack; + + +/* we do not have templates, so we use some macros to create linked list handlers + * for the several types + * DLL means "doubly linked list" + * rgerhards, 2008-01-23 + */ +#define DLL_Del(type, pThis) \ + if(pThis->pPrev != NULL) \ + pThis->pPrev->pNext = pThis->pNext; \ + if(pThis->pNext != NULL) \ + pThis->pNext->pPrev = pThis->pPrev; \ + if(pThis == dbg##type##ListRoot) \ + dbg##type##ListRoot = pThis->pNext; \ + if(pThis == dbg##type##ListLast) \ + dbg##type##ListLast = pThis->pPrev; \ + free(pThis); + +#define DLL_Add(type, pThis) \ + if(dbg##type##ListRoot == NULL) { \ + dbg##type##ListRoot = pThis; \ + dbg##type##ListLast = pThis; \ + } else { \ + pThis->pPrev = dbg##type##ListLast; \ + dbg##type##ListLast->pNext = pThis; \ + dbg##type##ListLast = pThis; \ + } + +/* we need to do our own mutex cancel cleanup handler as it shall not + * be subject to the debugging instrumentation (that would probably run us + * into an infinite loop + */ +static void dbgMutexCancelCleanupHdlr(void *pmut) +{ + pthread_mutex_unlock((pthread_mutex_t*) pmut); +} + + +/* handler to update the last execution location seen + * rgerhards, 2008-01-28 + */ +static inline void +dbgRecordExecLocation(int iStackPtr, int line) +{ + dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); + pThrd->lastLine[iStackPtr] = line; +} + + +/* ------------------------- mutex tracking code ------------------------- */ + +/* ------------------------- FuncDB utility functions ------------------------- */ + +#define SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ((int) (sizeof(pFuncDB->mutInfo) / sizeof(dbgFuncDBmutInfoEntry_t))) + +/* print a FuncDB + */ +static void dbgFuncDBPrint(dbgFuncDB_t *pFuncDB) +{ + assert(pFuncDB != NULL); + assert(pFuncDB->magic == dbgFUNCDB_MAGIC); + /* make output suitable for sorting on invocation count */ + dbgprintf("%10.10ld times called: %s:%d:%s\n", pFuncDB->nTimesCalled, pFuncDB->file, pFuncDB->line, pFuncDB->func); +} + + +/* print all funcdb entries + */ +static void dbgFuncDBPrintAll(void) +{ + dbgFuncDBListEntry_t *pFuncDBList; + int nFuncs = 0; + + for(pFuncDBList = pFuncDBListRoot ; pFuncDBList != NULL ; pFuncDBList = pFuncDBList->pNext) { + dbgFuncDBPrint(pFuncDBList->pFuncDB); + nFuncs++; + } + + dbgprintf("%d unique functions called\n", nFuncs); +} + + +/* find a mutex inside the FuncDB mutex table. Returns NULL if not found. Only mutexes from the same thread + * are found. + */ +static inline dbgFuncDBmutInfoEntry_t *dbgFuncDBGetMutexInfo(dbgFuncDB_t *pFuncDB, pthread_mutex_t *pmut) +{ + int i; + int iFound = -1; + pthread_t ourThrd = pthread_self(); + + for(i = 0 ; i < SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ; ++i) { + if(pFuncDB->mutInfo[i].pmut == pmut && pFuncDB->mutInfo[i].lockLn != -1 && pFuncDB->mutInfo[i].thrd == ourThrd) { + iFound = i; + break; + } + } + + return (iFound == -1) ? NULL : &pFuncDB->mutInfo[i]; +} + + +/* print any mutex that can be found in the FuncDB. Custom header is provided. + * "thrd" is the thread that is searched. If it is 0, mutexes for all threads + * shall be printed. + */ +static inline void +dbgFuncDBPrintActiveMutexes(dbgFuncDB_t *pFuncDB, char *pszHdrText, pthread_t thrd) +{ + int i; + char pszThrdName[64]; + + for(i = 0 ; i < SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ; ++i) { + if(pFuncDB->mutInfo[i].lockLn != -1 && (thrd == 0 || thrd == pFuncDB->mutInfo[i].thrd)) { + dbgGetThrdName(pszThrdName, sizeof(pszThrdName), pFuncDB->mutInfo[i].thrd, 1); + dbgprintf("%s:%d:%s:invocation %ld: %s %p[%d/%s]\n", pFuncDB->file, pFuncDB->line, pFuncDB->func, + pFuncDB->mutInfo[i].lInvocation, pszHdrText, (void*)pFuncDB->mutInfo[i].pmut, i, + pszThrdName); + } + } +} + +/* find a free mutex info spot in FuncDB. NULL is returned if table is full. + */ +static inline dbgFuncDBmutInfoEntry_t *dbgFuncDBFindFreeMutexInfo(dbgFuncDB_t *pFuncDB) +{ + int i; + int iFound = -1; + + for(i = 0 ; i < SIZE_FUNCDB_MUTEX_TABLE(pFuncDB) ; ++i) { + if(pFuncDB->mutInfo[i].lockLn == -1) { + iFound = i; + break; + } + } + + if(iFound == -1) { + dbgprintf("%s:%d:%s: INFO: out of space in FuncDB for mutex info (max %d entries) - ignoring\n", + pFuncDB->file, pFuncDB->line, pFuncDB->func, SIZE_FUNCDB_MUTEX_TABLE(pFuncDB)); + } + + return (iFound == -1) ? NULL : &pFuncDB->mutInfo[i]; +} + +/* add a mutex lock to the FuncDB. If the size is exhausted, info is discarded. + */ +static inline void dbgFuncDBAddMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_t *pmut, int lockLn) +{ + dbgFuncDBmutInfoEntry_t *pMutInfo; + + if((pMutInfo = dbgFuncDBFindFreeMutexInfo(pFuncDB)) != NULL) { + pMutInfo->pmut = pmut; + pMutInfo->lockLn = lockLn; + pMutInfo->lInvocation = pFuncDB->nTimesCalled; + pMutInfo->thrd = pthread_self(); + } +} + +/* remove a locked mutex from the FuncDB (unlock case!). + */ +static inline void dbgFuncDBRemoveMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_t *pmut) +{ + dbgFuncDBmutInfoEntry_t *pMutInfo; + + if((pMutInfo = dbgFuncDBGetMutexInfo(pFuncDB, pmut)) != NULL) { + pMutInfo->lockLn = -1; + } +} + + +/* ------------------------- END FuncDB utility functions ------------------------- */ + +/* ########################################################################### + * IMPORTANT NOTE + * Mutex instrumentation reduces the code's concurrency and thus affects its + * order of execution. It is vital to test the code also with mutex + * instrumentation turned off! Some bugs may not show up while it on... + * ########################################################################### + */ + +/* constructor & add new entry to list + */ +dbgMutLog_t *dbgMutLogAddEntry(pthread_mutex_t *pmut, short mutexOp, dbgFuncDB_t *pFuncDB, int lockLn) +{ + dbgMutLog_t *pLog; + + pLog = calloc(1, sizeof(dbgMutLog_t)); + assert(pLog != NULL); + + /* fill data members */ + pLog->mut = pmut; + pLog->thrd = pthread_self(); + pLog->mutexOp = mutexOp; + pLog->lockLn = lockLn; + pLog->pFuncDB = pFuncDB; + + DLL_Add(MutLog, pLog); + + return pLog; +} + + +/* destruct log entry + */ +void dbgMutLogDelEntry(dbgMutLog_t *pLog) +{ + assert(pLog != NULL); + DLL_Del(MutLog, pLog); +} + + +/* print a single mutex log entry */ +static void dbgMutLogPrintOne(dbgMutLog_t *pLog) +{ + char *strmutop; + char buf[64]; + char pszThrdName[64]; + + assert(pLog != NULL); + switch(pLog->mutexOp) { + case MUTOP_LOCKWAIT: + strmutop = "waited on"; + break; + case MUTOP_LOCK: + strmutop = "owned"; + break; + default: + snprintf(buf, sizeof(buf)/sizeof(char), "unknown state %d - should not happen!", pLog->mutexOp); + strmutop = buf; + break; + } + + dbgGetThrdName(pszThrdName, sizeof(pszThrdName), pLog->thrd, 1); + dbgprintf("mutex 0x%lx is being %s by code at %s:%d, thread %s\n", (unsigned long) pLog->mut, + strmutop, pLog->pFuncDB->file, + (pLog->mutexOp == MUTOP_LOCK) ? pLog->lockLn : pLog->pFuncDB->line, + pszThrdName); +} + +/* print the complete mutex log */ +static void dbgMutLogPrintAll(void) +{ + dbgMutLog_t *pLog; + + dbgprintf("Mutex log for all known mutex operations:\n"); + for(pLog = dbgMutLogListRoot ; pLog != NULL ; pLog = pLog->pNext) + dbgMutLogPrintOne(pLog); + +} + + +/* find the last log entry for that specific mutex object. Is used to delete + * a thread's own requests. Searches occur from the back. + * The pFuncDB is optional and may be NULL to indicate no specific funciont is + * reqested (aka "it is ignored" ;)). This is important for the unlock case. + */ +dbgMutLog_t *dbgMutLogFindSpecific(pthread_mutex_t *pmut, short mutop, dbgFuncDB_t *pFuncDB) +{ + dbgMutLog_t *pLog; + pthread_t mythrd = pthread_self(); + + pLog = dbgMutLogListLast; + while(pLog != NULL) { + if( pLog->mut == pmut && pLog->thrd == mythrd && pLog->mutexOp == mutop + && (pFuncDB == NULL || pLog->pFuncDB == pFuncDB)) + break; + pLog = pLog->pPrev; + } + + return pLog; +} + + +/* find mutex object from the back of the list */ +dbgMutLog_t *dbgMutLogFindFromBack(pthread_mutex_t *pmut, dbgMutLog_t *pLast) +{ + dbgMutLog_t *pLog; + + if(pLast == NULL) + pLog = dbgMutLogListLast; + else + pLog = pLast->pPrev; /* if we get the last processed one, we need to go one before it, else its an endless loop */ + + while(pLog != NULL) { + if(pLog->mut == pmut) { + break; + } + pLog = pLog->pPrev; + } + + return pLog; +} + + +/* find lock aquire for mutex from back of list */ +dbgMutLog_t *dbgMutLogFindHolder(pthread_mutex_t *pmut) +{ + dbgMutLog_t *pLog; + + pLog = dbgMutLogFindFromBack(pmut, NULL); + while(pLog != NULL) { + if(pLog->mutexOp == MUTOP_LOCK) + break; + pLog = dbgMutLogFindFromBack(pmut, pLog); + } + + return pLog; +} + +/* report wait on a mutex and add it to the mutex log */ +static inline void dbgMutexPreLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln) +{ + dbgMutLog_t *pHolder; + dbgMutLog_t *pLog; + char pszBuf[128]; + char pszHolderThrdName[64]; + char *pszHolder; + + pthread_mutex_lock(&mutMutLog); + pHolder = dbgMutLogFindHolder(pmut); + pLog = dbgMutLogAddEntry(pmut, MUTOP_LOCKWAIT, pFuncDB, ln); + + if(pHolder == NULL) + pszHolder = "[NONE]"; + else { + dbgGetThrdName(pszHolderThrdName, sizeof(pszHolderThrdName), pHolder->thrd, 1); + snprintf(pszBuf, sizeof(pszBuf)/sizeof(char), "%s:%d [%s]", pHolder->pFuncDB->file, pHolder->lockLn, pszHolderThrdName); + pszHolder = pszBuf; + } + + if(bPrintMutexAction) + dbgprintf("%s:%d:%s: mutex %p waiting on lock, held by %s\n", pFuncDB->file, ln, pFuncDB->func, (void*)pmut, pszHolder); + pthread_mutex_unlock(&mutMutLog); +} + + +/* report aquired mutex */ +static inline void dbgMutexLockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int lockLn) +{ + dbgMutLog_t *pLog; + + pthread_mutex_lock(&mutMutLog); + + /* find and delete "waiting" entry */ + pLog = dbgMutLogFindSpecific(pmut, MUTOP_LOCKWAIT, pFuncDB); + assert(pLog != NULL); + dbgMutLogDelEntry(pLog); + + /* add "lock" entry */ + pLog = dbgMutLogAddEntry(pmut, MUTOP_LOCK, pFuncDB, lockLn); + dbgFuncDBAddMutexLock(pFuncDB, pmut, lockLn); + pthread_mutex_unlock(&mutMutLog); + if(bPrintMutexAction) + dbgprintf("%s:%d:%s: mutex %p aquired\n", pFuncDB->file, lockLn, pFuncDB->func, (void*)pmut); +} + +/* if we unlock, we just remove the lock aquired entry from the log list */ +static inline void dbgMutexUnlockLog(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int unlockLn) +{ + dbgMutLog_t *pLog; + + pthread_mutex_lock(&mutMutLog); + pLog = dbgMutLogFindSpecific(pmut, MUTOP_LOCK, NULL); + assert(pLog != NULL); + + /* we found the last lock entry. We now need to see from which FuncDB we need to + * remove it. This is recorded inside the mutex log entry. + */ + dbgFuncDBRemoveMutexLock(pLog->pFuncDB, pmut); + + /* donw with the log entry, get rid of it... */ + dbgMutLogDelEntry(pLog); + + pthread_mutex_unlock(&mutMutLog); + if(bPrintMutexAction) + dbgprintf("%s:%d:%s: mutex %p UNlocked\n", pFuncDB->file, unlockLn, pFuncDB->func, (void*)pmut); +} + + +/* wrapper for pthread_mutex_lock() */ +int dbgMutexLock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) +{ + int ret; + + dbgRecordExecLocation(iStackPtr, ln); + dbgMutexPreLockLog(pmut, pFuncDB, ln); + ret = pthread_mutex_lock(pmut); + if(ret == 0) { + dbgMutexLockLog(pmut, pFuncDB, ln); + } else { + dbgprintf("%s:%d:%s: ERROR: pthread_mutex_lock() for mutex %p failed with error %d\n", + pFuncDB->file, ln, pFuncDB->func, (void*)pmut, ret); + } + + return ret; +} + + +/* wrapper for pthread_mutex_unlock() */ +int dbgMutexUnlock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) +{ + int ret; + dbgRecordExecLocation(iStackPtr, ln); + dbgMutexUnlockLog(pmut, pFuncDB, ln); + ret = pthread_mutex_unlock(pmut); + return ret; +} + + +/* wrapper for pthread_cond_wait() */ +int dbgCondWait(pthread_cond_t *cond, pthread_mutex_t *pmut, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) +{ + int ret; + dbgRecordExecLocation(iStackPtr, ln); + dbgMutexUnlockLog(pmut, pFuncDB, ln); + if(bPrintMutexAction) { + dbgprintf("%s:%d:%s: mutex %p waiting on condition %p\n", pFuncDB->file, pFuncDB->line, + pFuncDB->func, (void*)pmut, (void*)cond); + } + dbgMutexPreLockLog(pmut, pFuncDB, ln); + ret = pthread_cond_wait(cond, pmut); + return ret; +} + + +/* wrapper for pthread_cond_timedwait() */ +int dbgCondTimedWait(pthread_cond_t *cond, pthread_mutex_t *pmut, const struct timespec *abstime, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) +{ + int ret; + dbgRecordExecLocation(iStackPtr, ln); + dbgMutexUnlockLog(pmut, pFuncDB, ln); + dbgMutexPreLockLog(pmut, pFuncDB, ln); + if(bPrintMutexAction) { + dbgprintf("%s:%d:%s: mutex %p waiting on condition %p (with timeout)\n", pFuncDB->file, + pFuncDB->line, pFuncDB->func, (void*)pmut, (void*)cond); + } + ret = pthread_cond_timedwait(cond, pmut, abstime); + dbgMutexLockLog(pmut, pFuncDB, ln); + return ret; +} + + +/* ------------------------- end mutex tracking code ------------------------- */ + + +/* ------------------------- malloc/free tracking code ------------------------- */ + +/* wrapper for free() */ +void dbgFree(void *pMem, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr) +{ + dbgRecordExecLocation(iStackPtr, ln); + if(bLogAllocFree) { + dbgprintf("%s:%d:%s: free %p\n", pFuncDB->file, ln, pFuncDB->func, (void*) pMem); + } + free(pMem); +} + + +/* ------------------------- end malloc/free tracking code ------------------------- */ + +/* ------------------------- thread tracking code ------------------------- */ + +/* get ptr to call stack - if none exists, create a new stack + */ +static dbgThrdInfo_t *dbgGetThrdInfo(void) +{ + dbgThrdInfo_t *pThrd; + + pthread_mutex_lock(&mutCallStack); + if((pThrd = pthread_getspecific(keyCallStack)) == NULL) { + /* construct object */ + pThrd = calloc(1, sizeof(dbgThrdInfo_t)); + pThrd->thrd = pthread_self(); + (void) pthread_setspecific(keyCallStack, pThrd); + DLL_Add(CallStack, pThrd); + } + pthread_mutex_unlock(&mutCallStack); + return pThrd; +} + + + +/* find a specific thread ID. It must be present, else something is wrong + */ +static inline dbgThrdInfo_t *dbgFindThrd(pthread_t thrd) +{ + dbgThrdInfo_t *pThrd; + + for(pThrd = dbgCallStackListRoot ; pThrd != NULL ; pThrd = pThrd->pNext) { + if(pThrd->thrd == thrd) + break; + } + return pThrd; +} + + +/* build a string with the thread name. If none is set, the thread ID is + * used instead. Caller must provide buffer space. If bIncludeNumID is set + * to 1, the numerical ID is always included. + * rgerhards 2008-01-23 + */ +static void dbgGetThrdName(char *pszBuf, size_t lenBuf, pthread_t thrd, int bIncludeNumID) +{ + dbgThrdInfo_t *pThrd; + + assert(pszBuf != NULL); + + pThrd = dbgFindThrd(thrd); + + if(pThrd == 0 || pThrd->pszThrdName == NULL) { + /* no thread name, use numeric value */ + snprintf(pszBuf, lenBuf, "%lx", (long) thrd); + } else { + if(bIncludeNumID) { + snprintf(pszBuf, lenBuf, "%s (%lx)", pThrd->pszThrdName, (long) thrd); + } else { + snprintf(pszBuf, lenBuf, "%s", pThrd->pszThrdName); + } + } + +} + + +/* set a name for the current thread. The caller provided string is duplicated. + */ +void dbgSetThrdName(uchar *pszName) +{ + dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); + if(pThrd->pszThrdName != NULL) + free(pThrd->pszThrdName); + pThrd->pszThrdName = strdup((char*)pszName); +} + + +/* destructor for a call stack object */ +static void dbgCallStackDestruct(void *arg) +{ + dbgThrdInfo_t *pThrd = (dbgThrdInfo_t*) arg; + + dbgprintf("destructor for debug call stack %p called\n", pThrd); + if(pThrd->pszThrdName != NULL) { + free(pThrd->pszThrdName); + } + + pthread_mutex_lock(&mutCallStack); + DLL_Del(CallStack, pThrd); + pthread_mutex_unlock(&mutCallStack); +} + + +/* print a thread's call stack + */ +static void dbgCallStackPrint(dbgThrdInfo_t *pThrd) +{ + int i; + char pszThrdName[64]; + + pthread_mutex_lock(&mutCallStack); + dbgGetThrdName(pszThrdName, sizeof(pszThrdName), pThrd->thrd, 1); + dbgprintf("\n"); + dbgprintf("Recorded Call Order for Thread '%s':\n", pszThrdName); + for(i = 0 ; i < pThrd->stackPtr ; i++) { + dbgprintf("%d: %s:%d:%s:\n", i, pThrd->callStack[i]->file, pThrd->lastLine[i], pThrd->callStack[i]->func); + } + dbgprintf("maximum number of nested calls for this thread: %d.\n", pThrd->stackPtrMax); + dbgprintf("NOTE: not all calls may have been recorded, code does not currently guarantee that!\n"); + pthread_mutex_unlock(&mutCallStack); +} + +/* print all threads call stacks + */ +static void dbgCallStackPrintAll(void) +{ + dbgThrdInfo_t *pThrd; + /* stack info */ + for(pThrd = dbgCallStackListRoot ; pThrd != NULL ; pThrd = pThrd->pNext) { + dbgCallStackPrint(pThrd); + } +} + + +/* handler for SIGSEGV - MUST terminiate the app, but does so in a somewhat + * more meaningful way. + * rgerhards, 2008-01-22 + */ +void +sigsegvHdlr(int signum) +{ + char *signame; + struct sigaction sigAct; + + /* first, restore the default abort handler */ + memset(&sigAct, 0, sizeof (sigAct)); + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = SIG_DFL; + sigaction(SIGABRT, &sigAct, NULL); + + /* then do our actual processing */ + if(signum == SIGSEGV) { + signame = " (SIGSEGV)"; + } else if(signum == SIGABRT) { + signame = " (SIGABRT)"; + } else { + signame = ""; + } + + dbgprintf("\n\n\n\nSignal %d%s occured, execution must be terminated.\n\n\n\n", signum, signame); + + if(bAbortTrace) { + dbgPrintAllDebugInfo(); + dbgprintf("If the call trace is empty, you may want to ./configure --enable-rtinst\n"); + dbgprintf("\n\nTo submit bug reports, visit http://www.rsyslog.com/bugs\n\n"); + } + + dbgprintf("\n\nTo submit bug reports, visit http://www.rsyslog.com/bugs\n\n"); + if(stddbg != NULL) fflush(stddbg); + if(altdbg != NULL) fflush(altdbg); + + /* and finally abort... */ + /* TODO: think about restarting rsyslog in this case: may be a good idea, + * but may also be a very bad one (restart loops!) + */ + abort(); +} + + +/* print some debug output when an object is given + * This is mostly a copy of dbgprintf, but I do not know how to combine it + * into a single function as we have variable arguments and I don't know how to call + * from one vararg function into another. I don't dig in this, it is OK for the + * time being. -- rgerhards, 2008-01-29 + */ +void +dbgoprint(obj_t *pObj, char *fmt, ...) +{ + static pthread_t ptLastThrdID = 0; + static int bWasNL = 0; + va_list ap; + static char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ + static char pszWriteBuf[1024]; + size_t lenWriteBuf; + struct timespec t; + + if(!(Debug && debugging_on)) + return; + + /* a quick and very dirty hack to enable us to display just from those objects + * that we are interested in. So far, this must be changed at compile time (and + * chances are great it is commented out while you read it. Later, this shall + * be selectable via the environment. -- rgerhards, 2008-02-20 + */ +#if 0 + if(objGetObjID(pObj) != OBJexpr) + return; +#endif + + + pthread_mutex_lock(&mutdbgoprint); + pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgoprint); + + /* The bWasNL handler does not really work. It works if no thread + * switching occurs during non-NL messages. Else, things are messed + * up. Anyhow, it works well enough to provide useful help during + * getting this up and running. It is questionable if the extra effort + * is worth fixing it, giving the limited appliability. + * rgerhards, 2005-10-25 + * I have decided that it is not worth fixing it - especially as it works + * pretty well. + * rgerhards, 2007-06-15 + */ + if(ptLastThrdID != pthread_self()) { + if(!bWasNL) { + if(stddbg != NULL) fprintf(stddbg, "\n"); + if(altdbg != NULL) fprintf(altdbg, "\n"); + bWasNL = 1; + } + ptLastThrdID = pthread_self(); + } + + /* do not cache the thread name, as the caller might have changed it + * TODO: optimized, invalidate cache when new name is set + */ + dbgGetThrdName(pszThrdName, sizeof(pszThrdName), ptLastThrdID, 0); + + if(bWasNL) { + if(bPrintTime) { + clock_gettime(CLOCK_REALTIME, &t); + if(stddbg != NULL) fprintf(stddbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); + if(altdbg != NULL) fprintf(altdbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); + } + if(stddbg != NULL) fprintf(stddbg, "%s: ", pszThrdName); + if(altdbg != NULL) fprintf(altdbg, "%s: ", pszThrdName); + /* print object name header if we have an object */ + if(pObj != NULL) { + if(stddbg != NULL) fprintf(stddbg, "%s: ", obj.GetName(pObj)); + if(altdbg != NULL) fprintf(altdbg, "%s: ", obj.GetName(pObj)); + } + } + bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0; + va_start(ap, fmt); + lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); + if(lenWriteBuf >= sizeof(pszWriteBuf)) { + /* if our buffer was too small, we simply truncate. TODO: maybe something better? */ + lenWriteBuf = sizeof(pszWriteBuf) - 1; + } + va_end(ap); + /* + if(stddbg != NULL) fprintf(stddbg, "%s", pszWriteBuf); + if(altdbg != NULL) fprintf(altdbg, "%s", pszWriteBuf); + */ + if(stddbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, stddbg); + if(altdbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, altdbg); + + if(stddbg != NULL) fflush(stddbg); + if(altdbg != NULL) fflush(altdbg); + pthread_cleanup_pop(1); +} + + +/* print some debug output when no object is given + * WARNING: duplicate code, see dbgoprin above! + */ +void +dbgprintf(char *fmt, ...) +{ + static pthread_t ptLastThrdID = 0; + static int bWasNL = 0; + va_list ap; + static char pszThrdName[64]; /* 64 is to be on the safe side, anything over 20 is bad... */ + static char pszWriteBuf[1024]; + size_t lenWriteBuf; + struct timespec t; + + if(!(Debug && debugging_on)) + return; + + pthread_mutex_lock(&mutdbgprintf); + pthread_cleanup_push(dbgMutexCancelCleanupHdlr, &mutdbgprintf); + + /* The bWasNL handler does not really work. It works if no thread + * switching occurs during non-NL messages. Else, things are messed + * up. Anyhow, it works well enough to provide useful help during + * getting this up and running. It is questionable if the extra effort + * is worth fixing it, giving the limited appliability. + * rgerhards, 2005-10-25 + * I have decided that it is not worth fixing it - especially as it works + * pretty well. + * rgerhards, 2007-06-15 + */ + if(ptLastThrdID != pthread_self()) { + if(!bWasNL) { + if(stddbg != NULL) fprintf(stddbg, "\n"); + if(altdbg != NULL) fprintf(altdbg, "\n"); + bWasNL = 1; + } + ptLastThrdID = pthread_self(); + } + + /* do not cache the thread name, as the caller might have changed it + * TODO: optimized, invalidate cache when new name is set + */ + dbgGetThrdName(pszThrdName, sizeof(pszThrdName), ptLastThrdID, 0); + + if(bWasNL) { + if(bPrintTime) { + clock_gettime(CLOCK_REALTIME, &t); + if(stddbg != NULL) fprintf(stddbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); + if(altdbg != NULL) fprintf(altdbg, "%4.4ld.%9.9ld:", (long) (t.tv_sec % 10000), t.tv_nsec); + } + if(stddbg != NULL) fprintf(stddbg, "%s: ", pszThrdName); + if(altdbg != NULL) fprintf(altdbg, "%s: ", pszThrdName); + } + bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0; + va_start(ap, fmt); + lenWriteBuf = vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); + if(lenWriteBuf >= sizeof(pszWriteBuf)) { + /* if our buffer was too small, we simply truncate. TODO: maybe something better? */ + lenWriteBuf = sizeof(pszWriteBuf) - 1; + } + va_end(ap); + /* + if(stddbg != NULL) fprintf(stddbg, "%s", pszWriteBuf); + if(altdbg != NULL) fprintf(altdbg, "%s", pszWriteBuf); + */ + if(stddbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, stddbg); + if(altdbg != NULL) fwrite(pszWriteBuf, lenWriteBuf, 1, altdbg); + + if(stddbg != NULL) fflush(stddbg); + if(altdbg != NULL) fflush(altdbg); + pthread_cleanup_pop(1); +} + +void tester(void) +{ +BEGINfunc +ENDfunc +} + +/* handler called when a function is entered. This function creates a new + * funcDB on the heap if the passed-in pointer is NULL. + */ +int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int line) +{ + int iStackPtr = 0; /* TODO: find some better default, this one hurts the least, but it is not clean */ + dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); + dbgFuncDBListEntry_t *pFuncDBListEntry; + unsigned int i; + dbgFuncDB_t *pFuncDB; + + assert(ppFuncDB != NULL); + assert(file != NULL); + assert(func != NULL); + pFuncDB = *ppFuncDB; + assert((pFuncDB == NULL) || (pFuncDB->magic == dbgFUNCDB_MAGIC)); + + if(pFuncDB == NULL) { + /* we do not yet have a funcDB and need to create a new one. We also add it + * to the linked list of funcDBs. Please note that when a module is unloaded and + * then reloaded again, we currently do not try to find its previous funcDB but + * instead create a duplicate. While finding the past one is straightforward, it + * opens up the question what to do with e.g. mutex data left in it. We do not + * yet see any need to handle these questions, so duplicaton seems to be the right + * thing to do. -- rgerhards, 2008-03-10 + */ + /* dbgprintf("%s:%d:%s: called first time, initializing FuncDB\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); */ + /* get a new funcDB and add it to the list (all of this is protected by the mutex) */ + pthread_mutex_lock(&mutFuncDBList); + if((pFuncDBListEntry = calloc(1, sizeof(dbgFuncDBListEntry_t))) == NULL) { + dbgprintf("Error %d allocating memory for FuncDB List entry, not adding\n", errno); + pthread_mutex_unlock(&mutFuncDBList); + goto exit_it; + } else { + if((pFuncDB = calloc(1, sizeof(dbgFuncDB_t))) == NULL) { + dbgprintf("Error %d allocating memory for FuncDB, not adding\n", errno); + free(pFuncDBListEntry); + pthread_mutex_unlock(&mutFuncDBList); + goto exit_it; + } else { + pFuncDBListEntry->pFuncDB = pFuncDB; + pFuncDBListEntry->pNext = pFuncDBListRoot; + pFuncDBListRoot = pFuncDBListEntry; + } + } + /* now intialize the funcDB + * note that we duplicate the strings, because the address provided may go away + * if a loadable module is unloaded! + */ + pFuncDB->magic = dbgFUNCDB_MAGIC; + pFuncDB->file = strdup(file); + pFuncDB->func = strdup(func); + pFuncDB->line = line; + pFuncDB->nTimesCalled = 0; + for(i = 0 ; i < sizeof(pFuncDB->mutInfo)/sizeof(dbgFuncDBmutInfoEntry_t) ; ++i) { + pFuncDB->mutInfo[i].lockLn = -1; /* set to not Locked */ + } + + /* a round of safety checks... */ + if(pFuncDB->file == NULL || pFuncDB->func == NULL) { + dbgprintf("Error %d allocating memory for FuncDB, not adding\n", errno); + /* do a little bit of cleanup */ + if(pFuncDB->file != NULL) + free(pFuncDB->file); + if(pFuncDB->func != NULL) + free(pFuncDB->func); + free(pFuncDB); + free(pFuncDBListEntry); + pthread_mutex_unlock(&mutFuncDBList); + goto exit_it; + } + + /* done mutex-protected operations */ + pthread_mutex_unlock(&mutFuncDBList); + + *ppFuncDB = pFuncDB; /* all went well, so we can update the caller */ + } + + /* when we reach this point, we have a fully-initialized FuncDB! */ + ATOMIC_INC(pFuncDB->nTimesCalled); + if(bLogFuncFlow && dbgPrintNameIsInList((const uchar*)pFuncDB->file, printNameFileRoot)) + dbgprintf("%s:%d: %s: enter\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); + if(pThrd->stackPtr >= (int) (sizeof(pThrd->callStack) / sizeof(dbgFuncDB_t*))) { + dbgprintf("%s:%d: %s: debug module: call stack for this thread full, suspending call tracking\n", + pFuncDB->file, pFuncDB->line, pFuncDB->func); + iStackPtr = pThrd->stackPtr; + } else { + iStackPtr = pThrd->stackPtr++; + if(pThrd->stackPtr > pThrd->stackPtrMax) + pThrd->stackPtrMax = pThrd->stackPtr; + pThrd->callStack[iStackPtr] = pFuncDB; + pThrd->lastLine[iStackPtr] = line; + } + +exit_it: + return iStackPtr; +} + + +/* handler called when a function is exited + */ +void dbgExitFunc(dbgFuncDB_t *pFuncDB, int iStackPtrRestore, int iRet) +{ + dbgThrdInfo_t *pThrd = dbgGetThrdInfo(); + + assert(iStackPtrRestore >= 0); + assert(pFuncDB != NULL); + assert(pFuncDB->magic == dbgFUNCDB_MAGIC); + + dbgFuncDBPrintActiveMutexes(pFuncDB, "WARNING: mutex still owned by us as we exit function, mutex: ", pthread_self()); + if(bLogFuncFlow && dbgPrintNameIsInList((const uchar*)pFuncDB->file, printNameFileRoot)) { + if(iRet == RS_RET_NO_IRET) + dbgprintf("%s:%d: %s: exit: (no iRet)\n", pFuncDB->file, pFuncDB->line, pFuncDB->func); + else + dbgprintf("%s:%d: %s: exit: %d\n", pFuncDB->file, pFuncDB->line, pFuncDB->func, iRet); + } + pThrd->stackPtr = iStackPtrRestore; + if(pThrd->stackPtr < 0) { + dbgprintf("Stack pointer for thread %lx below 0 - resetting (some RETiRet still wrong!)\n", (long) pthread_self()); + pThrd->stackPtr = 0; + } +} + + +/* externally-callable handler to record the last exec location. We use a different function + * so that the internal one can be inline. + */ +void +dbgSetExecLocation(int iStackPtr, int line) +{ + dbgRecordExecLocation(iStackPtr, line); +} + + +void dbgPrintAllDebugInfo(void) +{ + dbgCallStackPrintAll(); + dbgMutLogPrintAll(); + if(bPrintFuncDBOnExit) + dbgFuncDBPrintAll(); +} + + +/* Handler for SIGUSR2. Dumps all available debug output + */ +static void sigusr2Hdlr(int __attribute__((unused)) signum) +{ + dbgprintf("SIGUSR2 received, dumping debug information\n"); + dbgPrintAllDebugInfo(); +} + +/* support system to set debug options at runtime */ + + +/* parse a param/value pair from the current location of the + * option string. Returns 1 if an option was found, 0 + * otherwise. 0 means there are NO MORE options to be + * processed. -- rgerhards, 2008-02-28 + */ +static int +dbgGetRTOptNamVal(uchar **ppszOpt, uchar **ppOptName, uchar **ppOptVal) +{ + int bRet = 0; + uchar *p; + size_t i; + static uchar optname[128]; /* not thread- or reentrant-safe, but that */ + static uchar optval[1024]; /* doesn't matter (called only once at startup) */ + + assert(ppszOpt != NULL); + assert(*ppszOpt != NULL); + + /* make sure we have some initial values */ + optname[0] = '\0'; + optval[0] = '\0'; + + p = *ppszOpt; + /* skip whitespace */ + while(*p && isspace(*p)) + ++p; + + /* name - up until '=' or whitespace */ + i = 0; + while(i < (sizeof(optname)/sizeof(uchar) - 1) && *p && *p != '=' && !isspace(*p)) { + optname[i++] = *p++; + } + + if(i > 0) { + bRet = 1; + optname[i] = '\0'; + if(*p == '=') { + /* we have a value, get it */ + ++p; + i = 0; + while(i < (sizeof(optval)/sizeof(uchar) - 1) && *p && !isspace(*p)) { + optval[i++] = *p++; + } + optval[i] = '\0'; + } + } + + /* done */ + *ppszOpt = p; + *ppOptName = optname; + *ppOptVal = optval; + return bRet; +} + + +/* create new PrintName list entry and add it to list (they will never + * be removed. -- rgerhards, 2008-02-28 + */ +static void +dbgPrintNameAdd(uchar *pName, dbgPrintName_t **ppRoot) +{ + dbgPrintName_t *pEntry; + + if((pEntry = calloc(1, sizeof(dbgPrintName_t))) == NULL) { + fprintf(stderr, "ERROR: out of memory during debug setup\n"); + exit(1); + } + + if((pEntry->pName = (uchar*) strdup((char*) pName)) == NULL) { + fprintf(stderr, "ERROR: out of memory during debug setup\n"); + exit(1); + } + + if(*ppRoot != NULL) { + pEntry->pNext = *ppRoot; /* we enqueue at the front */ + } + *ppRoot = pEntry; + +printf("Name %s added to %p\n", pName, *ppRoot); +} + + +/* check if name is in a printName list - returns 1 if so, 0 otherwise. + * There is one special handling: if the root pointer is NULL, the function + * always returns 1. This is because when no name is set, output shall be + * unrestricted. + * rgerhards, 2008-02-28 + */ +static int +dbgPrintNameIsInList(const uchar *pName, dbgPrintName_t *pRoot) +{ + int bFound = 0; + dbgPrintName_t *pEntry = pRoot; + + if(pRoot == NULL) + bFound = 1; + + while(pEntry != NULL && !bFound) { + if(!strcasecmp((char*)pEntry->pName, (char*)pName)) { + bFound = 1; + } else { + pEntry = pEntry->pNext; + } + } + + return bFound; +} + + +/* read in the runtime options + * rgerhards, 2008-02-28 + */ +static void +dbgGetRuntimeOptions(void) +{ + uchar *pszOpts; + uchar *optval; + uchar *optname; + + /* set some defaults */ + stddbg = stdout; + + if((pszOpts = (uchar*) getenv("RSYSLOG_DEBUG")) != NULL) { + /* we have options set, so let's process them */ + while(dbgGetRTOptNamVal(&pszOpts, &optname, &optval)) { + if(!strcasecmp((char*)optname, "help")) { + fprintf(stderr, + "rsyslogd runtime debug support - help requested, rsyslog terminates\n\n" + "environment variables:\n" + "addional logfile: export RSYSLOG_DEBUGFILE=\"/path/to/file\"\n" + "to set: export RSYSLOG_DEBUG=\"cmd cmd cmd\"\n\n" + "Commands are (all case-insensitive):\n" + "help (this list, terminates rsyslogd\n" + "LogFuncFlow\n" + "LogAllocFree (very partly implemented)\n" + "PrintFuncDB\n" + "PrintMutexAction\n" + "PrintAllDebugInfoOnExit (not yet implemented)\n" + "NoLogTimestamp\n" + "Nostdoout\n" + "filetrace=file (may be provided multiple times)\n" + "\nSee debug.html in your doc set or http://www.rsyslog.com for details\n"); + exit(1); + } else if(!strcasecmp((char*)optname, "debug")) { + /* this is earlier in the process than the -d option, as such it + * allows us to spit out debug messages from the very beginning. + */ + Debug = 1; + debugging_on = 1; + } else if(!strcasecmp((char*)optname, "logfuncflow")) { + bLogFuncFlow = 1; + } else if(!strcasecmp((char*)optname, "logallocfree")) { + bLogAllocFree = 1; + } else if(!strcasecmp((char*)optname, "printfuncdb")) { + bPrintFuncDBOnExit = 1; + } else if(!strcasecmp((char*)optname, "printmutexaction")) { + bPrintMutexAction = 1; + } else if(!strcasecmp((char*)optname, "printalldebuginfoonexit")) { + bPrintAllDebugOnExit = 1; + } else if(!strcasecmp((char*)optname, "nologtimestamp")) { + bPrintTime = 0; + } else if(!strcasecmp((char*)optname, "nostdout")) { + stddbg = NULL; + } else if(!strcasecmp((char*)optname, "noaborttrace")) { + bAbortTrace = 0; + } else if(!strcasecmp((char*)optname, "filetrace")) { + if(*optval == '\0') { + fprintf(stderr, "Error: logfile debug option requires filename, " + "e.g. \"logfile=debug.c\"\n"); + exit(1); + } else { + /* create new entry and add it to list */ + dbgPrintNameAdd(optval, &printNameFileRoot); + } + } else { + fprintf(stderr, "Error: invalid debug option '%s', value '%s' - ignored\n", + optval, optname); + } + } + } +} + + +/* end support system to set debug options at runtime */ + +rsRetVal dbgClassInit(void) +{ + DEFiRet; + + struct sigaction sigAct; + sigset_t sigSet; + + (void) pthread_key_create(&keyCallStack, dbgCallStackDestruct); /* MUST be the first action done! */ + + /* we initialize all Mutexes with code, as some platforms seem to have + * bugs in the static initializer macros. So better be on the safe side... + * rgerhards, 2008-03-06 + */ + pthread_mutex_init(&mutFuncDBList, NULL); + pthread_mutex_init(&mutMutLog, NULL); + pthread_mutex_init(&mutCallStack, NULL); + pthread_mutex_init(&mutdbgprintf, NULL); + pthread_mutex_init(&mutdbgoprint, NULL); + + /* while we try not to use any of the real rsyslog code (to avoid infinite loops), we + * need to have the ability to query object names. Thus, we need to obtain a pointer to + * the object interface. -- rgerhards, 2008-02-29 + */ + CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ + + memset(&sigAct, 0, sizeof (sigAct)); + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = sigusr2Hdlr; + sigaction(SIGUSR2, &sigAct, NULL); + + sigemptyset(&sigSet); + sigaddset(&sigSet, SIGUSR2); + pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL); + + dbgGetRuntimeOptions(); /* init debug system from environment */ + pszAltDbgFileName = getenv("RSYSLOG_DEBUGLOG"); + + if(pszAltDbgFileName != NULL) { + /* we have a secondary file, so let's open it) */ + if((altdbg = fopen(pszAltDbgFileName, "w")) == NULL) { + fprintf(stderr, "alternate debug file could not be opened, ignoring. Error: %s\n", strerror(errno)); + } + } + + dbgSetThrdName((uchar*)"main thread"); + +finalize_it: + RETiRet; +} + + +rsRetVal dbgClassExit(void) +{ + dbgFuncDBListEntry_t *pFuncDBListEtry, *pToDel; + pthread_key_delete(keyCallStack); + + if(bPrintAllDebugOnExit) + dbgPrintAllDebugInfo(); + + if(altdbg != NULL) + fclose(altdbg); + + /* now free all of our memory to make the memory debugger happy... */ + pFuncDBListEtry = pFuncDBListRoot; + while(pFuncDBListEtry != NULL) { + pToDel = pFuncDBListEtry; + pFuncDBListEtry = pFuncDBListEtry->pNext; + free(pToDel->pFuncDB->file); + free(pToDel->pFuncDB->func); + free(pToDel->pFuncDB); + free(pToDel); + } + + return RS_RET_OK; +} +/* vi:set ai: + */ diff --git a/runtime/debug.h b/runtime/debug.h new file mode 100644 index 00000000..214b7c05 --- /dev/null +++ b/runtime/debug.h @@ -0,0 +1,146 @@ +/* debug.h + * + * Definitions for the debug and run-time analysis support module. + * Contains a lot of macros. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef DEBUG_H_INCLUDED +#define DEBUG_H_INCLUDED + +#include +#include "obj-types.h" + +/* external static data elements (some time to be replaced) */ +extern int Debug; /* debug flag - read-only after startup */ +extern int debugging_on; /* read-only, except on sig USR1 */ + +/* data types */ + +/* the function database. It is used as a static var inside each function. That provides + * us the fast access to it that we need to make the instrumentation work. It's address + * also serves as a unique function identifier and can be used inside other structures + * to refer to the function (e.g. for pretty-printing names). + * rgerhards, 2008-01-24 + */ +typedef struct dbgFuncDBmutInfoEntry_s { + pthread_mutex_t *pmut; + int lockLn; /* line where it was locked (inside our func): -1 means mutex is not locked */ + pthread_t thrd; /* thrd where the mutex was locked */ + unsigned long lInvocation; /* invocation (unique during program run!) of this function that locked the mutex */ +} dbgFuncDBmutInfoEntry_t; +typedef struct dbgFuncDB_s { + unsigned magic; + unsigned long nTimesCalled; + char *func; + char *file; + int line; + dbgFuncDBmutInfoEntry_t mutInfo[5]; + /* remember to update the initializer if you add anything or change the order! */ +} dbgFuncDB_t; +#define dbgFUNCDB_MAGIC 0xA1B2C3D4 +#define dbgFuncDB_t_INITIALIZER \ + { \ + .magic = dbgFUNCDB_MAGIC,\ + .nTimesCalled = 0,\ + .func = __func__, \ + .file = __FILE__, \ + .line = __LINE__ \ + } + +/* the structure below was originally just the thread's call stack, but it has + * a bit evolved over time. So we have now ended up with the fact that it + * all debug info we know about the thread. + */ +typedef struct dbgCallStack_s { + pthread_t thrd; + dbgFuncDB_t *callStack[500]; + int lastLine[500]; /* last line where code execution was seen */ + int stackPtr; + int stackPtrMax; + char *pszThrdName; + struct dbgCallStack_s *pNext; + struct dbgCallStack_s *pPrev; +} dbgThrdInfo_t; + + +/* prototypes */ +rsRetVal dbgClassInit(void); +rsRetVal dbgClassExit(void); +void sigsegvHdlr(int signum); +void dbgoprint(obj_t *pObj, char *fmt, ...) __attribute__((format(printf, 2, 3))); +void dbgprintf(char *fmt, ...) __attribute__((format(printf, 1, 2))); +int dbgMutexLock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); +int dbgMutexUnlock(pthread_mutex_t *pmut, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); +int dbgCondWait(pthread_cond_t *cond, pthread_mutex_t *pmut, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); +int dbgCondTimedWait(pthread_cond_t *cond, pthread_mutex_t *pmut, const struct timespec *abstime, dbgFuncDB_t *pFuncD, int ln, int iStackPtr); +void dbgFree(void *pMem, dbgFuncDB_t *pFuncDB, int ln, int iStackPtr); +int dbgEntrFunc(dbgFuncDB_t **ppFuncDB, const char *file, const char *func, int line); +void dbgExitFunc(dbgFuncDB_t *pFuncDB, int iStackPtrRestore, int iRet); +void dbgSetExecLocation(int iStackPtr, int line); +void dbgSetThrdName(uchar *pszName); +void dbgPrintAllDebugInfo(void); + +/* macros */ +#ifdef RTINST +# define BEGINfunc static dbgFuncDB_t *pdbgFuncDB; int dbgCALLStaCK_POP_POINT = dbgEntrFunc(&pdbgFuncDB, __FILE__, __func__, __LINE__); +# define ENDfunc dbgExitFunc(pdbgFuncDB, dbgCALLStaCK_POP_POINT, RS_RET_NO_IRET); +# define ENDfuncIRet dbgExitFunc(pdbgFuncDB, dbgCALLStaCK_POP_POINT, iRet); +# define ASSERT(x) assert(x) +#else +# define BEGINfunc +# define ENDfunc +# define ENDfuncIRet +# define ASSERT(x) +#endif +#ifdef RTINST +# define RUNLOG dbgSetExecLocation(dbgCALLStaCK_POP_POINT, __LINE__); dbgprintf("%s:%d: %s: log point\n", __FILE__, __LINE__, __func__) +# define RUNLOG_VAR(fmt, x) dbgSetExecLocation(dbgCALLStaCK_POP_POINT, __LINE__);\ + dbgprintf("%s:%d: %s: var '%s'[%s]: " fmt "\n", __FILE__, __LINE__, __func__, #x, fmt, x) +# define RUNLOG_STR(str) dbgSetExecLocation(dbgCALLStaCK_POP_POINT, __LINE__);\ + dbgprintf("%s:%d: %s: %s\n", __FILE__, __LINE__, __func__, str) +#else +# define RUNLOG +# define RUNLOG_VAR(fmt, x) +# define RUNLOG_STR(str) +#endif + +/* mutex operations */ +#define MUTOP_LOCKWAIT 1 +#define MUTOP_LOCK 2 +#define MUTOP_UNLOCK 3 + + +/* debug aides */ +#ifdef RTINST +#define d_pthread_mutex_lock(x) dbgMutexLock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) +#define d_pthread_mutex_unlock(x) dbgMutexUnlock(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) +#define d_pthread_cond_wait(cond, mut) dbgCondWait(cond, mut, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) +#define d_pthread_cond_timedwait(cond, mut, to) dbgCondTimedWait(cond, mut, to, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) +#define d_free(x) dbgFree(x, pdbgFuncDB, __LINE__, dbgCALLStaCK_POP_POINT ) +#else +#define d_pthread_mutex_lock(x) pthread_mutex_lock(x) +#define d_pthread_mutex_unlock(x) pthread_mutex_unlock(x) +#define d_pthread_cond_wait(cond, mut) pthread_cond_wait(cond, mut) +#define d_pthread_cond_timedwait(cond, mut, to) pthread_cond_timedwait(cond, mut, to) +#define d_free(x) free(x) +#endif +#endif /* #ifndef DEBUG_H_INCLUDED */ diff --git a/runtime/errmsg.c b/runtime/errmsg.c new file mode 100644 index 00000000..42f84724 --- /dev/null +++ b/runtime/errmsg.c @@ -0,0 +1,122 @@ +/* The errmsg object. + * + * Module begun 2008-03-05 by Rainer Gerhards, based on some code + * from syslogd.c. I converted this module to lgpl and have checked that + * all contributors agreed to that step. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#include "config.h" +#include +#include +#include +#include +#include + +#include "rsyslog.h" +#include "syslogd.h" +#include "obj.h" +#include "errmsg.h" +#include "sysvar.h" +#include "srUtils.h" +#include "stringbuf.h" + +/* static data */ +DEFobjStaticHelpers + + +/* ------------------------------ methods ------------------------------ */ + + +/* TODO: restructure this code some time. Especially look if we need + * to check errno and, if so, how to do that in a clean way. + */ +static void __attribute__((format(printf, 2, 3))) +LogError(int __attribute__((unused)) iErrCode, char *fmt, ... ) +{ + va_list ap; + char buf[1024]; + char msg[1024]; + char errStr[1024]; + size_t lenBuf; + + BEGINfunc + assert(fmt != NULL); + /* Format parameters */ + va_start(ap, fmt); + lenBuf = vsnprintf(buf, sizeof(buf), fmt, ap); + if(lenBuf >= sizeof(buf)) { + /* if our buffer was too small, we simply truncate. */ + lenBuf--; + } + va_end(ap); + + /* Log the error now */ + buf[sizeof(buf)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ + + dbgprintf("Called LogError, msg: %s\n", buf); + + if (errno == 0) { + snprintf(msg, sizeof(msg), "%s", buf); + } else { + rs_strerror_r(errno, errStr, sizeof(errStr)); + snprintf(msg, sizeof(msg), "%s: %s", buf, errStr); + } + msg[sizeof(msg)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ + errno = 0; + logmsgInternal(LOG_SYSLOG|LOG_ERR, msg, ADDDATE); + + ENDfunc +} + + +/* queryInterface function + * rgerhards, 2008-03-05 + */ +BEGINobjQueryInterface(errmsg) +CODESTARTobjQueryInterface(errmsg) + if(pIf->ifVersion != errmsgCURR_IF_VERSION) { /* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->LogError = LogError; +finalize_it: +ENDobjQueryInterface(errmsg) + + +/* Initialize the errmsg class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-02-19 + */ +BEGINAbstractObjClassInit(errmsg, 1, OBJ_IS_CORE_MODULE) /* class, version */ + /* request objects we use */ + + /* set our own handlers */ +ENDObjClassInit(errmsg) + +/* vi:set ai: + */ diff --git a/runtime/errmsg.h b/runtime/errmsg.h new file mode 100644 index 00000000..bde6bcff --- /dev/null +++ b/runtime/errmsg.h @@ -0,0 +1,46 @@ +/* The errmsg object. It is used to emit error message inside rsyslog. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef INCLUDED_ERRMSG_H +#define INCLUDED_ERRMSG_H + +#include "errmsg.h" + +/* TODO: define error codes */ +#define NO_ERRCODE -1 + +/* the errmsg object */ +typedef struct errmsg_s { +} errmsg_t; + + +/* interfaces */ +BEGINinterface(errmsg) /* name must also be changed in ENDinterface macro! */ + void __attribute__((format(printf, 2, 3))) (*LogError)(int iErrCode, char *pszErrFmt, ... ); +ENDinterface(errmsg) +#define errmsgCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + + +/* prototypes */ +PROTOTYPEObj(errmsg); + +#endif /* #ifndef INCLUDED_ERRMSG_H */ diff --git a/runtime/linkedlist.c b/runtime/linkedlist.c new file mode 100644 index 00000000..ce20651e --- /dev/null +++ b/runtime/linkedlist.c @@ -0,0 +1,414 @@ +/* linkedlist.c + * This file set implements a generic linked list object. It can be used + * wherever a linke list is required. + * + * NOTE: we do not currently provide a constructor and destructor for the + * object itself as we assume it will always be part of another strucuture. + * Having a pointer to it, I think, does not really make sense but costs + * performance. Consequently, there is is llInit() and llDestroy() and they + * do what a constructor and destructur do, except for creating the + * linkedList_t structure itself. + * + * File begun on 2007-07-31 by RGerhards + * + * Copyright (C) 2007, 2008 by Rainer Gerhards and Adiscon GmbH + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include +#include +#include + +#include "rsyslog.h" +#include "linkedlist.h" + + +/* Initialize an existing linkedList_t structure + * pKey destructor may be zero to take care of non-keyed lists. + */ +rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pKeyDestructor)(void*), int (*pCmpOp)()) +{ + assert(pThis != NULL); + assert(pEltDestructor != NULL); + + pThis->pEltDestruct = pEltDestructor; + pThis->pKeyDestruct = pKeyDestructor; + pThis->cmpOp = pCmpOp; + pThis->pKey = NULL; + pThis->iNumElts = 0; + pThis->pRoot = NULL; + pThis->pLast = NULL; + + return RS_RET_OK; +}; + + +/* llDestroyEltData - destroys a list element + * It is a separate function as the + * functionality is needed in multiple code-pathes. + */ +static rsRetVal llDestroyElt(linkedList_t *pList, llElt_t *pElt) +{ + DEFiRet; + + assert(pList != NULL); + assert(pElt != NULL); + + /* we ignore errors during destruction, as we need to try + * free the element in any case. + */ + if(pElt->pData != NULL) + pList->pEltDestruct(pElt->pData); + if(pElt->pKey != NULL) + pList->pKeyDestruct(pElt->pKey); + free(pElt); + pList->iNumElts--; /* one less */ + + RETiRet; +} + + +/* llDestroy - destroys a COMPLETE linkedList + */ +rsRetVal llDestroy(linkedList_t *pThis) +{ + DEFiRet; + llElt_t *pElt; + llElt_t *pEltPrev; + + assert(pThis != NULL); + + pElt = pThis->pRoot; + while(pElt != NULL) { + pEltPrev = pElt; + pElt = pElt->pNext; + /* we ignore errors during destruction, as we need to try + * finish the linked list in any case. + */ + llDestroyElt(pThis, pEltPrev); + } + /* now clean up the pointers */ + pThis->pRoot = NULL; + pThis->pLast = NULL; + + RETiRet; +} + +/* llDestroyRootElt - destroy the root element but otherwise + * keeps this list intact. -- rgerhards, 2007-08-03 + */ +rsRetVal llDestroyRootElt(linkedList_t *pThis) +{ + DEFiRet; + llElt_t *pPrev; + + if(pThis->pRoot == NULL) { + ABORT_FINALIZE(RS_RET_EMPTY_LIST); + } + + pPrev = pThis->pRoot; + if(pPrev->pNext == NULL) { + /* it was the only list element */ + pThis->pLast = NULL; + pThis->pRoot = NULL; + } else { + /* there are other list elements */ + pThis->pRoot = pPrev->pNext; + } + + CHKiRet(llDestroyElt(pThis, pPrev)); + +finalize_it: + RETiRet; +} + + +/* get next user data element of a linked list. The caller must also + * provide a "cookie" to the function. On initial call, it must be + * NULL. Other than that, the caller is not allowed to to modify the + * cookie. In the current implementation, the cookie is an actual + * pointer to the current list element, but this is nothing that the + * caller should rely on. + */ +rsRetVal llGetNextElt(linkedList_t *pThis, linkedListCookie_t *ppElt, void **ppUsr) +{ + llElt_t *pElt; + DEFiRet; + + assert(pThis != NULL); + assert(ppElt != NULL); + assert(ppUsr != NULL); + + pElt = *ppElt; + + pElt = (pElt == NULL) ? pThis->pRoot : pElt->pNext; + + if(pElt == NULL) { + iRet = RS_RET_END_OF_LINKEDLIST; + } else { + *ppUsr = pElt->pData; + } + + *ppElt = pElt; + + RETiRet; +} + + +/* return the key of an Elt + * rgerhards, 2007-09-11: note that ppDatea is actually a void**, + * but I need to make it a void* to avoid lots of compiler warnings. + * It will be converted later down in the code. + */ +rsRetVal llGetKey(llElt_t *pThis, void *ppData) +{ + assert(pThis != NULL); + assert(ppData != NULL); + + *(void**) ppData = pThis->pKey; + + return RS_RET_OK; +} + + +/* construct a new llElt_t + */ +static rsRetVal llEltConstruct(llElt_t **ppThis, void *pKey, void *pData) +{ + DEFiRet; + llElt_t *pThis; + + assert(ppThis != NULL); + + if((pThis = (llElt_t*) calloc(1, sizeof(llElt_t))) == NULL) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + pThis->pKey = pKey; + pThis->pData = pData; + +finalize_it: + *ppThis = pThis; + RETiRet; +} + + +/* append a user element to the end of the linked list. This includes setting a key. If no + * key is desired, simply pass in a NULL pointer for it. + */ +rsRetVal llAppend(linkedList_t *pThis, void *pKey, void *pData) +{ + llElt_t *pElt; + DEFiRet; + + CHKiRet(llEltConstruct(&pElt, pKey, pData)); + + pThis->iNumElts++; /* one more */ + if(pThis->pLast == NULL) { + pThis->pRoot = pElt; + } else { + pThis->pLast->pNext = pElt; + } + pThis->pLast = pElt; + +finalize_it: + RETiRet; +} + + +/* unlink a requested element. As we have singly-linked lists, the + * caller also needs to pass in the previous element (or NULL, if it is the + * root element). + * rgerhards, 2007-11-21 + */ +static rsRetVal llUnlinkElt(linkedList_t *pThis, llElt_t *pElt, llElt_t *pEltPrev) +{ + assert(pElt != NULL); + + if(pEltPrev == NULL) { /* root element? */ + pThis->pRoot = pElt->pNext; + } else { /* regular element */ + pEltPrev->pNext = pElt->pNext; + } + + if(pElt == pThis->pLast) + pThis->pLast = pEltPrev; + + return RS_RET_OK; +} + + +/* unlinks and immediately deletes an element. Previous element must + * be given (or zero if the root element is to be deleted). + * rgerhards, 2007-11-21 + */ +static rsRetVal llUnlinkAndDelteElt(linkedList_t *pThis, llElt_t *pElt, llElt_t *pEltPrev) +{ + DEFiRet; + + assert(pElt != NULL); + + CHKiRet(llUnlinkElt(pThis, pElt, pEltPrev)); + CHKiRet(llDestroyElt(pThis, pElt)); + +finalize_it: + RETiRet; +} + +/* find a user element based on the provided key - this is the + * internal variant, which also tracks the last element pointer + * before the found element. This is necessary to delete elements. + * NULL means there is no element in front of it, aka the found elt + * is the root elt. + * rgerhards, 2007-11-21 + */ +static rsRetVal llFindElt(linkedList_t *pThis, void *pKey, llElt_t **ppElt, llElt_t **ppEltPrev) +{ + DEFiRet; + llElt_t *pElt; + llElt_t *pEltPrev = NULL; + int bFound = 0; + + assert(pThis != NULL); + assert(pKey != NULL); + assert(ppElt != NULL); + assert(ppEltPrev != NULL); + + pElt = pThis->pRoot; + while(pElt != NULL && bFound == 0) { + if(pThis->cmpOp(pKey, pElt->pKey) == 0) + bFound = 1; + else { + pEltPrev = pElt; + pElt = pElt->pNext; + } + } + + if(bFound == 1) { + *ppElt = pElt; + *ppEltPrev = pEltPrev; + } else + iRet = RS_RET_NOT_FOUND; + + RETiRet; +} + + +/* find a user element based on the provided key + */ +rsRetVal llFind(linkedList_t *pThis, void *pKey, void **ppData) +{ + DEFiRet; + llElt_t *pElt; + llElt_t *pEltPrev; + + CHKiRet(llFindElt(pThis, pKey, &pElt, &pEltPrev)); + + /* if we reach this point, we have found the element */ + *ppData = pElt->pData; + +finalize_it: + RETiRet; +} + + +/* find a delete an element based on user-provided key. The element is + * delete, the caller does not receive anything. If we need to receive + * the element before destruction, we may implement an llFindAndUnlink() + * at that time. + * rgerhards, 2007-11-21 + */ +rsRetVal llFindAndDelete(linkedList_t *pThis, void *pKey) +{ + DEFiRet; + llElt_t *pElt; + llElt_t *pEltPrev; + + CHKiRet(llFindElt(pThis, pKey, &pElt, &pEltPrev)); + + /* if we reach this point, we have found an element */ + CHKiRet(llUnlinkAndDelteElt(pThis, pElt, pEltPrev)); + +finalize_it: + RETiRet; +} + + +/* provide the count of linked list elements + */ +rsRetVal llGetNumElts(linkedList_t *pThis, int *piCnt) +{ + DEFiRet; + + assert(pThis != NULL); + assert(piCnt != NULL); + + *piCnt = pThis->iNumElts; + + RETiRet; +} + + +/* execute a function on all list members. The functions receives a + * user-supplied parameter, which may be either a simple value + * or a pointer to a structure with more data. If the user-supplied + * function does not return RS_RET_OK, this function here terminates. + * rgerhards, 2007-08-02 + * rgerhards, 2007-11-21: added functionality to delete a list element. + * If the called user function returns RS_RET_OK_DELETE_LISTENTRY the current element + * is deleted. + */ +rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam) +{ + DEFiRet; + rsRetVal iRetLL; + void *pData; + linkedListCookie_t llCookie = NULL; + linkedListCookie_t llCookiePrev = NULL; /* previous list element (needed for deletion, NULL = at root) */ + + assert(pThis != NULL); + assert(pFunc != NULL); + + while((iRetLL = llGetNextElt(pThis, &llCookie, (void**)&pData)) == RS_RET_OK) { + iRet = pFunc(pData, pParam); + if(iRet == RS_RET_OK_DELETE_LISTENTRY) { + /* delete element */ + CHKiRet(llUnlinkAndDelteElt(pThis, llCookie, llCookiePrev)); + /* we need to revert back, as we have just deleted the current element. + * So the actual current element is the one before it, which happens to be + * stored in llCookiePrev. -- rgerhards, 2007-11-21 + */ + llCookie = llCookiePrev; + } else if (iRet != RS_RET_OK) { + goto finalize_it; + } + llCookiePrev = llCookie; + } + + if(iRetLL != RS_RET_END_OF_LINKEDLIST) + iRet = iRetLL; + +finalize_it: + RETiRet; +} + +/* vim:set ai: + */ diff --git a/runtime/linkedlist.h b/runtime/linkedlist.h new file mode 100644 index 00000000..aeacd6d7 --- /dev/null +++ b/runtime/linkedlist.h @@ -0,0 +1,73 @@ +/* Definition of the linkedlist object. + * + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef LINKEDLIST_H_INCLUDED +#define LINKEDLIST_H_INCLUDED + +/* this is a single entry for a parse routine. It describes exactly + * one entry point/handler. + * The short name is cslch (Configfile SysLine CommandHandler) + */ +struct llElt_s { /* config file sysline parse entry */ + struct llElt_s *pNext; + void *pKey; /* key for this element */ + void *pData; /* user-supplied data pointer */ +}; +typedef struct llElt_s llElt_t; + + +/* this is the list of known configuration commands with pointers to + * their handlers. + * The short name is cslc (Configfile SysLine Command) + */ +struct linkedList_s { /* config file sysline parse entry */ + int iNumElts; /* number of elements in list */ + rsRetVal (*pEltDestruct)(void*pData); /* destructor for user pointer in llElt_t's */ + rsRetVal (*pKeyDestruct)(void*pKey); /* destructor for key pointer in llElt_t's */ + int (*cmpOp)(void*, void*); /* pointer to key compare operation function, retval like strcmp */ + void *pKey; /* the list key (searchable, if set) */ + llElt_t *pRoot; /* list root */ + llElt_t *pLast; /* list tail */ +}; +typedef struct linkedList_s linkedList_t; + +typedef llElt_t* linkedListCookie_t; /* this type avoids exposing internals and keeps us flexible */ + +/* prototypes */ +rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pKeyDestructor)(void*), int (*pCmpOp)()); +rsRetVal llDestroy(linkedList_t *pThis); +rsRetVal llDestroyRootElt(linkedList_t *pThis); +rsRetVal llGetNextElt(linkedList_t *pThis, linkedListCookie_t *ppElt, void **ppUsr); +rsRetVal llAppend(linkedList_t *pThis, void *pKey, void *pData); +rsRetVal llFind(linkedList_t *pThis, void *pKey, void **ppData); +rsRetVal llGetKey(llElt_t *pThis, void *ppData); +rsRetVal llGetNumElts(linkedList_t *pThis, int *piCnt); +rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam); +rsRetVal llFindAndDelete(linkedList_t *pThis, void *pKey); +/* use the macro below to define a function that will be executed by + * llExecFunc() + */ +#define DEFFUNC_llExecFunc(funcName)\ + static rsRetVal funcName(void __attribute__((unused)) *pData, void __attribute__((unused)) *pParam) + +#endif /* #ifndef LINKEDLIST_H_INCLUDED */ diff --git a/runtime/module-template.h b/runtime/module-template.h new file mode 100644 index 00000000..5db73d33 --- /dev/null +++ b/runtime/module-template.h @@ -0,0 +1,482 @@ +/* module-template.h + * This header contains macros that can be used to implement the + * plumbing of modules. + * + * File begun on 2007-07-25 by RGerhards + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef MODULE_TEMPLATE_H_INCLUDED +#define MODULE_TEMPLATE_H_INCLUDED 1 + +#include "modules.h" +#include "obj.h" +#include "objomsr.h" +#include "threads.h" + +/* macro to define standard output-module static data members + */ +#define DEF_MOD_STATIC_DATA \ + static __attribute__((unused)) rsRetVal (*omsdRegCFSLineHdlr)(); + +#define DEF_OMOD_STATIC_DATA \ + DEF_MOD_STATIC_DATA \ + DEFobjCurrIf(obj) +#define DEF_IMOD_STATIC_DATA \ + DEF_MOD_STATIC_DATA \ + DEFobjCurrIf(obj) +#define DEF_LMOD_STATIC_DATA \ + DEF_MOD_STATIC_DATA + + +/* Macro to define the module type. Each module can only have a single type. If + * a module provides multiple types, several separate modules must be created which + * then should share a single library containing the majority of code. This macro + * must be present in each module. -- rgerhards, 2007-12-14 + */ +#define MODULE_TYPE(x)\ +static rsRetVal modGetType(eModType_t *modType) \ + { \ + *modType = x; \ + return RS_RET_OK;\ + } + +#define MODULE_TYPE_INPUT MODULE_TYPE(eMOD_IN) +#define MODULE_TYPE_OUTPUT MODULE_TYPE(eMOD_OUT) +#define MODULE_TYPE_LIB \ + DEF_LMOD_STATIC_DATA \ + MODULE_TYPE(eMOD_LIB) + +/* macro to define a unique module id. This must be able to fit in a void*. The + * module id must be unique inside a running rsyslogd application. It is used to + * track ownership of several objects. Most importantly, when the module is + * unloaded the module id value is used to find what needs to be destroyed. + * We currently use a pointer to modExit() as the module id. This sounds to be + * reasonable save, as each module must have this entry point AND there is no valid + * reason for twice this entry point being in memory. + * rgerhards, 2007-11-21 + */ +#define STD_LOADABLE_MODULE_ID ((void*) modExit) + + +/* macro to implement the "modGetID()" interface function + * rgerhards 2007-11-21 + */ +#define DEFmodGetID \ +static rsRetVal modGetID(void **pID) \ + { \ + *pID = STD_LOADABLE_MODULE_ID;\ + return RS_RET_OK;\ + } + +/* to following macros are used to generate function headers and standard + * functionality. It works as follows (described on the sample case of + * createInstance()): + * + * BEGINcreateInstance + * ... custom variable definitions (on stack) ... (if any) + * CODESTARTcreateInstance + * ... custom code ... (if any) + * ENDcreateInstance + */ + +/* createInstance() + */ +#define BEGINcreateInstance \ +static rsRetVal createInstance(instanceData **ppData)\ + {\ + DEFiRet; /* store error code here */\ + instanceData *pData; /* use this to point to data elements */ + +#define CODESTARTcreateInstance \ + if((pData = calloc(1, sizeof(instanceData))) == NULL) {\ + *ppData = NULL;\ + ENDfunc \ + return RS_RET_OUT_OF_MEMORY;\ + } + +#define ENDcreateInstance \ + *ppData = pData;\ + RETiRet;\ +} + +/* freeInstance() + * This is the cleanup function for the module instance. It is called immediately before + * the module instance is destroyed (unloaded). The module should do any cleanup + * here, e.g. close file, free instantance heap memory and the like. Control will + * not be passed back to the module once this function is finished. Keep in mind, + * however, that other instances may still be loaded and used. So do not destroy + * anything that may be used by another instance. If you have such a ressource, you + * currently need to do the instance counting yourself. + */ +#define BEGINfreeInstance \ +static rsRetVal freeInstance(void* pModData)\ +{\ + DEFiRet;\ + instanceData *pData; + +#define CODESTARTfreeInstance \ + pData = (instanceData*) pModData; + +#define ENDfreeInstance \ + if(pData != NULL)\ + free(pData); /* we need to free this in any case */\ + RETiRet;\ +} + +/* isCompatibleWithFeature() + */ +#define BEGINisCompatibleWithFeature \ +static rsRetVal isCompatibleWithFeature(syslogFeature __attribute__((unused)) eFeat)\ +{\ + rsRetVal iRet = RS_RET_INCOMPATIBLE; \ + BEGINfunc + +#define CODESTARTisCompatibleWithFeature + +#define ENDisCompatibleWithFeature \ + RETiRet;\ +} + +/* doAction() + */ +#define BEGINdoAction \ +static rsRetVal doAction(uchar __attribute__((unused)) **ppString, unsigned __attribute__((unused)) iMsgOpts, instanceData __attribute__((unused)) *pData)\ +{\ + DEFiRet; + +#define CODESTARTdoAction \ + /* ppString may be NULL if the output module requested no strings */ + +#define ENDdoAction \ + RETiRet;\ +} + + +/* dbgPrintInstInfo() + * Extra comments: + * Print debug information about this instance. + */ +#define BEGINdbgPrintInstInfo \ +static rsRetVal dbgPrintInstInfo(void *pModData)\ +{\ + DEFiRet;\ + instanceData *pData = NULL; + +#define CODESTARTdbgPrintInstInfo \ + pData = (instanceData*) pModData; + +#define ENDdbgPrintInstInfo \ + RETiRet;\ +} + + +/* parseSelectorAct() + * Extra comments: + * try to process a selector action line. Checks if the action + * applies to this module and, if so, processed it. If not, it + * is left untouched. The driver will then call another module. + * On exit, ppModData must point to instance data. Also, a string + * request object must be created and filled. A macro is defined + * for that. + * For the most usual case, we have defined a macro below. + * If more than one string is requested, the macro can be used together + * with own code that overwrites the entry count. In this case, the + * macro must come before the own code. It is recommended to be + * placed right after CODESTARTparseSelectorAct. + */ +#define BEGINparseSelectorAct \ +static rsRetVal parseSelectorAct(uchar **pp, void **ppModData, omodStringRequest_t **ppOMSR)\ +{\ + DEFiRet;\ + uchar *p;\ + instanceData *pData = NULL; + +#define CODESTARTparseSelectorAct \ + assert(pp != NULL);\ + assert(ppModData != NULL);\ + assert(ppOMSR != NULL);\ + p = *pp; + +#define CODE_STD_STRING_REQUESTparseSelectorAct(NumStrReqEntries) \ + CHKiRet(OMSRconstruct(ppOMSR, NumStrReqEntries)); + +#define CODE_STD_FINALIZERparseSelectorAct \ +finalize_it:\ + if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) {\ + *ppModData = pData;\ + *pp = p;\ + } else {\ + /* cleanup, we failed */\ + if(*ppOMSR != NULL) {\ + OMSRdestruct(*ppOMSR);\ + *ppOMSR = NULL;\ + }\ + if(pData != NULL) {\ + freeInstance(pData);\ + } \ + } + +#define ENDparseSelectorAct \ + RETiRet;\ +} + + +/* tryResume() + * This entry point is called to check if a module can resume operations. This + * happens when a module requested that it be suspended. In suspended state, + * the engine periodically tries to resume the module. If that succeeds, normal + * processing continues. If not, the module will not be called unless a + * tryResume() call succeeds. + * Returns RS_RET_OK, if resumption succeeded, RS_RET_SUSPENDED otherwise + * rgerhard, 2007-08-02 + */ +#define BEGINtryResume \ +static rsRetVal tryResume(instanceData __attribute__((unused)) *pData)\ +{\ + DEFiRet; + +#define CODESTARTtryResume \ + assert(pData != NULL); + +#define ENDtryResume \ + RETiRet;\ +} + + + +/* queryEtryPt() + */ +#define BEGINqueryEtryPt \ +DEFmodGetID \ +static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\ +{\ + DEFiRet; + +#define CODESTARTqueryEtryPt \ + if((name == NULL) || (pEtryPoint == NULL)) {\ + ENDfunc \ + return RS_RET_PARAM_ERROR;\ + } \ + *pEtryPoint = NULL; + +#define ENDqueryEtryPt \ + if(iRet == RS_RET_OK)\ + if(*pEtryPoint == NULL) { \ + dbgprintf("entry point '%s' not present in module\n", name); \ + iRet = RS_RET_MODULE_ENTRY_POINT_NOT_FOUND;\ + } \ + RETiRet;\ +} + +/* the following definition is the standard block for queryEtryPt for all types + * of modules. It should be included in any module, and typically is so by calling + * the module-type specific macros. + */ +#define CODEqueryEtryPt_STD_MOD_QUERIES \ + if(!strcmp((char*) name, "modExit")) {\ + *pEtryPoint = modExit;\ + } else if(!strcmp((char*) name, "modGetID")) {\ + *pEtryPoint = modGetID;\ + } else if(!strcmp((char*) name, "getType")) {\ + *pEtryPoint = modGetType;\ + } + +/* the following definition is the standard block for queryEtryPt for output + * modules. This can be used if no specific handling (e.g. to cover version + * differences) is needed. + */ +#define CODEqueryEtryPt_STD_OMOD_QUERIES \ + CODEqueryEtryPt_STD_MOD_QUERIES \ + else if(!strcmp((char*) name, "doAction")) {\ + *pEtryPoint = doAction;\ + } else if(!strcmp((char*) name, "dbgPrintInstInfo")) {\ + *pEtryPoint = dbgPrintInstInfo;\ + } else if(!strcmp((char*) name, "freeInstance")) {\ + *pEtryPoint = freeInstance;\ + } else if(!strcmp((char*) name, "parseSelectorAct")) {\ + *pEtryPoint = parseSelectorAct;\ + } else if(!strcmp((char*) name, "isCompatibleWithFeature")) {\ + *pEtryPoint = isCompatibleWithFeature;\ + } else if(!strcmp((char*) name, "tryResume")) {\ + *pEtryPoint = tryResume;\ + } + +/* the following definition is the standard block for queryEtryPt for INPUT + * modules. This can be used if no specific handling (e.g. to cover version + * differences) is needed. + */ +#define CODEqueryEtryPt_STD_IMOD_QUERIES \ + CODEqueryEtryPt_STD_MOD_QUERIES \ + else if(!strcmp((char*) name, "runInput")) {\ + *pEtryPoint = runInput;\ + } else if(!strcmp((char*) name, "willRun")) {\ + *pEtryPoint = willRun;\ + } else if(!strcmp((char*) name, "afterRun")) {\ + *pEtryPoint = afterRun;\ + } + +/* the following definition is the standard block for queryEtryPt for LIBRARY + * modules. This can be used if no specific handling (e.g. to cover version + * differences) is needed. + */ +#define CODEqueryEtryPt_STD_LIB_QUERIES \ + CODEqueryEtryPt_STD_MOD_QUERIES + +/* modInit() + * This has an extra parameter, which is the specific name of the modInit + * function. That is needed for built-in modules, which must have unique + * names in order to link statically. Please note that this is alwaysy only + * the case with modInit() and NO other entry point. The reason is that only + * modInit() is visible form a linker/loader point of view. All other entry + * points are passed via rsyslog-internal query functions and are defined + * static inside the modules source. This is an important concept, as it allows + * us to support different interface versions within a single module. (Granted, + * we do not currently have different interface versions, so we can not put + * it to a test - but our firm believe is that we can do all abstraction needed...) + * + * Extra Comments: + * initialize the module + * + * Later, much more must be done. So far, we only return a pointer + * to the queryEtryPt() function + * TODO: do interface version checking & handshaking + * iIfVersRequetsed is the version of the interface specification that the + * caller would like to see being used. ipIFVersProvided is what we + * decide to provide. + * rgerhards, 2007-11-21: see modExit() comment below for important information + * on the need to initialize static data with code. modInit() may be called on a + * cached, left-in-memory copy of a previous incarnation. + */ +#define BEGINmodInit(uniqName) \ +rsRetVal modInit##uniqName(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t __attribute__((unused)) *pModInfo)\ +{\ + DEFiRet; \ + rsRetVal (*pObjGetObjInterface)(obj_if_t *pIf); + +#define CODESTARTmodInit \ + assert(pHostQueryEtryPt != NULL);\ + iRet = pHostQueryEtryPt((uchar*)"objGetObjInterface", &pObjGetObjInterface); \ + if((iRet != RS_RET_OK) || (pQueryEtryPt == NULL) || (ipIFVersProvided == NULL) || (pObjGetObjInterface == NULL)) { \ + ENDfunc \ + return (iRet == RS_RET_OK) ? RS_RET_PARAM_ERROR : iRet; \ + } \ + /* now get the obj interface so that we can access other objects */ \ + CHKiRet(pObjGetObjInterface(&obj)); + +#define ENDmodInit \ +finalize_it:\ + *pQueryEtryPt = queryEtryPt;\ + RETiRet;\ +} + + +/* definitions for host API queries */ +#define CODEmodInit_QueryRegCFSLineHdlr \ + CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr)); + +#endif /* #ifndef MODULE_TEMPLATE_H_INCLUDED */ + +/* modExit() + * This is the counterpart to modInit(). It destroys a module and makes it ready for + * unloading. It is similiar to freeInstance() for the instance data. Please note that + * this entry point needs to free any module-globale data structures and registrations. + * For example, the CfSysLineHandlers a module has registered need to be unregistered + * here. This entry point is only called immediately before unloading of the module. So + * it is likely to be destroyed. HOWEVER, the caller may decide to keep the module cached. + * So a module must never assume that it is actually destroyed. A call to modInit() may + * happen immediately after modExit(). So a module can NOT assume that static data elements + * are being re-initialized by the loader - this must always be done by module code itself. + * It is suggested to do this in modInit(). - rgerhards, 2007-11-21 + */ +#define BEGINmodExit \ +static rsRetVal modExit(void)\ +{\ + DEFiRet; + +#define CODESTARTmodExit + +#define ENDmodExit \ + RETiRet;\ +} + + +/* runInput() + * This is the main function for input modules. It is used to gather data from the + * input source and submit it to the message queue. Each runInput() instance has its own + * thread. This is handled by the rsyslog engine. It needs to spawn off new threads only + * if there is a module-internal need to do so. + */ +#define BEGINrunInput \ +static rsRetVal runInput(thrdInfo_t __attribute__((unused)) *pThrd)\ +{\ + DEFiRet; + +#define CODESTARTrunInput \ + dbgSetThrdName((uchar*)__FILE__); /* we need to provide something better later */ + +#define ENDrunInput \ + RETiRet;\ +} + + +/* willRun() + * This is a function that will be replaced in the longer term. It is used so + * that a module can tell the caller if it will run or not. This is to be replaced + * when we introduce input module instances. However, these require config syntax + * changes and I may (or may not... ;)) hold that until another config file + * format is available. -- rgerhards, 2007-12-17 + * returns RS_RET_NO_RUN if it will not run (RS_RET_OK or error otherwise) + */ +#define BEGINwillRun \ +static rsRetVal willRun(void)\ +{\ + DEFiRet; + +#define CODESTARTwillRun + +#define ENDwillRun \ + RETiRet;\ +} + + +/* afterRun() + * This function is called after an input module has been run and its thread has + * been terminated. It shall do any necessary cleanup. + * This is expected to evolve into a freeInstance type of call once the input module + * interface evolves to support multiple instances. + * rgerhards, 2007-12-17 + */ +#define BEGINafterRun \ +static rsRetVal afterRun(void)\ +{\ + DEFiRet; + +#define CODESTARTafterRun + +#define ENDafterRun \ + RETiRet;\ +} + + +/* + * vi:set ai: + */ diff --git a/runtime/modules.c b/runtime/modules.c new file mode 100644 index 00000000..f10390c7 --- /dev/null +++ b/runtime/modules.c @@ -0,0 +1,803 @@ +/* modules.c + * This is the implementation of syslogd modules object. + * This object handles plug-ins and build-in modules of all kind. + * + * Modules are reference-counted. Anyone who access a module must call + * Use() before any function is accessed and Release() when he is done. + * When the reference count reaches 0, rsyslog unloads the module (that + * may be changed in the future to cache modules). Rsyslog does NOT + * unload modules with a reference count > 0, even if the unload + * method is called! + * + * File begun on 2007-07-22 by RGerhards + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" +#include "rsyslog.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef OS_BSD +# include "libgen.h" +#endif + +#include /* TODO: replace this with the libtools equivalent! */ + +#include +#include + +#include "syslogd.h" +#include "cfsysline.h" +#include "modules.h" +#include "errmsg.h" + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) + +static modInfo_t *pLoadedModules = NULL; /* list of currently-loaded modules */ +static modInfo_t *pLoadedModulesLast = NULL; /* tail-pointer */ + +/* config settings */ +uchar *pModDir = NULL; /* read-only after startup */ + + +#ifdef DEBUG +/* we add some home-grown support to track our users (and detect who does not free us). In + * the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11 + */ + +/* add a user to the current list of users (always at the root) */ +static void +modUsrAdd(modInfo_t *pThis, char *pszUsr) +{ + modUsr_t *pUsr; + + BEGINfunc + if((pUsr = calloc(1, sizeof(modUsr_t))) == NULL) + goto finalize_it; + + if((pUsr->pszFile = strdup(pszUsr)) == NULL) { + free(pUsr); + goto finalize_it; + } + + if(pThis->pModUsrRoot != NULL) { + pUsr->pNext = pThis->pModUsrRoot; + } + pThis->pModUsrRoot = pUsr; + +finalize_it: + ENDfunc; +} + + +/* remove a user from the current user list + * rgerhards, 2008-03-11 + */ +static void +modUsrDel(modInfo_t *pThis, char *pszUsr) +{ + modUsr_t *pUsr; + modUsr_t *pPrev = NULL; + + for(pUsr = pThis->pModUsrRoot ; pUsr != NULL ; pUsr = pUsr->pNext) { + if(!strcmp(pUsr->pszFile, pszUsr)) + break; + else + pPrev = pUsr; + } + + if(pUsr == NULL) { + dbgprintf("oops - tried to delete user %s from module %s and it wasn't registered as one...\n", + pszUsr, pThis->pszName); + } else { + if(pPrev == NULL) { + /* This was at the root! */ + pThis->pModUsrRoot = pUsr->pNext; + } else { + pPrev->pNext = pUsr->pNext; + } + /* free ressources */ + free(pUsr->pszFile); + free(pUsr); + pUsr = NULL; /* just to make sure... */ + } +} + + +/* print a short list all all source files using the module in question + * rgerhards, 2008-03-11 + */ +static void +modUsrPrint(modInfo_t *pThis) +{ + modUsr_t *pUsr; + + for(pUsr = pThis->pModUsrRoot ; pUsr != NULL ; pUsr = pUsr->pNext) { + dbgprintf("\tmodule %s is currently in use by file %s\n", + pThis->pszName, pUsr->pszFile); + } +} + + +/* print all loaded modules and who is accessing them. This is primarily intended + * to be called at end of run to detect "module leaks" and who is causing them. + * rgerhards, 2008-03-11 + */ +//static void +void +modUsrPrintAll(void) +{ + modInfo_t *pMod; + + BEGINfunc + for(pMod = pLoadedModules ; pMod != NULL ; pMod = pMod->pNext) { + dbgprintf("printing users of loadable module %s, refcount %u, ptr %p, type %d\n", pMod->pszName, pMod->uRefCnt, pMod, pMod->eType); + modUsrPrint(pMod); + } + ENDfunc +} + +#endif /* #ifdef DEBUG */ + + +/* Construct a new module object + */ +static rsRetVal moduleConstruct(modInfo_t **pThis) +{ + modInfo_t *pNew; + + if((pNew = calloc(1, sizeof(modInfo_t))) == NULL) + return RS_RET_OUT_OF_MEMORY; + + /* OK, we got the element, now initialize members that should + * not be zero-filled. + */ + + *pThis = pNew; + return RS_RET_OK; +} + + +/* Destructs a module object. The object must not be linked to the + * linked list of modules. Please note that all other dependencies on this + * modules must have been removed before (e.g. CfSysLineHandlers!) + */ +static void moduleDestruct(modInfo_t *pThis) +{ + assert(pThis != NULL); + if(pThis->pszName != NULL) + free(pThis->pszName); + if(pThis->pModHdlr != NULL) { +# ifdef VALGRIND +# warning "dlclose disabled for valgrind" +# else + dlclose(pThis->pModHdlr); +# endif + } + + free(pThis); +} + + +/* The following function is the queryEntryPoint for host-based entry points. + * Modules may call it to get access to core interface functions. Please note + * that utility functions can be accessed via shared libraries - at least this + * is my current shool of thinking. + * Please note that the implementation as a query interface allows to take + * care of plug-in interface version differences. -- rgerhards, 2007-07-31 + */ +static rsRetVal queryHostEtryPt(uchar *name, rsRetVal (**pEtryPoint)()) +{ + DEFiRet; + + if((name == NULL) || (pEtryPoint == NULL)) + return RS_RET_PARAM_ERROR; + + if(!strcmp((char*) name, "regCfSysLineHdlr")) { + *pEtryPoint = regCfSysLineHdlr; + } else if(!strcmp((char*) name, "objGetObjInterface")) { + *pEtryPoint = objGetObjInterface; + } else { + *pEtryPoint = NULL; /* to be on the safe side */ + ABORT_FINALIZE(RS_RET_ENTRY_POINT_NOT_FOUND); + } + +finalize_it: + RETiRet; +} + + +/* get the name of a module + */ +static uchar *modGetName(modInfo_t *pThis) +{ + return((pThis->pszName == NULL) ? (uchar*) "" : pThis->pszName); +} + + +/* get the state-name of a module. The state name is its name + * together with a short description of the module state (which + * is pulled from the module itself. + * rgerhards, 2007-07-24 + * TODO: the actual state name is not yet pulled + */ +static uchar *modGetStateName(modInfo_t *pThis) +{ + return(modGetName(pThis)); +} + + +/* Add a module to the loaded module linked list + */ +static inline void +addModToList(modInfo_t *pThis) +{ + assert(pThis != NULL); + + if(pLoadedModules == NULL) { + pLoadedModules = pLoadedModulesLast = pThis; + } else { + /* there already exist entries */ + pThis->pPrev = pLoadedModulesLast; + pLoadedModulesLast->pNext = pThis; + pLoadedModulesLast = pThis; + } +} + + +/* Get the next module pointer - this is used to traverse the list. + * The function returns the next pointer or NULL, if there is no next one. + * The last object must be provided to the function. If NULL is provided, + * it starts at the root of the list. Even in this case, NULL may be + * returned - then, the list is empty. + * rgerhards, 2007-07-23 + */ +static modInfo_t *GetNxt(modInfo_t *pThis) +{ + modInfo_t *pNew; + + if(pThis == NULL) + pNew = pLoadedModules; + else + pNew = pThis->pNext; + + return(pNew); +} + + +/* this function is like GetNxt(), but it returns pointers to + * modules of specific type only. As we currently deal just with output modules, + * it is a dummy, to be filled with real code later. + * rgerhards, 2007-07-24 + */ +static modInfo_t *GetNxtType(modInfo_t *pThis, eModType_t rqtdType) +{ + modInfo_t *pMod = pThis; + + do { + pMod = GetNxt(pMod); + } while(!(pMod == NULL || pMod->eType == rqtdType)); /* warning: do ... while() */ + + return pMod; +} + + +/* Prepare a module for unloading. + * This is currently a dummy, to be filled when we have a plug-in + * interface - rgerhards, 2007-08-09 + * rgerhards, 2007-11-21: + * When this function is called, all instance-data must already have + * been destroyed. In the case of output modules, this happens when the + * rule set is being destroyed. When we implement other module types, we + * need to think how we handle it there (and if we have any instance data). + * rgerhards, 2008-03-10: reject unload request if the module has a reference + * count > 0. + */ +static rsRetVal +modPrepareUnload(modInfo_t *pThis) +{ + DEFiRet; + void *pModCookie; + + assert(pThis != NULL); + + if(pThis->uRefCnt > 0) { + dbgprintf("rejecting unload of module '%s' because it has a refcount of %d\n", + pThis->pszName, pThis->uRefCnt); + ABORT_FINALIZE(RS_RET_MODULE_STILL_REFERENCED); + } + + CHKiRet(pThis->modGetID(&pModCookie)); + pThis->modExit(); /* tell the module to get ready for unload */ + CHKiRet(unregCfSysLineHdlrs4Owner(pModCookie)); + +finalize_it: + RETiRet; +} + + +/* Add an already-loaded module to the module linked list. This function does + * everything needed to fully initialize the module. + */ +static rsRetVal +doModInit(rsRetVal (*modInit)(int, int*, rsRetVal(**)(), rsRetVal(*)(), modInfo_t*), uchar *name, void *pModHdlr) +{ + DEFiRet; + modInfo_t *pNew = NULL; + rsRetVal (*modGetType)(eModType_t *pType); + + assert(modInit != NULL); + + if((iRet = moduleConstruct(&pNew)) != RS_RET_OK) { + pNew = NULL; + ABORT_FINALIZE(iRet); + } + + CHKiRet((*modInit)(CURR_MOD_IF_VERSION, &pNew->iIFVers, &pNew->modQueryEtryPt, queryHostEtryPt, pNew)); + + if(pNew->iIFVers != CURR_MOD_IF_VERSION) { + ABORT_FINALIZE(RS_RET_MISSING_INTERFACE); + } + + /* We now poll the module to see what type it is. We do this only once as this + * can never change in the lifetime of an module. -- rgerhards, 2007-12-14 + */ + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"getType", &modGetType)); + CHKiRet((iRet = (*modGetType)(&pNew->eType)) != RS_RET_OK); + dbgprintf("module of type %d being loaded.\n", pNew->eType); + + /* OK, we know we can successfully work with the module. So we now fill the + * rest of the data elements. First we load the interfaces common to all + * module types. + */ + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modGetID", &pNew->modGetID)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"modExit", &pNew->modExit)); + + /* ... and now the module-specific interfaces */ + switch(pNew->eType) { + case eMOD_IN: + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"runInput", &pNew->mod.im.runInput)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"willRun", &pNew->mod.im.willRun)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"afterRun", &pNew->mod.im.afterRun)); + break; + case eMOD_OUT: + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"freeInstance", &pNew->freeInstance)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"dbgPrintInstInfo", &pNew->dbgPrintInstInfo)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"doAction", &pNew->mod.om.doAction)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"parseSelectorAct", &pNew->mod.om.parseSelectorAct)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"isCompatibleWithFeature", &pNew->isCompatibleWithFeature)); + CHKiRet((*pNew->modQueryEtryPt)((uchar*)"tryResume", &pNew->tryResume)); + break; + case eMOD_LIB: + break; + } + + pNew->pszName = (uchar*) strdup((char*)name); /* we do not care if strdup() fails, we can accept that */ + pNew->pModHdlr = pModHdlr; + /* TODO: take this from module */ + if(pModHdlr == NULL) + pNew->eLinkType = eMOD_LINK_STATIC; + else + pNew->eLinkType = eMOD_LINK_DYNAMIC_LOADED; + + /* we initialized the structure, now let's add it to the linked list of modules */ + addModToList(pNew); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pNew != NULL) + moduleDestruct(pNew); + } + + RETiRet; +} + +/* Print loaded modules. This is more or less a + * debug or test aid, but anyhow I think it's worth it... + * This only works if the dbgprintf() subsystem is initialized. + * TODO: update for new input modules! + */ +static void modPrintList(void) +{ + modInfo_t *pMod; + + pMod = GetNxt(NULL); + while(pMod != NULL) { + dbgprintf("Loaded Module: Name='%s', IFVersion=%d, ", + (char*) modGetName(pMod), pMod->iIFVers); + dbgprintf("type="); + switch(pMod->eType) { + case eMOD_OUT: + dbgprintf("output"); + break; + case eMOD_IN: + dbgprintf("input"); + break; + case eMOD_LIB: + dbgprintf("library"); + break; + } + dbgprintf(" module.\n"); + dbgprintf("Entry points:\n"); + dbgprintf("\tqueryEtryPt: 0x%lx\n", (unsigned long) pMod->modQueryEtryPt); + dbgprintf("\tdoAction: 0x%lx\n", (unsigned long) pMod->mod.om.doAction); + dbgprintf("\tparseSelectorAct: 0x%lx\n", (unsigned long) pMod->mod.om.parseSelectorAct); + dbgprintf("\tdbgPrintInstInfo: 0x%lx\n", (unsigned long) pMod->dbgPrintInstInfo); + dbgprintf("\tfreeInstance: 0x%lx\n", (unsigned long) pMod->freeInstance); + dbgprintf("\n"); + pMod = GetNxt(pMod); /* done, go next */ + } +} + + +/* unlink and destroy a module. The caller must provide a pointer to the module + * itself as well as one to its immediate predecessor. + * rgerhards, 2008-02-26 + */ +static rsRetVal +modUnlinkAndDestroy(modInfo_t **ppThis) +{ + DEFiRet; + modInfo_t *pThis; + + assert(ppThis != NULL); + pThis = *ppThis; + assert(pThis != NULL); + + /* first check if we are permitted to unload */ + if(pThis->eType == eMOD_LIB) { + if(pThis->uRefCnt > 0) { + dbgprintf("module %s NOT unloaded because it still has a refcount of %u\n", + pThis->pszName, pThis->uRefCnt); +# ifdef DEBUG + //modUsrPrintAll(); +# endif + ABORT_FINALIZE(RS_RET_MODULE_STILL_REFERENCED); + } + } + + /* we need to unlink the module before we can destruct it -- rgerhards, 2008-02-26 */ + if(pThis->pPrev == NULL) { + /* module is root, so we need to set a new root */ + pLoadedModules = pThis->pNext; + } else { + pThis->pPrev->pNext = pThis->pNext; + } + + if(pThis->pNext == NULL) { + pLoadedModulesLast = pThis->pPrev; + } else { + pThis->pNext->pPrev = pThis->pPrev; + } + + /* finally, we are ready for the module to go away... */ + dbgprintf("Unloading module %s\n", modGetName(pThis)); + CHKiRet(modPrepareUnload(pThis)); + *ppThis = pThis->pNext; + + moduleDestruct(pThis); + +finalize_it: + RETiRet; +} + + +/* unload all loaded modules of a specific type (use eMOD_ALL if you want to + * unload all module types). The unload happens only if the module is no longer + * referenced. So some modules may survive this call. + * rgerhards, 2008-03-11 + */ +static rsRetVal +modUnloadAndDestructAll(eModLinkType_t modLinkTypesToUnload) +{ + DEFiRet; + modInfo_t *pModCurr; /* module currently being processed */ + + pModCurr = GetNxt(NULL); + while(pModCurr != NULL) { + if(modLinkTypesToUnload == eMOD_LINK_ALL || pModCurr->eLinkType == modLinkTypesToUnload) { + if(modUnlinkAndDestroy(&pModCurr) == RS_RET_MODULE_STILL_REFERENCED) { + pModCurr = GetNxt(pModCurr); + } + /* Note: if the module was successfully unloaded, it has updated the + * pModCurr pointer to the next module. So we do NOT need to advance + * to the next module on successful unload. + */ + } else { + pModCurr = GetNxt(pModCurr); + } + } + +# ifdef DEBUG + if(pLoadedModules != NULL) { + dbgprintf("modules still loaded after module.UnloadAndDestructAll:\n"); + modUsrPrintAll(); + } +# endif + + RETiRet; +} + + +/* load a module and initialize it, based on doModLoad() from conf.c + * rgerhards, 2008-03-05 + * varmojfekoj added support for dynamically loadable modules on 2007-08-13 + * rgerhards, 2007-09-25: please note that the non-threadsafe function dlerror() is + * called below. This is ok because modules are currently only loaded during + * configuration file processing, which is executed on a single thread. Should we + * change that design at any stage (what is unlikely), we need to find a + * replacement. + */ +static rsRetVal +Load(uchar *pModName) +{ + DEFiRet; + + size_t iPathLen, iModNameLen; + uchar szPath[PATH_MAX]; + uchar *pModNameCmp; + int bHasExtension; + void *pModHdlr, *pModInit; + modInfo_t *pModInfo; + + assert(pModName != NULL); + dbgprintf("Requested to load module '%s'\n", pModName); + + iModNameLen = strlen((char *) pModName); + if(iModNameLen > 3 && !strcmp((char *) pModName + iModNameLen - 3, ".so")) { + iModNameLen -= 3; + bHasExtension = TRUE; + } else + bHasExtension = FALSE; + + pModInfo = GetNxt(NULL); + while(pModInfo != NULL) { + if(!strncmp((char *) pModName, (char *) (pModNameCmp = modGetName(pModInfo)), iModNameLen) && + (!*(pModNameCmp + iModNameLen) || !strcmp((char *) pModNameCmp + iModNameLen, ".so"))) { + dbgprintf("Module '%s' already loaded\n", pModName); + ABORT_FINALIZE(RS_RET_OK); + } + pModInfo = GetNxt(pModInfo); + } + + /* now build our load module name */ + if(*pModName == '/') { + *szPath = '\0'; /* we do not need to append the path - its already in the module name */ + iPathLen = 0; + } else { + *szPath = '\0'; + strncat((char *) szPath, (pModDir == NULL) ? _PATH_MODDIR : (char*) pModDir, sizeof(szPath) - 1); + iPathLen = strlen((char*) szPath); + if((szPath[iPathLen - 1] != '/')) { + if((iPathLen <= sizeof(szPath) - 2)) { + szPath[iPathLen++] = '/'; + szPath[iPathLen] = '\0'; + } else { + errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); + } + } + } + + /* ... add actual name ... */ + strncat((char *) szPath, (char *) pModName, sizeof(szPath) - iPathLen - 1); + + /* now see if we have an extension and, if not, append ".so" */ + if(!bHasExtension) { + /* we do not have an extension and so need to add ".so" + * TODO: I guess this is highly importable, so we should change the + * algo over time... -- rgerhards, 2008-03-05 + */ + /* ... so now add the extension */ + strncat((char *) szPath, ".so", sizeof(szPath) - strlen((char*) szPath) - 1); + iPathLen += 3; + } + + if(iPathLen + strlen((char*) pModName) >= sizeof(szPath)) { + errmsg.LogError(NO_ERRCODE, "could not load module '%s', path too long\n", pModName); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_PATHLEN); + } + + /* complete load path constructed, so ... GO! */ + dbgprintf("loading module '%s'\n", szPath); + if(!(pModHdlr = dlopen((char *) szPath, RTLD_NOW))) { + errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlopen: %s\n", szPath, dlerror()); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_DLOPEN); + } + if(!(pModInit = dlsym(pModHdlr, "modInit"))) { + errmsg.LogError(NO_ERRCODE, "could not load module '%s', dlsym: %s\n", szPath, dlerror()); + dlclose(pModHdlr); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_NO_INIT); + } + if((iRet = doModInit(pModInit, (uchar*) pModName, pModHdlr)) != RS_RET_OK) { + errmsg.LogError(NO_ERRCODE, "could not load module '%s', rsyslog error %d\n", szPath, iRet); + dlclose(pModHdlr); + ABORT_FINALIZE(RS_RET_MODULE_LOAD_ERR_INIT_FAILED); + } + +finalize_it: + RETiRet; +} + + +/* set the default module load directory. A NULL value may be provided, in + * which case any previous value is deleted but no new one set. The caller-provided + * string is duplicated. If it needs to be freed, that's the caller's duty. + * rgerhards, 2008-03-07 + */ +static rsRetVal +SetModDir(uchar *pszModDir) +{ + DEFiRet; + + dbgprintf("setting default module load directory '%s'\n", pszModDir); + if(pModDir != NULL) { + free(pModDir); + } + + pModDir = (uchar*) strdup((char*)pszModDir); + + RETiRet; +} + + +/* Reference-Counting object access: add 1 to the current reference count. Must be + * called by anyone interested in using a module. -- rgerhards, 20080-03-10 + */ +static rsRetVal +Use(char *srcFile, modInfo_t *pThis) +{ + DEFiRet; + + assert(pThis != NULL); + pThis->uRefCnt++; + dbgprintf("source file %s requested reference for module '%s', reference count now %u\n", + srcFile, pThis->pszName, pThis->uRefCnt); + +# ifdef DEBUG + modUsrAdd(pThis, srcFile); +# endif + + RETiRet; + +} + + +/* Reference-Counting object access: subract one from the current refcount. Must + * by called by anyone who no longer needs a module. If count reaches 0, the + * module is unloaded. -- rgerhards, 20080-03-10 + */ +static rsRetVal +Release(char *srcFile, modInfo_t **ppThis) +{ + DEFiRet; + modInfo_t *pThis; + + assert(ppThis != NULL); + pThis = *ppThis; + assert(pThis != NULL); + if(pThis->uRefCnt == 0) { + /* oops, we are already at 0? */ + dbgprintf("internal error: module '%s' already has a refcount of 0 (released by %s)!\n", + pThis->pszName, srcFile); + } else { + --pThis->uRefCnt; + dbgprintf("file %s released module '%s', reference count now %u\n", + srcFile, pThis->pszName, pThis->uRefCnt); +# ifdef DEBUG + modUsrDel(pThis, srcFile); + modUsrPrint(pThis); +# endif + } + + if(pThis->uRefCnt == 0) { + /* we have a zero refcount, so we must unload the module */ + dbgprintf("module '%s' has zero reference count, unloading...\n", pThis->pszName); + modUnlinkAndDestroy(&pThis); + /* we must NOT do a *ppThis = NULL, because ppThis now points into freed memory! + * If in doubt, see obj.c::ReleaseObj() for how we are called. + */ + } + + RETiRet; + +} + + +/* exit our class + * rgerhards, 2008-03-11 + */ +BEGINObjClassExit(module, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(module) + /* release objects we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + +# ifdef DEBUG + modUsrPrintAll(); /* debug aid - TODO: integrate with debug.c, at least the settings! */ +# endif +ENDObjClassExit(module) + + +/* queryInterface function + * rgerhards, 2008-03-05 + */ +BEGINobjQueryInterface(module) +CODESTARTobjQueryInterface(module) + if(pIf->ifVersion != moduleCURR_IF_VERSION) { /* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->GetNxt = GetNxt; + pIf->GetNxtType = GetNxtType; + pIf->GetName = modGetName; + pIf->GetStateName = modGetStateName; + pIf->PrintList = modPrintList; + pIf->UnloadAndDestructAll = modUnloadAndDestructAll; + pIf->doModInit = doModInit; + pIf->SetModDir = SetModDir; + pIf->Load = Load; + pIf->Use = Use; + pIf->Release = Release; +finalize_it: +ENDobjQueryInterface(module) + + +/* Initialize our class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-03-05 + */ +BEGINAbstractObjClassInit(module, 1, OBJ_IS_CORE_MODULE) /* class, version - CHANGE class also in END MACRO! */ + uchar *pModPath; + + /* use any module load path specified in the environment */ + if((pModPath = (uchar*) getenv("RSYSLOG_MODDIR")) != NULL) { + SetModDir(pModPath); + } + + /* now check if another module path was set via the command line (-M) + * if so, that overrides the environment. Please note that we must use + * a global setting here because the command line parser can NOT call + * into the module object, because it is not initialized at that point. So + * instead a global setting is changed and we pick it up as soon as we + * initialize -- rgerhards, 2008-04-04 + */ + if(glblModPath != NULL) { + SetModDir(glblModPath); + } + + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); +ENDObjClassInit(module) + +/* vi:set ai: + */ diff --git a/runtime/modules.h b/runtime/modules.h new file mode 100644 index 00000000..7d34bcf7 --- /dev/null +++ b/runtime/modules.h @@ -0,0 +1,150 @@ +/* modules.h + * + * Definition for build-in and plug-ins module handler. This file is the base + * for all dynamically loadable module support. In theory, in v3 all modules + * are dynamically loaded, in practice we currently do have a few build-in + * once. This may become removed. + * + * The loader keeps track of what is loaded. For library modules, it is also + * used to find objects (libraries) and to obtain the queryInterface function + * for them. A reference count is maintened for libraries, so that they are + * unloaded only when nobody still accesses them. + * + * File begun on 2007-07-22 by RGerhards + * + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef MODULES_H_INCLUDED +#define MODULES_H_INCLUDED 1 + +#include "objomsr.h" + + +/* the following define defines the current version of the module interface. + * It can be used by any module which want's to simply prevent version conflicts + * and does not intend to do specific old-version emulations. + * rgerhards, 2008-03-04 + * version 3 adds modInfo_t ptr to call of modInit -- rgerhards, 2008-03-10 + * version 4 removes needUDPSocket OM callback -- rgerhards, 2008-03-22 + */ +#define CURR_MOD_IF_VERSION 4 + +typedef enum eModType_ { + eMOD_IN, /* input module */ + eMOD_OUT, /* output module */ + eMOD_LIB /* library module - this module provides one or many interfaces */ +} eModType_t; + + +#ifdef DEBUG +typedef struct modUsr_s { + struct modUsr_s *pNext; + char *pszFile; +} modUsr_t; +#endif + + +/* how is this module linked? */ +typedef enum eModLinkType_ { + eMOD_LINK_STATIC, + eMOD_LINK_DYNAMIC_UNLOADED, /* dynalink module, currently not loaded */ + eMOD_LINK_DYNAMIC_LOADED, /* dynalink module, currently loaded */ + eMOD_LINK_ALL /* special: all linkage types, e.g. for unload */ +} eModLinkType_t; + +typedef struct modInfo_s { + struct modInfo_s *pPrev; /* support for creating a double linked module list */ + struct modInfo_s *pNext; /* support for creating a linked module list */ + int iIFVers; /* Interface version of module */ + eModType_t eType; /* type of this module */ + eModLinkType_t eLinkType; + uchar* pszName; /* printable module name, e.g. for dbgprintf */ + unsigned uRefCnt; /* reference count for this module; 0 -> may be unloaded */ + /* functions supported by all types of modules */ + rsRetVal (*modInit)(int, int*, rsRetVal(**)()); /* initialize the module */ + /* be sure to support version handshake! */ + rsRetVal (*modQueryEtryPt)(uchar *name, rsRetVal (**EtryPoint)()); /* query entry point addresses */ + rsRetVal (*isCompatibleWithFeature)(syslogFeature); + rsRetVal (*freeInstance)(void*);/* called before termination or module unload */ + rsRetVal (*dbgPrintInstInfo)(void*);/* called before termination or module unload */ + rsRetVal (*tryResume)(void*);/* called to see if module actin can be resumed now */ + rsRetVal (*modExit)(void); /* called before termination or module unload */ + rsRetVal (*modGetID)(void **); /* get its unique ID from module */ + /* below: parse a configuration line - return if processed + * or not. If not, must be parsed to next module. + */ + rsRetVal (*parseConfigLine)(uchar **pConfLine); + /* below: create an instance of this module. Most importantly the module + * can allocate instance memory in this call. + */ + rsRetVal (*createInstance)(); + /* TODO: pass pointer to msg submit function to IM rger, 2007-12-14 */ + union { + struct {/* data for input modules */ + rsRetVal (*runInput)(thrdInfo_t*); /* function to gather input and submit to queue */ + rsRetVal (*willRun)(void); /* function to gather input and submit to queue */ + rsRetVal (*afterRun)(thrdInfo_t*); /* function to gather input and submit to queue */ + } im; + struct {/* data for output modules */ + /* below: perform the configured action + */ + rsRetVal (*doAction)(uchar**, unsigned, void*); + rsRetVal (*parseSelectorAct)(uchar**, void**,omodStringRequest_t**); + } om; + struct { /* data for library modules */ + } fm; + } mod; + void *pModHdlr; /* handler to the dynamic library holding the module */ +# ifdef DEBUG + /* we add some home-grown support to track our users (and detect who does not free us). In + * the long term, this should probably be migrated into debug.c (TODO). -- rgerhards, 2008-03-11 + */ + modUsr_t *pModUsrRoot; +# endif +} modInfo_t; + +/* interfaces */ +BEGINinterface(module) /* name must also be changed in ENDinterface macro! */ + modInfo_t *(*GetNxt)(modInfo_t *pThis); + modInfo_t *(*GetNxtType)(modInfo_t *pThis, eModType_t rqtdType); + uchar *(*GetName)(modInfo_t *pThis); + uchar *(*GetStateName)(modInfo_t *pThis); + rsRetVal (*Use)(char *srcFile, modInfo_t *pThis); /**< must be called before a module is used (ref counting) */ + rsRetVal (*Release)(char *srcFile, modInfo_t **ppThis); /**< release a module (ref counting) */ + void (*PrintList)(void); + rsRetVal (*UnloadAndDestructAll)(eModLinkType_t modLinkTypesToUnload); + rsRetVal (*doModInit)(rsRetVal (*modInit)(), uchar *name, void *pModHdlr); + rsRetVal (*Load)(uchar *name); + rsRetVal (*SetModDir)(uchar *name); +ENDinterface(module) +#define moduleCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ + +/* prototypes */ +PROTOTYPEObj(module); + +/* TODO: remove them below (means move the config init code) -- rgerhards, 2008-02-19 */ +extern uchar *pModDir; /* read-only after startup */ + + +#endif /* #ifndef MODULES_H_INCLUDED */ +/* + * vi:set ai: + */ diff --git a/runtime/msg.c b/runtime/msg.c new file mode 100644 index 00000000..ed9cdbbb --- /dev/null +++ b/runtime/msg.c @@ -0,0 +1,2294 @@ +/* msg.c + * The msg object. Implementation of all msg-related functions + * + * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) + * This file is under development and has not yet arrived at being fully + * self-contained and a real object. So far, it is mostly an excerpt + * of the "old" message code without any modifications. However, it + * helps to have things at the right place one we go to the meat of it. + * + * Copyright 2007, 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" +#include +#include +#include +#define SYSLOG_NAMES +#include +#include +#include +#include "rsyslog.h" +#include "syslogd.h" +#include "srUtils.h" +#include "stringbuf.h" +#include "template.h" +#include "msg.h" +#include "var.h" +#include "datetime.h" +#include "regexp.h" +#include "atomic.h" + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(var) +DEFobjCurrIf(datetime) +DEFobjCurrIf(regexp) + +static syslogCODE rs_prioritynames[] = + { + { "alert", LOG_ALERT }, + { "crit", LOG_CRIT }, + { "debug", LOG_DEBUG }, + { "emerg", LOG_EMERG }, + { "err", LOG_ERR }, + { "error", LOG_ERR }, /* DEPRECATED */ + { "info", LOG_INFO }, + { "none", INTERNAL_NOPRI }, /* INTERNAL */ + { "notice", LOG_NOTICE }, + { "panic", LOG_EMERG }, /* DEPRECATED */ + { "warn", LOG_WARNING }, /* DEPRECATED */ + { "warning", LOG_WARNING }, + { NULL, -1 } + }; + +#ifndef LOG_AUTHPRIV +# define LOG_AUTHPRIV LOG_AUTH +#endif +static syslogCODE rs_facilitynames[] = + { + { "auth", LOG_AUTH }, + { "authpriv", LOG_AUTHPRIV }, + { "cron", LOG_CRON }, + { "daemon", LOG_DAEMON }, +#if defined(LOG_FTP) + {"ftp", LOG_FTP}, +#endif + { "kern", LOG_KERN }, + { "lpr", LOG_LPR }, + { "mail", LOG_MAIL }, + //{ "mark", INTERNAL_MARK }, /* INTERNAL */ + { "news", LOG_NEWS }, + { "security", LOG_AUTH }, /* DEPRECATED */ + { "syslog", LOG_SYSLOG }, + { "user", LOG_USER }, + { "uucp", LOG_UUCP }, + { "local0", LOG_LOCAL0 }, + { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, + { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, + { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, + { "local7", LOG_LOCAL7 }, + { NULL, -1 } + }; + +/* some forward declarations */ +static int getAPPNAMELen(msg_t *pM); + +/* The following functions will support advanced output module + * multithreading, once this is implemented. Currently, we + * include them as hooks only. The idea is that we need to guard + * some msg objects data fields against concurrent access if + * we run on multiple threads. Please note that in any case this + * is not necessary for calls from INPUT modules, because they + * construct the message object and do this serially. Only when + * the message is in the processing queue, multiple threads may + * access a single object. Consequently, there are no guard functions + * for "set" methods, as these are called during input. Only "get" + * functions that modify important structures have them. + * rgerhards, 2007-07-20 + * We now support locked and non-locked operations, depending on + * the configuration of rsyslog. To support this, we use function + * pointers. Initially, we start in non-locked mode. There, all + * locking operations call into dummy functions. When locking is + * enabled, the function pointers are changed to functions doing + * actual work. We also introduced another MsgPrepareEnqueue() function + * which initializes the locking structures, if needed. This is + * necessary because internal messages during config file startup + * processing are always created in non-locking mode. So we can + * not initialize locking structures during constructions. We now + * postpone this until when the message is fully constructed and + * enqueued. Then we know the status of locking. This has a nice + * side effect, and that is that during the initial creation of + * the Msg object no locking needs to be done, which results in better + * performance. -- rgerhards, 2008-01-05 + */ +static void (*funcLock)(msg_t *pMsg); +static void (*funcUnlock)(msg_t *pMsg); +static void (*funcDeleteMutex)(msg_t *pMsg); +void (*funcMsgPrepareEnqueue)(msg_t *pMsg); +#if 1 /* This is a debug aid */ +#define MsgLock(pMsg) funcLock(pMsg) +#define MsgUnlock(pMsg) funcUnlock(pMsg) +#else +#define MsgLock(pMsg) {dbgprintf("line %d\n - ", __LINE__); funcLock(pMsg);; } +#define MsgUnlock(pMsg) {dbgprintf("line %d - ", __LINE__); funcUnlock(pMsg); } +#endif + +/* the next function is a dummy to be used by the looking functions + * when the class is not yet running in an environment where locking + * is necessary. Please note that the need to lock can (and will) change + * during a single run. Typically, this is depending on the operation mode + * of the message queues (which is operator-configurable). -- rgerhards, 2008-01-05 + */ +static void MsgLockingDummy(msg_t __attribute__((unused)) *pMsg) +{ + /* empty be design */ +} + + +/* The following function prepares a message for enqueue into the queue. This is + * where a message may be accessed by multiple threads. This implementation here + * is the version for multiple concurrent acces. It initializes the locking + * structures. + */ +static void MsgPrepareEnqueueLockingCase(msg_t *pThis) +{ + assert(pThis != NULL); + pthread_mutexattr_settype(&pThis->mutAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&pThis->mut, &pThis->mutAttr); +} + +/* ... and now the locking and unlocking implementations: */ +static void MsgLockLockingCase(msg_t *pThis) +{ + /* DEV debug only! dbgprintf("MsgLock(0x%lx)\n", (unsigned long) pThis); */ + assert(pThis != NULL); + pthread_mutex_lock(&pThis->mut); +} + +static void MsgUnlockLockingCase(msg_t *pThis) +{ + /* DEV debug only! dbgprintf("MsgUnlock(0x%lx)\n", (unsigned long) pThis); */ + assert(pThis != NULL); + pthread_mutex_unlock(&pThis->mut); +} + +/* delete the mutex object on message destruction (locking case) + */ +static void MsgDeleteMutexLockingCase(msg_t *pThis) +{ + assert(pThis != NULL); + pthread_mutex_destroy(&pThis->mut); +} + +/* enable multiple concurrent access on the message object + * This works on a class-wide basis and can bot be undone. + * That is, if it is once enabled, it can not be disabled during + * the same run. When this function is called, no other thread + * must manipulate message objects. Then we would have race conditions, + * but guarding against this is counter-productive because it + * would cost additional time. Plus, it would be a programming error. + * rgerhards, 2008-01-05 + */ +rsRetVal MsgEnableThreadSafety(void) +{ + funcLock = MsgLockLockingCase; + funcUnlock = MsgUnlockLockingCase; + funcMsgPrepareEnqueue = MsgPrepareEnqueueLockingCase; + funcDeleteMutex = MsgDeleteMutexLockingCase; + return RS_RET_OK; +} + +/* end locking functions */ + + +/* "Constructor" for a msg "object". Returns a pointer to + * the new object or NULL if no such object could be allocated. + * An object constructed via this function should only be destroyed + * via "msgDestruct()". + */ +rsRetVal msgConstruct(msg_t **ppThis) +{ + DEFiRet; + msg_t *pM; + + assert(ppThis != NULL); + if((pM = calloc(1, sizeof(msg_t))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + /* initialize members that are non-zero */ + pM->iRefCount = 1; + pM->iSeverity = -1; + pM->iFacility = -1; + datetime.getCurrTime(&(pM->tRcvdAt)); + objConstructSetObjInfo(pM); + + /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/ + + *ppThis = pM; + +finalize_it: + RETiRet; +} + + +BEGINobjDestruct(msg) /* be sure to specify the object type also in END and CODESTART macros! */ + int currRefCount; +CODESTARTobjDestruct(msg) + /* DEV Debugging only ! dbgprintf("msgDestruct\t0x%lx, Ref now: %d\n", (unsigned long)pM, pM->iRefCount - 1); */ +# ifdef DO_HAVE_ATOMICS + currRefCount = ATOMIC_DEC_AND_FETCH(pThis->iRefCount); +# else + currRefCount = --pThis->iRefCount; +# endif + if(currRefCount == 0) + { + /* DEV Debugging Only! dbgprintf("msgDestruct\t0x%lx, RefCount now 0, doing DESTROY\n", (unsigned long)pThis); */ + if(pThis->pszUxTradMsg != NULL) + free(pThis->pszUxTradMsg); + if(pThis->pszRawMsg != NULL) + free(pThis->pszRawMsg); + if(pThis->pszTAG != NULL) + free(pThis->pszTAG); + if(pThis->pszHOSTNAME != NULL) + free(pThis->pszHOSTNAME); + if(pThis->pszRcvFrom != NULL) + free(pThis->pszRcvFrom); + if(pThis->pszMSG != NULL) + free(pThis->pszMSG); + if(pThis->pszFacility != NULL) + free(pThis->pszFacility); + if(pThis->pszFacilityStr != NULL) + free(pThis->pszFacilityStr); + if(pThis->pszSeverity != NULL) + free(pThis->pszSeverity); + if(pThis->pszSeverityStr != NULL) + free(pThis->pszSeverityStr); + if(pThis->pszRcvdAt3164 != NULL) + free(pThis->pszRcvdAt3164); + if(pThis->pszRcvdAt3339 != NULL) + free(pThis->pszRcvdAt3339); + if(pThis->pszRcvdAt_MySQL != NULL) + free(pThis->pszRcvdAt_MySQL); + if(pThis->pszRcvdAt_PgSQL != NULL) + free(pThis->pszRcvdAt_PgSQL); + if(pThis->pszTIMESTAMP3164 != NULL) + free(pThis->pszTIMESTAMP3164); + if(pThis->pszTIMESTAMP3339 != NULL) + free(pThis->pszTIMESTAMP3339); + if(pThis->pszTIMESTAMP_MySQL != NULL) + free(pThis->pszTIMESTAMP_MySQL); + if(pThis->pszTIMESTAMP_PgSQL != NULL) + free(pThis->pszTIMESTAMP_PgSQL); + if(pThis->pszPRI != NULL) + free(pThis->pszPRI); + if(pThis->pCSProgName != NULL) + rsCStrDestruct(&pThis->pCSProgName); + if(pThis->pCSStrucData != NULL) + rsCStrDestruct(&pThis->pCSStrucData); + if(pThis->pCSAPPNAME != NULL) + rsCStrDestruct(&pThis->pCSAPPNAME); + if(pThis->pCSPROCID != NULL) + rsCStrDestruct(&pThis->pCSPROCID); + if(pThis->pCSMSGID != NULL) + rsCStrDestruct(&pThis->pCSMSGID); + funcDeleteMutex(pThis); + } else { + pThis = NULL; /* tell framework not to destructing the object! */ + } +ENDobjDestruct(msg) + + +/* The macros below are used in MsgDup(). I use macros + * to keep the fuction code somewhat more readyble. It is my + * replacement for inline functions in CPP + */ +#define tmpCOPYSZ(name) \ + if(pOld->psz##name != NULL) { \ + if((pNew->psz##name = srUtilStrDup(pOld->psz##name, pOld->iLen##name)) == NULL) {\ + msgDestruct(&pNew);\ + return NULL;\ + }\ + pNew->iLen##name = pOld->iLen##name;\ + } + +/* copy the CStr objects. + * if the old value is NULL, we do not need to do anything because we + * initialized the new value to NULL via calloc(). + */ +#define tmpCOPYCSTR(name) \ + if(pOld->pCS##name != NULL) {\ + if(rsCStrConstructFromCStr(&(pNew->pCS##name), pOld->pCS##name) != RS_RET_OK) {\ + msgDestruct(&pNew);\ + return NULL;\ + }\ + } +/* Constructs a message object by duplicating another one. + * Returns NULL if duplication failed. We do not need to lock the + * message object here, because a fully-created msg object is never + * allowed to be manipulated. For this, MsgDup() must be used, so MsgDup() + * can never run into a situation where the message object is being + * modified while its content is copied - it's forbidden by definition. + * rgerhards, 2007-07-10 + */ +msg_t* MsgDup(msg_t* pOld) +{ + msg_t* pNew; + + assert(pOld != NULL); + + BEGINfunc + if(msgConstruct(&pNew) != RS_RET_OK) { + return NULL; + } + + /* now copy the message properties */ + pNew->iRefCount = 1; + pNew->iSeverity = pOld->iSeverity; + pNew->iFacility = pOld->iFacility; + pNew->bParseHOSTNAME = pOld->bParseHOSTNAME; + pNew->msgFlags = pOld->msgFlags; + pNew->iProtocolVersion = pOld->iProtocolVersion; + memcpy(&pNew->tRcvdAt, &pOld->tRcvdAt, sizeof(struct syslogTime)); + memcpy(&pNew->tTIMESTAMP, &pOld->tTIMESTAMP, sizeof(struct syslogTime)); + tmpCOPYSZ(Severity); + tmpCOPYSZ(SeverityStr); + tmpCOPYSZ(Facility); + tmpCOPYSZ(FacilityStr); + tmpCOPYSZ(PRI); + tmpCOPYSZ(RawMsg); + tmpCOPYSZ(MSG); + tmpCOPYSZ(UxTradMsg); + tmpCOPYSZ(TAG); + tmpCOPYSZ(HOSTNAME); + tmpCOPYSZ(RcvFrom); + + tmpCOPYCSTR(ProgName); + tmpCOPYCSTR(StrucData); + tmpCOPYCSTR(APPNAME); + tmpCOPYCSTR(PROCID); + tmpCOPYCSTR(MSGID); + + /* we do not copy all other cache properties, as we do not even know + * if they are needed once again. So we let them re-create if needed. + */ + + ENDfunc + return pNew; +} +#undef tmpCOPYSZ +#undef tmpCOPYCSTR + + +/* This method serializes a message object. That means the whole + * object is modified into text form. That text form is suitable for + * later reconstruction of the object by calling MsgDeSerialize(). + * The most common use case for this method is the creation of an + * on-disk representation of the message object. + * We do not serialize the cache properties. We re-create them when needed. + * This saves us a lot of memory. Performance is no concern, as serializing + * is a so slow operation that recration of the caches does not count. Also, + * we do not serialize bParseHOSTNAME, as this is only a helper variable + * during msg construction - and never again used later. + * rgerhards, 2008-01-03 + */ +static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) +{ + DEFiRet; + + assert(pThis != NULL); + assert(pStrm != NULL); + + CHKiRet(obj.BeginSerialize(pStrm, (obj_t*) pThis)); + objSerializeSCALAR(pStrm, iProtocolVersion, SHORT); + objSerializeSCALAR(pStrm, iSeverity, SHORT); + objSerializeSCALAR(pStrm, iFacility, SHORT); + objSerializeSCALAR(pStrm, msgFlags, INT); + objSerializeSCALAR(pStrm, tRcvdAt, SYSLOGTIME); + objSerializeSCALAR(pStrm, tTIMESTAMP, SYSLOGTIME); + + objSerializePTR(pStrm, pszRawMsg, PSZ); + objSerializePTR(pStrm, pszMSG, PSZ); + objSerializePTR(pStrm, pszUxTradMsg, PSZ); + objSerializePTR(pStrm, pszTAG, PSZ); + objSerializePTR(pStrm, pszHOSTNAME, PSZ); + objSerializePTR(pStrm, pszRcvFrom, PSZ); + + objSerializePTR(pStrm, pCSStrucData, CSTR); + objSerializePTR(pStrm, pCSAPPNAME, CSTR); + objSerializePTR(pStrm, pCSPROCID, CSTR); + objSerializePTR(pStrm, pCSMSGID, CSTR); + + CHKiRet(obj.EndSerialize(pStrm)); + +finalize_it: + RETiRet; +} + + +/* Increment reference count - see description of the "msg" + * structure for details. As a convenience to developers, + * this method returns the msg pointer that is passed to it. + * It is recommended that it is called as follows: + * + * pSecondMsgPointer = MsgAddRef(pOrgMsgPointer); + */ +msg_t *MsgAddRef(msg_t *pM) +{ + assert(pM != NULL); +# ifdef DO_HAVE_ATOMICS + ATOMIC_INC(pM->iRefCount); +# else + MsgLock(pM); + pM->iRefCount++; + MsgUnlock(pM); +# endif + /* DEV debugging only! dbgprintf("MsgAddRef\t0x%x done, Ref now: %d\n", (int)pM, pM->iRefCount);*/ + return(pM); +} + + +/* This functions tries to aquire the PROCID from TAG. Its primary use is + * when a legacy syslog message has been received and should be forwarded as + * syslog-protocol (or the PROCID is requested for any other reason). + * In legacy syslog, the PROCID is considered to be the character sequence + * between the first [ and the first ]. This usually are digits only, but we + * do not check that. However, if there is no closing ], we do not assume we + * can obtain a PROCID. Take in mind that not every legacy syslog message + * actually has a PROCID. + * rgerhards, 2005-11-24 + */ +static rsRetVal aquirePROCIDFromTAG(msg_t *pM) +{ + register int i; + DEFiRet; + + assert(pM != NULL); + if(pM->pCSPROCID != NULL) + return RS_RET_OK; /* we are already done ;) */ + + if(getProtocolVersion(pM) != 0) + return RS_RET_OK; /* we can only emulate if we have legacy format */ + + /* find first '['... */ + i = 0; + while((i < pM->iLenTAG) && (pM->pszTAG[i] != '[')) + ++i; + if(!(i < pM->iLenTAG)) + return RS_RET_OK; /* no [, so can not emulate... */ + + ++i; /* skip '[' */ + + /* now obtain the PROCID string... */ + CHKiRet(rsCStrConstruct(&pM->pCSPROCID)); + rsCStrSetAllocIncrement(pM->pCSPROCID, 16); + while((i < pM->iLenTAG) && (pM->pszTAG[i] != ']')) { + CHKiRet(rsCStrAppendChar(pM->pCSPROCID, pM->pszTAG[i])); + ++i; + } + + if(!(i < pM->iLenTAG)) { + /* oops... it looked like we had a PROCID, but now it has + * turned out this is not true. In this case, we need to free + * the buffer and simply return. Note that this is NOT an error + * case! + */ + rsCStrDestruct(&pM->pCSPROCID); + FINALIZE; + } + + /* OK, finaally we could obtain a PROCID. So let's use it ;) */ + CHKiRet(rsCStrFinish(pM->pCSPROCID)); + +finalize_it: + RETiRet; +} + + +/* Parse and set the "programname" for a given MSG object. Programname + * is a BSD concept, it is the tag without any instance-specific information. + * Precisely, the programname is terminated by either (whichever occurs first): + * - end of tag + * - nonprintable character + * - ':' + * - '[' + * - '/' + * The above definition has been taken from the FreeBSD syslogd sources. + * + * The program name is not parsed by default, because it is infrequently-used. + * If it is needed, this function should be called first. It checks if it is + * already set and extracts it, if not. + * A message object must be provided, else a crash will occur. + * rgerhards, 2005-10-19 + */ +static rsRetVal aquireProgramName(msg_t *pM) +{ + DEFiRet; + register int i; + + assert(pM != NULL); + if(pM->pCSProgName == NULL) { + /* ok, we do not yet have it. So let's parse the TAG + * to obtain it. + */ + CHKiRet(rsCStrConstruct(&pM->pCSProgName)); + rsCStrSetAllocIncrement(pM->pCSProgName, 33); + for( i = 0 + ; (i < pM->iLenTAG) && isprint((int) pM->pszTAG[i]) + && (pM->pszTAG[i] != '\0') && (pM->pszTAG[i] != ':') + && (pM->pszTAG[i] != '[') && (pM->pszTAG[i] != '/') + ; ++i) { + CHKiRet(rsCStrAppendChar(pM->pCSProgName, pM->pszTAG[i])); + } + CHKiRet(rsCStrFinish(pM->pCSProgName)); + } +finalize_it: + RETiRet; +} + + +/* This function moves the HOSTNAME inside the message object to the + * TAG. It is a specialised function used to handle the condition when + * a message without HOSTNAME is being processed. The missing HOSTNAME + * is only detected at a later stage, during TAG processing, so that + * we already had set the HOSTNAME property and now need to move it to + * the TAG. Of course, we could do this via a couple of get/set methods, + * but it is far more efficient to do it via this specialised method. + * This is especially important as this can be a very common case, e.g. + * when BSD syslog is acting as a sender. + * rgerhards, 2005-11-10. + */ +void moveHOSTNAMEtoTAG(msg_t *pM) +{ + assert(pM != NULL); + if(pM->pszTAG != NULL) + free(pM->pszTAG); + pM->pszTAG = pM->pszHOSTNAME; + pM->iLenTAG = pM->iLenHOSTNAME; + pM->pszHOSTNAME = NULL; + pM->iLenHOSTNAME = 0; +} + +/* Access methods - dumb & easy, not a comment for each ;) + */ +void setProtocolVersion(msg_t *pM, int iNewVersion) +{ + assert(pM != NULL); + if(iNewVersion != 0 && iNewVersion != 1) { + dbgprintf("Tried to set unsupported protocol version %d - changed to 0.\n", iNewVersion); + iNewVersion = 0; + } + pM->iProtocolVersion = iNewVersion; +} + +int getProtocolVersion(msg_t *pM) +{ + assert(pM != NULL); + return(pM->iProtocolVersion); +} + +/* note: string is taken from constant pool, do NOT free */ +char *getProtocolVersionString(msg_t *pM) +{ + assert(pM != NULL); + return(pM->iProtocolVersion ? "1" : "0"); +} + +int getMSGLen(msg_t *pM) +{ + return((pM == NULL) ? 0 : pM->iLenMSG); +} + + +char *getRawMsg(msg_t *pM) +{ + if(pM == NULL) + return ""; + else + if(pM->pszRawMsg == NULL) + return ""; + else + return (char*)pM->pszRawMsg; +} + +char *getUxTradMsg(msg_t *pM) +{ + if(pM == NULL) + return ""; + else + if(pM->pszUxTradMsg == NULL) + return ""; + else + return (char*)pM->pszUxTradMsg; +} + +char *getMSG(msg_t *pM) +{ + if(pM == NULL) + return ""; + else + if(pM->pszMSG == NULL) + return ""; + else + return (char*)pM->pszMSG; +} + + +/* Get PRI value in text form */ +char *getPRI(msg_t *pM) +{ + if(pM == NULL) + return ""; + + MsgLock(pM); + if(pM->pszPRI == NULL) { + /* OK, we need to construct it... + * we use a 5 byte buffer - as of + * RFC 3164, it can't be longer. Should it + * still be, snprintf will truncate... + */ + if((pM->pszPRI = malloc(5)) == NULL) return ""; + pM->iLenPRI = snprintf((char*)pM->pszPRI, 5, "%d", + LOG_MAKEPRI(pM->iFacility, pM->iSeverity)); + } + MsgUnlock(pM); + + return (char*)pM->pszPRI; +} + + +/* Get PRI value as integer */ +int getPRIi(msg_t *pM) +{ + assert(pM != NULL); + return (pM->iFacility << 3) + (pM->iSeverity); +} + + +char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) +{ + if(pM == NULL) + return ""; + + switch(eFmt) { + case tplFmtDefault: + MsgLock(pM); + if(pM->pszTIMESTAMP3164 == NULL) { + if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP3164); + case tplFmtMySQLDate: + MsgLock(pM); + if(pM->pszTIMESTAMP_MySQL == NULL) { + if((pM->pszTIMESTAMP_MySQL = malloc(15)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestampToMySQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_MySQL, 15); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP_MySQL); + case tplFmtPgSQLDate: + MsgLock(pM); + if(pM->pszTIMESTAMP_PgSQL == NULL) { + if((pM->pszTIMESTAMP_PgSQL = malloc(21)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestampToPgSQL(&pM->tTIMESTAMP, pM->pszTIMESTAMP_PgSQL, 21); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP_PgSQL); + case tplFmtRFC3164Date: + MsgLock(pM); + if(pM->pszTIMESTAMP3164 == NULL) { + if((pM->pszTIMESTAMP3164 = malloc(16)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestamp3164(&pM->tTIMESTAMP, pM->pszTIMESTAMP3164, 16); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP3164); + case tplFmtRFC3339Date: + MsgLock(pM); + if(pM->pszTIMESTAMP3339 == NULL) { + if((pM->pszTIMESTAMP3339 = malloc(33)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; /* TODO: check this: can it cause a free() of constant memory?) */ + } + datetime.formatTimestamp3339(&pM->tTIMESTAMP, pM->pszTIMESTAMP3339, 33); + } + MsgUnlock(pM); + return(pM->pszTIMESTAMP3339); + } + return "INVALID eFmt OPTION!"; +} + +char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) +{ + if(pM == NULL) + return ""; + + switch(eFmt) { + case tplFmtDefault: + MsgLock(pM); + if(pM->pszRcvdAt3164 == NULL) { + if((pM->pszRcvdAt3164 = malloc(16)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16); + } + MsgUnlock(pM); + return(pM->pszRcvdAt3164); + case tplFmtMySQLDate: + MsgLock(pM); + if(pM->pszRcvdAt_MySQL == NULL) { + if((pM->pszRcvdAt_MySQL = malloc(15)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestampToMySQL(&pM->tRcvdAt, pM->pszRcvdAt_MySQL, 15); + } + MsgUnlock(pM); + return(pM->pszRcvdAt_MySQL); + case tplFmtPgSQLDate: + MsgLock(pM); + if(pM->pszRcvdAt_PgSQL == NULL) { + if((pM->pszRcvdAt_PgSQL = malloc(21)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestampToPgSQL(&pM->tRcvdAt, pM->pszRcvdAt_PgSQL, 21); + } + MsgUnlock(pM); + return(pM->pszRcvdAt_PgSQL); + case tplFmtRFC3164Date: + MsgLock(pM); + if(pM->pszRcvdAt3164 == NULL) { + if((pM->pszRcvdAt3164 = malloc(16)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestamp3164(&pM->tRcvdAt, pM->pszRcvdAt3164, 16); + } + MsgUnlock(pM); + return(pM->pszRcvdAt3164); + case tplFmtRFC3339Date: + MsgLock(pM); + if(pM->pszRcvdAt3339 == NULL) { + if((pM->pszRcvdAt3339 = malloc(33)) == NULL) { + glblHadMemShortage = 1; + MsgUnlock(pM); + return ""; + } + datetime.formatTimestamp3339(&pM->tRcvdAt, pM->pszRcvdAt3339, 33); + } + MsgUnlock(pM); + return(pM->pszRcvdAt3339); + } + return "INVALID eFmt OPTION!"; +} + + +char *getSeverity(msg_t *pM) +{ + if(pM == NULL) + return ""; + + MsgLock(pM); + if(pM->pszSeverity == NULL) { + /* we use a 2 byte buffer - can only be one digit */ + if((pM->pszSeverity = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; } + pM->iLenSeverity = + snprintf((char*)pM->pszSeverity, 2, "%d", pM->iSeverity); + } + MsgUnlock(pM); + return((char*)pM->pszSeverity); +} + + +char *getSeverityStr(msg_t *pM) +{ + syslogCODE *c; + int val; + char *name = NULL; + + if(pM == NULL) + return ""; + + MsgLock(pM); + if(pM->pszSeverityStr == NULL) { + for(c = rs_prioritynames, val = pM->iSeverity; c->c_name; c++) + if(c->c_val == val) { + name = c->c_name; + break; + } + if(name == NULL) { + /* we use a 2 byte buffer - can only be one digit */ + if((pM->pszSeverityStr = malloc(2)) == NULL) { MsgUnlock(pM) ; return ""; } + pM->iLenSeverityStr = + snprintf((char*)pM->pszSeverityStr, 2, "%d", pM->iSeverity); + } else { + if((pM->pszSeverityStr = (uchar*) strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; } + pM->iLenSeverityStr = strlen((char*)name); + } + } + MsgUnlock(pM); + return((char*)pM->pszSeverityStr); +} + +char *getFacility(msg_t *pM) +{ + if(pM == NULL) + return ""; + + MsgLock(pM); + if(pM->pszFacility == NULL) { + /* we use a 12 byte buffer - as of + * syslog-protocol, facility can go + * up to 2^32 -1 + */ + if((pM->pszFacility = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; } + pM->iLenFacility = + snprintf((char*)pM->pszFacility, 12, "%d", pM->iFacility); + } + MsgUnlock(pM); + return((char*)pM->pszFacility); +} + +char *getFacilityStr(msg_t *pM) +{ + syslogCODE *c; + int val; + char *name = NULL; + + if(pM == NULL) + return ""; + + MsgLock(pM); + if(pM->pszFacilityStr == NULL) { + for(c = rs_facilitynames, val = pM->iFacility << 3; c->c_name; c++) + if(c->c_val == val) { + name = c->c_name; + break; + } + if(name == NULL) { + /* we use a 12 byte buffer - as of + * syslog-protocol, facility can go + * up to 2^32 -1 + */ + if((pM->pszFacilityStr = malloc(12)) == NULL) { MsgUnlock(pM) ; return ""; } + pM->iLenFacilityStr = + snprintf((char*)pM->pszFacilityStr, 12, "%d", val >> 3); + } else { + if((pM->pszFacilityStr = (uchar*)strdup(name)) == NULL) { MsgUnlock(pM) ; return ""; } + pM->iLenFacilityStr = strlen((char*)name); + } + } + MsgUnlock(pM); + return((char*)pM->pszFacilityStr); +} + + +/* set flow control state (if not called, the default - NO_DELAY - is used) + * This needs no locking because it is only done while the object is + * not fully constructed (which also means you must not call this + * method after the msg has been handed over to a queue). + * rgerhards, 2008-03-14 + */ +rsRetVal +MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl) +{ + DEFiRet; + assert(pMsg != NULL); + assert(eFlowCtl == eFLOWCTL_NO_DELAY || eFlowCtl == eFLOWCTL_LIGHT_DELAY || eFlowCtl == eFLOWCTL_FULL_DELAY); + + pMsg->flowCtlType = eFlowCtl; + + RETiRet; +} + + +/* rgerhards 2004-11-24: set APP-NAME in msg object + * TODO: revisit msg locking code! + */ +rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME) +{ + DEFiRet; + assert(pMsg != NULL); + if(pMsg->pCSAPPNAME == NULL) { + /* we need to obtain the object first */ + CHKiRet(rsCStrConstruct(&pMsg->pCSAPPNAME)); + rsCStrSetAllocIncrement(pMsg->pCSAPPNAME, 128); + } + /* if we reach this point, we have the object */ + iRet = rsCStrSetSzStr(pMsg->pCSAPPNAME, (uchar*) pszAPPNAME); + +finalize_it: + RETiRet; +} + + +static void tryEmulateAPPNAME(msg_t *pM); /* forward reference */ +/* rgerhards, 2005-11-24 + */ +char *getAPPNAME(msg_t *pM) +{ + assert(pM != NULL); + MsgLock(pM); + if(pM->pCSAPPNAME == NULL) + tryEmulateAPPNAME(pM); + MsgUnlock(pM); + return (pM->pCSAPPNAME == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSAPPNAME); +} + + +/* rgerhards 2004-11-24: set PROCID in msg object + */ +rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID) +{ + DEFiRet; + ISOBJ_TYPE_assert(pMsg, msg); + if(pMsg->pCSPROCID == NULL) { + /* we need to obtain the object first */ + CHKiRet(rsCStrConstruct(&pMsg->pCSPROCID)); + rsCStrSetAllocIncrement(pMsg->pCSPROCID, 128); + } + /* if we reach this point, we have the object */ + iRet = rsCStrSetSzStr(pMsg->pCSPROCID, (uchar*) pszPROCID); + +finalize_it: + RETiRet; +} + +/* rgerhards, 2005-11-24 + */ +int getPROCIDLen(msg_t *pM) +{ + assert(pM != NULL); + MsgLock(pM); + if(pM->pCSPROCID == NULL) + aquirePROCIDFromTAG(pM); + MsgUnlock(pM); + return (pM->pCSPROCID == NULL) ? 1 : rsCStrLen(pM->pCSPROCID); +} + + +/* rgerhards, 2005-11-24 + */ +char *getPROCID(msg_t *pM) +{ + char* pszRet; + + ISOBJ_TYPE_assert(pM, msg); + MsgLock(pM); + if(pM->pCSPROCID == NULL) + aquirePROCIDFromTAG(pM); + pszRet = (pM->pCSPROCID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSPROCID); + MsgUnlock(pM); + return pszRet; +} + + +/* rgerhards 2004-11-24: set MSGID in msg object + */ +rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID) +{ + DEFiRet; + ISOBJ_TYPE_assert(pMsg, msg); + if(pMsg->pCSMSGID == NULL) { + /* we need to obtain the object first */ + CHKiRet(rsCStrConstruct(&pMsg->pCSMSGID)); + rsCStrSetAllocIncrement(pMsg->pCSMSGID, 128); + } + /* if we reach this point, we have the object */ + iRet = rsCStrSetSzStr(pMsg->pCSMSGID, (uchar*) pszMSGID); + +finalize_it: + RETiRet; +} + +/* rgerhards, 2005-11-24 + */ +#if 0 /* This method is currently not called, be we like to preserve it */ +static int getMSGIDLen(msg_t *pM) +{ + return (pM->pCSMSGID == NULL) ? 1 : rsCStrLen(pM->pCSMSGID); +} +#endif + + +/* rgerhards, 2005-11-24 + */ +char *getMSGID(msg_t *pM) +{ + return (pM->pCSMSGID == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSMSGID); +} + + +/* Set the TAG to a caller-provided string. This is thought + * to be a heap buffer that the caller will no longer use. This + * function is a performance optimization over MsgSetTAG(). + * rgerhards 2004-11-19 + */ +void MsgAssignTAG(msg_t *pMsg, uchar *pBuf) +{ + assert(pMsg != NULL); + pMsg->iLenTAG = (pBuf == NULL) ? 0 : strlen((char*)pBuf); + pMsg->pszTAG = (uchar*) pBuf; +} + + +/* rgerhards 2004-11-16: set TAG in msg object + */ +void MsgSetTAG(msg_t *pMsg, char* pszTAG) +{ + assert(pMsg != NULL); + if(pMsg->pszTAG != NULL) + free(pMsg->pszTAG); + pMsg->iLenTAG = strlen(pszTAG); + if((pMsg->pszTAG = malloc(pMsg->iLenTAG + 1)) != NULL) + memcpy(pMsg->pszTAG, pszTAG, pMsg->iLenTAG + 1); + else + dbgprintf("Could not allocate memory in MsgSetTAG()\n"); +} + + +/* This function tries to emulate the TAG if none is + * set. Its primary purpose is to provide an old-style TAG + * when a syslog-protocol message has been received. Then, + * the tag is APP-NAME "[" PROCID "]". The function first checks + * if there is a TAG and, if not, if it can emulate it. + * rgerhards, 2005-11-24 + */ +static void tryEmulateTAG(msg_t *pM) +{ + int iTAGLen; + uchar *pBuf; + assert(pM != NULL); + + if(pM->pszTAG != NULL) + return; /* done, no need to emulate */ + + if(getProtocolVersion(pM) == 1) { + if(!strcmp(getPROCID(pM), "-")) { + /* no process ID, use APP-NAME only */ + MsgSetTAG(pM, getAPPNAME(pM)); + } else { + /* now we can try to emulate */ + iTAGLen = getAPPNAMELen(pM) + getPROCIDLen(pM) + 3; + if((pBuf = malloc(iTAGLen * sizeof(char))) == NULL) + return; /* nothing we can do */ + snprintf((char*)pBuf, iTAGLen, "%s[%s]", getAPPNAME(pM), getPROCID(pM)); + MsgAssignTAG(pM, pBuf); + } + } +} + + +#if 0 /* This method is currently not called, be we like to preserve it */ +static int getTAGLen(msg_t *pM) +{ + if(pM == NULL) + return 0; + else { + tryEmulateTAG(pM); + if(pM->pszTAG == NULL) + return 0; + else + return pM->iLenTAG; + } +} +#endif + + +char *getTAG(msg_t *pM) +{ + char *ret; + + if(pM == NULL) + ret = ""; + else { + MsgLock(pM); + tryEmulateTAG(pM); + if(pM->pszTAG == NULL) + ret = ""; + else + ret = (char*) pM->pszTAG; + MsgUnlock(pM); + } + return(ret); +} + + +int getHOSTNAMELen(msg_t *pM) +{ + if(pM == NULL) + return 0; + else + if(pM->pszHOSTNAME == NULL) + return 0; + else + return pM->iLenHOSTNAME; +} + + +char *getHOSTNAME(msg_t *pM) +{ + if(pM == NULL) + return ""; + else + if(pM->pszHOSTNAME == NULL) + return ""; + else + return (char*) pM->pszHOSTNAME; +} + + +char *getRcvFrom(msg_t *pM) +{ + if(pM == NULL) + return ""; + else + if(pM->pszRcvFrom == NULL) + return ""; + else + return (char*) pM->pszRcvFrom; +} + +/* rgerhards 2004-11-24: set STRUCTURED DATA in msg object + */ +rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData) +{ + DEFiRet; + ISOBJ_TYPE_assert(pMsg, msg); + if(pMsg->pCSStrucData == NULL) { + /* we need to obtain the object first */ + CHKiRet(rsCStrConstruct(&pMsg->pCSStrucData)); + rsCStrSetAllocIncrement(pMsg->pCSStrucData, 128); + } + /* if we reach this point, we have the object */ + iRet = rsCStrSetSzStr(pMsg->pCSStrucData, (uchar*) pszStrucData); + +finalize_it: + RETiRet; +} + +/* get the length of the "STRUCTURED-DATA" sz string + * rgerhards, 2005-11-24 + */ +#if 0 /* This method is currently not called, be we like to preserve it */ +static int getStructuredDataLen(msg_t *pM) +{ + return (pM->pCSStrucData == NULL) ? 1 : rsCStrLen(pM->pCSStrucData); +} +#endif + + +/* get the "STRUCTURED-DATA" as sz string + * rgerhards, 2005-11-24 + */ +char *getStructuredData(msg_t *pM) +{ + return (pM->pCSStrucData == NULL) ? "-" : (char*) rsCStrGetSzStrNoNULL(pM->pCSStrucData); +} + + + +/* get the length of the "programname" sz string + * rgerhards, 2005-10-19 + */ +int getProgramNameLen(msg_t *pM) +{ + int iRet; + + assert(pM != NULL); + MsgLock(pM); + if((iRet = aquireProgramName(pM)) != RS_RET_OK) { + dbgprintf("error %d returned by aquireProgramName() in getProgramNameLen()\n", iRet); + MsgUnlock(pM); + return 0; /* best we can do (consistent wiht what getProgramName() returns) */ + } + MsgUnlock(pM); + + return (pM->pCSProgName == NULL) ? 0 : rsCStrLen(pM->pCSProgName); +} + + +/* get the "programname" as sz string + * rgerhards, 2005-10-19 + */ +char *getProgramName(msg_t *pM) /* this is the non-locking version for internal use */ +{ + int iRet; + char *pszRet; + + assert(pM != NULL); + MsgLock(pM); + if((iRet = aquireProgramName(pM)) != RS_RET_OK) { + dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet); + pszRet = ""; /* best we can do */ + } else { + pszRet = (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName); + } + + MsgUnlock(pM); + return pszRet; +} +/* The code below was an approach without PTHREAD_MUTEX_RECURSIVE + * However, it turned out to be quite complex. So far, we use recursive + * locking, which is OK from a performance point of view, especially as + * we do not anticipate that multithreading msg objects is used often. + * However, we may re-think about using non-recursive locking and I leave this + * code in here to conserve the idea. -- rgerhards, 2008-01-05 + */ +#if 0 +static char *getProgramNameNoLock(msg_t *pM) /* this is the non-locking version for internal use */ +{ + int iRet; + + assert(pM != NULL); + if((iRet = aquireProgramName(pM)) != RS_RET_OK) { + dbgprintf("error %d returned by aquireProgramName() in getProgramName()\n", iRet); + return ""; /* best we can do */ + } + + return (pM->pCSProgName == NULL) ? "" : (char*) rsCStrGetSzStrNoNULL(pM->pCSProgName); +} +char *getProgramName(msg_t *pM) /* this is the external callable version */ +{ + char *pszRet; + + MsgLock(pM); + pszRet = getProgramNameNoLock(pM); + MsgUnlock(pM); + return pszRet; +} +/* an alternative approach has been: */ +/* The macro below is used to generate external function definitions + * for such functions that may also be called internally (and thus have + * both a locking and non-locking implementation. Over time, we could + * reconsider how we handle that. -- rgerhards, 2008-01-05 + */ +#define EXT_LOCKED_FUNC(fName, ret) \ +ret fName(msg_t *pM) \ +{ \ + ret valRet; \ + MsgLock(pM); \ + valRet = fName##NoLock(pM); \ + MsgUnlock(pM); \ + return(valRet); \ +} +EXT_LOCKED_FUNC(getProgramName, char*) +/* in this approach, the external function is provided by the macro and + * needs not to be writen. + */ +#endif /* #if 0 -- saved code */ + + +/* This function tries to emulate APPNAME if it is not present. Its + * main use is when we have received a log record via legacy syslog and + * now would like to send out the same one via syslog-protocol. + */ +static void tryEmulateAPPNAME(msg_t *pM) +{ + assert(pM != NULL); + if(pM->pCSAPPNAME != NULL) + return; /* we are already done */ + + if(getProtocolVersion(pM) == 0) { + /* only then it makes sense to emulate */ + MsgSetAPPNAME(pM, getProgramName(pM)); + } +} + + +/* rgerhards, 2005-11-24 + */ +static int getAPPNAMELen(msg_t *pM) +{ + assert(pM != NULL); + if(pM->pCSAPPNAME == NULL) + tryEmulateAPPNAME(pM); + return (pM->pCSAPPNAME == NULL) ? 0 : rsCStrLen(pM->pCSAPPNAME); +} + + +/* rgerhards 2004-11-16: set pszRcvFrom in msg object + */ +void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom) +{ + assert(pMsg != NULL); + if(pMsg->pszRcvFrom != NULL) + free(pMsg->pszRcvFrom); + + pMsg->iLenRcvFrom = strlen(pszRcvFrom); + if((pMsg->pszRcvFrom = malloc(pMsg->iLenRcvFrom + 1)) != NULL) { + memcpy(pMsg->pszRcvFrom, pszRcvFrom, pMsg->iLenRcvFrom + 1); + } +} + + +/* Set the HOSTNAME to a caller-provided string. This is thought + * to be a heap buffer that the caller will no longer use. This + * function is a performance optimization over MsgSetHOSTNAME(). + * rgerhards 2004-11-19 + */ +void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf) +{ + assert(pMsg != NULL); + assert(pBuf != NULL); + pMsg->iLenHOSTNAME = strlen(pBuf); + pMsg->pszHOSTNAME = (uchar*) pBuf; +} + + +/* rgerhards 2004-11-09: set HOSTNAME in msg object + * rgerhards, 2007-06-21: + * Does not return anything. If an error occurs, the hostname is + * simply not set. I have changed this behaviour. The only problem + * we can run into is memory shortage. If we have such, it is better + * to loose the hostname than the full message. So we silently ignore + * that problem and hope that memory will be available the next time + * we need it. The rest of the code already knows how to handle an + * unset HOSTNAME. + */ +void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME) +{ + assert(pMsg != NULL); + if(pMsg->pszHOSTNAME != NULL) + free(pMsg->pszHOSTNAME); + + pMsg->iLenHOSTNAME = strlen(pszHOSTNAME); + if((pMsg->pszHOSTNAME = malloc(pMsg->iLenHOSTNAME + 1)) != NULL) + memcpy(pMsg->pszHOSTNAME, pszHOSTNAME, pMsg->iLenHOSTNAME + 1); + else + dbgprintf("Could not allocate memory in MsgSetHOSTNAME()\n"); +} + + +/* Set the UxTradMsg to a caller-provided string. This is thought + * to be a heap buffer that the caller will no longer use. This + * function is a performance optimization over MsgSetUxTradMsg(). + * rgerhards 2004-11-19 + */ +#if 0 /* This method is currently not called, be we like to preserve it */ +static void MsgAssignUxTradMsg(msg_t *pMsg, char *pBuf) +{ + assert(pMsg != NULL); + assert(pBuf != NULL); + pMsg->iLenUxTradMsg = strlen(pBuf); + pMsg->pszUxTradMsg = pBuf; +} +#endif + + +/* rgerhards 2004-11-17: set the traditional Unix message in msg object + */ +int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg) +{ + assert(pMsg != NULL); + assert(pszUxTradMsg != NULL); + pMsg->iLenUxTradMsg = strlen(pszUxTradMsg); + if(pMsg->pszUxTradMsg != NULL) + free(pMsg->pszUxTradMsg); + if((pMsg->pszUxTradMsg = malloc(pMsg->iLenUxTradMsg + 1)) != NULL) + memcpy(pMsg->pszUxTradMsg, pszUxTradMsg, pMsg->iLenUxTradMsg + 1); + else + dbgprintf("Could not allocate memory for pszUxTradMsg buffer."); + + return(0); +} + + +/* rgerhards 2004-11-09: set MSG in msg object + */ +void MsgSetMSG(msg_t *pMsg, char* pszMSG) +{ + assert(pMsg != NULL); + assert(pszMSG != NULL); + + if(pMsg->pszMSG != NULL) + free(pMsg->pszMSG); + + pMsg->iLenMSG = strlen(pszMSG); + if((pMsg->pszMSG = (uchar*) malloc(pMsg->iLenMSG + 1)) != NULL) + memcpy(pMsg->pszMSG, pszMSG, pMsg->iLenMSG + 1); + else + dbgprintf("MsgSetMSG could not allocate memory for pszMSG buffer."); +} + +/* rgerhards 2004-11-11: set RawMsg in msg object + */ +void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg) +{ + assert(pMsg != NULL); + if(pMsg->pszRawMsg != NULL) + free(pMsg->pszRawMsg); + + pMsg->iLenRawMsg = strlen(pszRawMsg); + if((pMsg->pszRawMsg = (uchar*) malloc(pMsg->iLenRawMsg + 1)) != NULL) + memcpy(pMsg->pszRawMsg, pszRawMsg, pMsg->iLenRawMsg + 1); + else + dbgprintf("Could not allocate memory for pszRawMsg buffer."); +} + + +/* Decode a priority into textual information like auth.emerg. + * The variable pRes must point to a user-supplied buffer and + * pResLen must contain its size. The pointer to the buffer + * is also returned, what makes this functiona suitable for + * use in printf-like functions. + * Note: a buffer size of 20 characters is always sufficient. + * Interface to this function changed 2007-06-15 by RGerhards + */ +char *textpri(char *pRes, size_t pResLen, int pri) +{ + syslogCODE *c_pri, *c_fac; + + assert(pRes != NULL); + assert(pResLen > 0); + + for (c_fac = rs_facilitynames; c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri)<<3); c_fac++); + for (c_pri = rs_prioritynames; c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++); + + snprintf (pRes, pResLen, "%s.%s<%d>", c_fac->c_name, c_pri->c_name, pri); + + return pRes; +} + + +/* This function returns the current date in different + * variants. It is used to construct the $NOW series of + * system properties. The returned buffer must be freed + * by the caller when no longer needed. If the function + * can not allocate memory, it returns a NULL pointer. + * Added 2007-07-10 rgerhards + */ +typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_HHOUR, NOW_QHOUR, NOW_MINUTE } eNOWType; +#define tmpBUFSIZE 16 /* size of formatting buffer */ +static uchar *getNOW(eNOWType eNow) +{ + uchar *pBuf; + struct syslogTime t; + + if((pBuf = (uchar*) malloc(sizeof(uchar) * tmpBUFSIZE)) == NULL) { + glblHadMemShortage = 1; + return NULL; + } + + datetime.getCurrTime(&t); + switch(eNow) { + case NOW_NOW: + snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d-%2.2d-%2.2d", t.year, t.month, t.day); + break; + case NOW_YEAR: + snprintf((char*) pBuf, tmpBUFSIZE, "%4.4d", t.year); + break; + case NOW_MONTH: + snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.month); + break; + case NOW_DAY: + snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.day); + break; + case NOW_HOUR: + snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.hour); + break; + case NOW_HHOUR: + snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute / 30); + break; + case NOW_QHOUR: + snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute / 15); + break; + case NOW_MINUTE: + snprintf((char*) pBuf, tmpBUFSIZE, "%2.2d", t.minute); + break; + } + + return(pBuf); +} +#undef tmpBUFSIZE /* clean up */ + + +/* This function returns a string-representation of the + * requested message property. This is a generic function used + * to abstract properties so that these can be easier + * queried. Returns NULL if property could not be found. + * Actually, this function is a big if..elseif. What it does + * is simply to map property names (from MonitorWare) to the + * message object data fields. + * + * In case we need string forms of propertis we do not + * yet have in string form, we do a memory allocation that + * is sufficiently large (in all cases). Once the string + * form has been obtained, it is saved until the Msg object + * is finally destroyed. This is so that we save the processing + * time in the (likely) case that this property is requested + * again. It also saves us a lot of dynamic memory management + * issues in the upper layers, because we so can guarantee that + * the buffer will remain static AND available during the lifetime + * of the object. Please note that both the max size allocation as + * well as keeping things in memory might like look like a + * waste of memory (some might say it actually is...) - we + * deliberately accept this because performance is more important + * to us ;) + * rgerhards 2004-11-18 + * Parameter "bMustBeFreed" is set by this function. It tells the + * caller whether or not the string returned must be freed by the + * caller itself. It is is 0, the caller MUST NOT free it. If it is + * 1, the caller MUST free 1. Handling this wrongly leads to either + * a memory leak of a program abort (do to double-frees or frees on + * the constant memory pool). So be careful to do it right. + * rgerhards 2004-11-23 + * regular expression support contributed by Andres Riancho merged + * on 2005-09-13 + * changed so that it now an be called without a template entry (NULL). + * In this case, only the (unmodified) property is returned. This will + * be used in selector line processing. + * rgerhards 2005-09-15 + */ +char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, + cstr_t *pCSPropName, unsigned short *pbMustBeFreed) +{ + uchar *pName; + char *pRes; /* result pointer */ + char *pBufStart; + char *pBuf; + int iLen; + +#ifdef FEATURE_REGEXP + /* Variables necessary for regular expression matching */ + size_t nmatch = 1; + regmatch_t pmatch[1]; +#endif + + assert(pMsg != NULL); + assert(pbMustBeFreed != NULL); + + if(pCSPropName == NULL) { + assert(pTpe != NULL); + pName = pTpe->data.field.pPropRepl; + } else { + pName = rsCStrGetSzStrNoNULL(pCSPropName); + } + *pbMustBeFreed = 0; + + /* sometimes there are aliases to the original MonitoWare + * property names. These come after || in the ifs below. */ + if(!strcmp((char*) pName, "msg")) { + pRes = getMSG(pMsg); + } else if(!strcmp((char*) pName, "rawmsg")) { + pRes = getRawMsg(pMsg); + } else if(!strcmp((char*) pName, "uxtradmsg")) { + pRes = getUxTradMsg(pMsg); + } else if(!strcmp((char*) pName, "fromhost")) { + pRes = getRcvFrom(pMsg); + } else if(!strcmp((char*) pName, "source") || !strcmp((char*) pName, "hostname")) { + pRes = getHOSTNAME(pMsg); + } else if(!strcmp((char*) pName, "syslogtag")) { + pRes = getTAG(pMsg); + } else if(!strcmp((char*) pName, "pri")) { + pRes = getPRI(pMsg); + } else if(!strcmp((char*) pName, "pri-text")) { + pBuf = malloc(20 * sizeof(char)); + if(pBuf == NULL) { + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } else { + *pbMustBeFreed = 1; + pRes = textpri(pBuf, 20, getPRIi(pMsg)); + } + } else if(!strcmp((char*) pName, "iut")) { + pRes = "1"; /* always 1 for syslog messages (a MonitorWare thing;)) */ + } else if(!strcmp((char*) pName, "syslogfacility")) { + pRes = getFacility(pMsg); + } else if(!strcmp((char*) pName, "syslogfacility-text")) { + pRes = getFacilityStr(pMsg); + } else if(!strcmp((char*) pName, "syslogseverity") || !strcmp((char*) pName, "syslogpriority")) { + pRes = getSeverity(pMsg); + } else if(!strcmp((char*) pName, "syslogseverity-text") || !strcmp((char*) pName, "syslogpriority-text")) { + pRes = getSeverityStr(pMsg); + } else if(!strcmp((char*) pName, "timegenerated")) { + pRes = getTimeGenerated(pMsg, pTpe->data.field.eDateFormat); + } else if(!strcmp((char*) pName, "timereported") + || !strcmp((char*) pName, "timestamp")) { + pRes = getTimeReported(pMsg, pTpe->data.field.eDateFormat); + } else if(!strcmp((char*) pName, "programname")) { + pRes = getProgramName(pMsg); + } else if(!strcmp((char*) pName, "protocol-version")) { + pRes = getProtocolVersionString(pMsg); + } else if(!strcmp((char*) pName, "structured-data")) { + pRes = getStructuredData(pMsg); + } else if(!strcmp((char*) pName, "app-name")) { + pRes = getAPPNAME(pMsg); + } else if(!strcmp((char*) pName, "procid")) { + pRes = getPROCID(pMsg); + } else if(!strcmp((char*) pName, "msgid")) { + pRes = getMSGID(pMsg); + /* here start system properties (those, that do not relate to the message itself */ + } else if(!strcmp((char*) pName, "$now")) { + if((pRes = (char*) getNOW(NOW_NOW)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$year")) { + if((pRes = (char*) getNOW(NOW_YEAR)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$month")) { + if((pRes = (char*) getNOW(NOW_MONTH)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$day")) { + if((pRes = (char*) getNOW(NOW_DAY)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$hour")) { + if((pRes = (char*) getNOW(NOW_HOUR)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$hhour")) { + if((pRes = (char*) getNOW(NOW_HHOUR)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$qhour")) { + if((pRes = (char*) getNOW(NOW_QHOUR)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else if(!strcmp((char*) pName, "$minute")) { + if((pRes = (char*) getNOW(NOW_MINUTE)) == NULL) { + return "***OUT OF MEMORY***"; + } else + *pbMustBeFreed = 1; /* all of these functions allocate dyn. memory */ + } else { + /* there is no point in continuing, we may even otherwise render the + * error message unreadable. rgerhards, 2007-07-10 + */ + dbgprintf("invalid property name: '%s'\n", pName); + return "**INVALID PROPERTY NAME**"; + } + + /* If we did not receive a template pointer, we are already done... */ + if(pTpe == NULL) { + return pRes; + } + + /* Now check if we need to make "temporary" transformations (these + * are transformations that do not go back into the message - + * memory must be allocated for them!). + */ + + /* substring extraction */ + /* first we check if we need to extract by field number + * rgerhards, 2005-12-22 + */ + if(pTpe->data.field.has_fields == 1) { + size_t iCurrFld; + char *pFld; + char *pFldEnd; + /* first, skip to the field in question. The field separator + * is always one character and is stored in the template entry. + */ + iCurrFld = 1; + pFld = pRes; + while(*pFld && iCurrFld < pTpe->data.field.iToPos) { + /* skip fields until the requested field or end of string is found */ + while(*pFld && (uchar) *pFld != pTpe->data.field.field_delim) + ++pFld; /* skip to field terminator */ + if(*pFld == pTpe->data.field.field_delim) { + ++pFld; /* eat it */ + ++iCurrFld; + } + } + dbgprintf("field requested %d, field found %d\n", pTpe->data.field.iToPos, (int) iCurrFld); + + if(iCurrFld == pTpe->data.field.iToPos) { + /* field found, now extract it */ + /* first of all, we need to find the end */ + pFldEnd = pFld; + while(*pFldEnd && *pFldEnd != pTpe->data.field.field_delim) + ++pFldEnd; + --pFldEnd; /* we are already at the delimiter - so we need to + * step back a little not to copy it as part of the field. */ + /* we got our end pointer, now do the copy */ + /* TODO: code copied from below, this is a candidate for a separate function */ + iLen = pFldEnd - pFld + 1; /* the +1 is for an actual char, NOT \0! */ + pBufStart = pBuf = malloc((iLen + 1) * sizeof(char)); + if(pBuf == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + /* now copy */ + memcpy(pBuf, pFld, iLen); + pBuf[iLen] = '\0'; /* terminate it */ + if(*pbMustBeFreed == 1) + free(pRes); + pRes = pBufStart; + *pbMustBeFreed = 1; + if(*(pFldEnd+1) != '\0') + ++pFldEnd; /* OK, skip again over delimiter char */ + } else { + /* field not found, return error */ + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**FIELD NOT FOUND**"; + } + } else if(pTpe->data.field.iFromPos != 0 || pTpe->data.field.iToPos != 0) { + /* we need to obtain a private copy */ + int iFrom, iTo; + char *pSb; + iFrom = pTpe->data.field.iFromPos; + iTo = pTpe->data.field.iToPos; + /* need to zero-base to and from (they are 1-based!) */ + if(iFrom > 0) + --iFrom; + if(iTo > 0) + --iTo; + iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */ + pBufStart = pBuf = malloc((iLen + 1) * sizeof(char)); + if(pBuf == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + pSb = pRes; + if(iFrom) { + /* skip to the start of the substring (can't do pointer arithmetic + * because the whole string might be smaller!!) + */ + while(*pSb && iFrom) { + --iFrom; + ++pSb; + } + } + /* OK, we are at the begin - now let's copy... */ + while(*pSb && iLen) { + *pBuf++ = *pSb; + ++pSb; + --iLen; + } + *pBuf = '\0'; + if(*pbMustBeFreed == 1) + free(pRes); + pRes = pBufStart; + *pbMustBeFreed = 1; +#ifdef FEATURE_REGEXP + } else { + /* Check for regular expressions */ + if (pTpe->data.field.has_regex != 0) { + if (pTpe->data.field.has_regex == 2) + /* Could not compile regex before! */ + return "**NO MATCH** **BAD REGULAR EXPRESSION**"; + + dbgprintf("debug: String to match for regex is: %s\n", pRes); + + if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) { + if (0 != regexp.regexec(&pTpe->data.field.re, pRes, nmatch, pmatch, 0)) { + /* we got no match! */ + if (*pbMustBeFreed == 1) { + free(pRes); + *pbMustBeFreed = 0; + } + return "**NO MATCH**"; + } else { + /* Match! */ + /* I need to malloc pB */ + int iLenBuf; + char *pB; + + iLenBuf = pmatch[0].rm_eo - pmatch[0].rm_so; + pB = (char *) malloc((iLenBuf + 1) * sizeof(char)); + + if (pB == NULL) { + if (*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY ALLOCATING pBuf**"; + } + + /* Lets copy the matched substring to the buffer */ + memcpy(pB, pRes + pmatch[0].rm_so, iLenBuf); + pB[iLenBuf] = '\0';/* terminate string, did not happen before */ + + if (*pbMustBeFreed == 1) + free(pRes); + pRes = pB; + *pbMustBeFreed = 1; + } + } else { + /* we could not load regular expression support. This is quite unexpected at + * this stage of processing (after all, the config parser found it), but so + * it is. We return an error in that case. -- rgerhards, 2008-03-07 + */ + dbgprintf("could not get regexp object pointer, so regexp can not be evaluated\n"); + if (*pbMustBeFreed == 1) { + free(pRes); + *pbMustBeFreed = 0; + } + return "***REGEXP NOT AVAILABLE***"; + } + } +#endif /* #ifdef FEATURE_REGEXP */ + } + + if(*pRes) { + /* case conversations (should go after substring, because so we are able to + * work on the smallest possible buffer). + */ + if(pTpe->data.field.eCaseConv != tplCaseConvNo) { + /* we need to obtain a private copy */ + int iBufLen = strlen(pRes); + char *pBStart; + char *pB; + char *pSrc; + pBStart = pB = malloc((iBufLen + 1) * sizeof(char)); + if(pB == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + pSrc = pRes; + while(*pSrc) { + *pB++ = (pTpe->data.field.eCaseConv == tplCaseConvUpper) ? + (char)toupper((int)*pSrc) : (char)tolower((int)*pSrc); + /* currently only these two exist */ + ++pSrc; + } + *pB = '\0'; + if(*pbMustBeFreed == 1) + free(pRes); + pRes = pBStart; + *pbMustBeFreed = 1; + } + + /* now do control character dropping/escaping/replacement + * Only one of these can be used. If multiple options are given, the + * result is random (though currently there obviously is an order of + * preferrence, see code below. But this is NOT guaranteed. + * RGerhards, 2006-11-17 + * We must copy the strings if we modify them, because they may either + * point to static memory or may point into the message object, in which + * case we would actually modify the original property (which of course + * is wrong). + * This was found and fixed by varmojefkoj on 2007-09-11 + */ + if(pTpe->data.field.options.bDropCC) { + int iLenBuf = 0; + char *pSrc = pRes; + char *pDstStart; + char *pDst; + char bDropped = 0; + + while(*pSrc) { + if(!iscntrl((int) *pSrc++)) + iLenBuf++; + else + bDropped = 1; + } + + if(bDropped) { + pDst = pDstStart = malloc(iLenBuf + 1); + if(pDst == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + for(pSrc = pRes; *pSrc; pSrc++) { + if(!iscntrl((int) *pSrc)) + *pDst++ = *pSrc; + } + *pDst = '\0'; + if(*pbMustBeFreed == 1) + free(pRes); + pRes = pDstStart; + *pbMustBeFreed = 1; + } + } else if(pTpe->data.field.options.bSpaceCC) { + char *pSrc; + char *pDstStart; + char *pDst; + + if(*pbMustBeFreed == 1) { + /* in this case, we already work on dynamic + * memory, so there is no need to copy it - we can + * modify it in-place without any harm. This is a + * performance optiomization. + */ + for(pDst = pRes; *pDst; pDst++) { + if(iscntrl((int) *pDst)) + *pDst = ' '; + } + } else { + pDst = pDstStart = malloc(strlen(pRes) + 1); + if(pDst == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + for(pSrc = pRes; *pSrc; pSrc++) { + if(iscntrl((int) *pSrc)) + *pDst++ = ' '; + else + *pDst++ = *pSrc; + } + *pDst = '\0'; + pRes = pDstStart; + *pbMustBeFreed = 1; + } + } else if(pTpe->data.field.options.bEscapeCC) { + /* we must first count how many control charactes are + * present, because we need this to compute the new string + * buffer length. While doing so, we also compute the string + * length. + */ + int iNumCC = 0; + int iLenBuf = 0; + char *pB; + + for(pB = pRes ; *pB ; ++pB) { + ++iLenBuf; + if(iscntrl((int) *pB)) + ++iNumCC; + } + + if(iNumCC > 0) { /* if 0, there is nothing to escape, so we are done */ + /* OK, let's do the escaping... */ + char *pBStart; + char szCCEsc[8]; /* buffer for escape sequence */ + int i; + + iLenBuf += iNumCC * 4; + pBStart = pB = malloc((iLenBuf + 1) * sizeof(char)); + if(pB == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + while(*pRes) { + if(iscntrl((int) *pRes)) { + snprintf(szCCEsc, sizeof(szCCEsc), "#%3.3d", *pRes); + for(i = 0 ; i < 4 ; ++i) + *pB++ = szCCEsc[i]; + } else { + *pB++ = *pRes; + } + ++pRes; + } + *pB = '\0'; + if(*pbMustBeFreed == 1) + free(pRes); + pRes = pBStart; + *pbMustBeFreed = 1; + } + } + } + + /* Take care of spurious characters to make the property safe + * for a path definition + */ + if(pTpe->data.field.options.bSecPathDrop || pTpe->data.field.options.bSecPathReplace) { + if(pTpe->data.field.options.bSecPathDrop) { + int iLenBuf = 0; + char *pSrc = pRes; + char *pDstStart; + char *pDst; + char bDropped = 0; + + while(*pSrc) { + if(*pSrc++ != '/') + iLenBuf++; + else + bDropped = 1; + } + + if(bDropped) { + pDst = pDstStart = malloc(iLenBuf + 1); + if(pDst == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + for(pSrc = pRes; *pSrc; pSrc++) { + if(*pSrc != '/') + *pDst++ = *pSrc; + } + *pDst = '\0'; + if(*pbMustBeFreed == 1) + free(pRes); + pRes = pDstStart; + *pbMustBeFreed = 1; + } + } else { + char *pSrc; + char *pDstStart; + char *pDst; + + if(*pbMustBeFreed == 1) { + /* here, again, we can modify the string as we already obtained + * a private buffer. As we do not change the size of that buffer, + * in-place modification is possible. This is a performance + * enhancement. + */ + for(pDst = pRes; *pDst; pDst++) { + if(*pDst == '/') + *pDst++ = '_'; + } + } else { + pDst = pDstStart = malloc(strlen(pRes) + 1); + if(pDst == NULL) { + if(*pbMustBeFreed == 1) + free(pRes); + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + for(pSrc = pRes; *pSrc; pSrc++) { + if(*pSrc == '/') + *pDst++ = '_'; + else + *pDst++ = *pSrc; + } + *pDst = '\0'; + /* we must NOT check if it needs to be freed, because we have done + * this in the if above. So if we come to hear, the pSrc string needs + * not to be freed (and we do not need to care about it). + */ + pRes = pDstStart; + *pbMustBeFreed = 1; + } + } + + /* check for "." and ".." (note the parenthesis in the if condition!) */ + if((*pRes == '.') && (*(pRes + 1) == '\0' || (*(pRes + 1) == '.' && *(pRes + 2) == '\0'))) { + char *pTmp = pRes; + + if(*(pRes + 1) == '\0') + pRes = "_"; + else + pRes = "_.";; + if(*pbMustBeFreed == 1) + free(pTmp); + *pbMustBeFreed = 0; + } else if(*pRes == '\0') { + if(*pbMustBeFreed == 1) + free(pRes); + pRes = "_"; + *pbMustBeFreed = 0; + } + } + + /* Now drop last LF if present (pls note that this must not be done + * if bEscapeCC was set! + */ + if(pTpe->data.field.options.bDropLastLF && !pTpe->data.field.options.bEscapeCC) { + int iLn = strlen(pRes); + char *pB; + if(iLn > 0 && *(pRes + iLn - 1) == '\n') { + /* we have a LF! */ + /* check if we need to obtain a private copy */ + if(*pbMustBeFreed == 0) { + /* ok, original copy, need a private one */ + pB = malloc((iLn + 1) * sizeof(char)); + if(pB == NULL) { + *pbMustBeFreed = 0; + return "**OUT OF MEMORY**"; + } + memcpy(pB, pRes, iLn - 1); + pRes = pB; + *pbMustBeFreed = 1; + } + *(pRes + iLn - 1) = '\0'; /* drop LF ;) */ + } + } + + /*dbgprintf("MsgGetProp(\"%s\"): \"%s\"\n", pName, pRes); only for verbose debug logging */ + return(pRes); +} + + +/* The returns a message variable suitable for use with RainerScript. Most importantly, this means + * that the value is returned in a var_t object. The var_t is constructed inside this function and + * MUST be freed by the caller. + * rgerhards, 2008-02-25 + */ +rsRetVal +msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar) +{ + DEFiRet; + var_t *pVar; + uchar *pszProp = NULL; + cstr_t *pstrProp; + unsigned short bMustBeFreed = 0; + + ISOBJ_TYPE_assert(pThis, msg); + ASSERT(pstrPropName != NULL); + ASSERT(ppVar != NULL); + + /* make sure we have a var_t instance */ + CHKiRet(var.Construct(&pVar)); + CHKiRet(var.ConstructFinalize(pVar)); + + /* always call MsgGetProp() without a template specifier */ + pszProp = (uchar*) MsgGetProp(pThis, NULL, pstrPropName, &bMustBeFreed); + + /* now create a string object out of it and hand that over to the var */ + CHKiRet(rsCStrConstructFromszStr(&pstrProp, pszProp)); + CHKiRet(var.SetString(pVar, pstrProp)); + + /* finally store var */ + *ppVar = pVar; + +finalize_it: + if(bMustBeFreed) + free(pszProp); + + RETiRet; +} + + +/* This function can be used as a generic way to set properties. + * We have to handle a lot of legacy, so our return value is not always + * 100% correct (called functions do not always provide one, should + * change over time). + * rgerhards, 2008-01-07 + */ +#define isProp(name) !rsCStrSzStrCmp(pProp->pcsName, (uchar*) name, sizeof(name) - 1) +rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pThis, msg); + assert(pProp != NULL); + + if(isProp("iProtocolVersion")) { + setProtocolVersion(pThis, pProp->val.num); + } else if(isProp("iSeverity")) { + pThis->iSeverity = pProp->val.num; + } else if(isProp("iFacility")) { + pThis->iFacility = pProp->val.num; + } else if(isProp("msgFlags")) { + pThis->msgFlags = pProp->val.num; + } else if(isProp("pszRawMsg")) { + MsgSetRawMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszMSG")) { + MsgSetMSG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszUxTradMsg")) { + MsgSetUxTradMsg(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszTAG")) { + MsgSetTAG(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszRcvFrom")) { + MsgSetHOSTNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pszHOSTNAME")) { + MsgSetRcvFrom(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pCSStrucData")) { + MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pCSAPPNAME")) { + MsgSetAPPNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pCSPROCID")) { + MsgSetPROCID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("pCSMSGID")) { + MsgSetMSGID(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); + } else if(isProp("tRcvdAt")) { + memcpy(&pThis->tRcvdAt, &pProp->val.vSyslogTime, sizeof(struct syslogTime)); + } else if(isProp("tTIMESTAMP")) { + memcpy(&pThis->tTIMESTAMP, &pProp->val.vSyslogTime, sizeof(struct syslogTime)); + } + + RETiRet; +} +#undef isProp + + +/* This is a construction finalizer that must be called after all properties + * have been set. It does some final work on the message object. After this + * is done, the object is considered ready for full processing. + * rgerhards, 2008-07-08 + */ +static rsRetVal msgConstructFinalizer(msg_t *pThis) +{ + MsgPrepareEnqueue(pThis); + return RS_RET_OK; +} + + +/* get the severity - this is an entry point that + * satisfies the base object class getSeverity semantics. + * rgerhards, 2008-01-14 + */ +static rsRetVal +MsgGetSeverity(obj_t *pThis, int *piSeverity) +{ + ISOBJ_TYPE_assert(pThis, msg); + assert(piSeverity != NULL); + *piSeverity = ((msg_t*) pThis)->iSeverity; + return RS_RET_OK; +} + + +/* dummy */ +rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } + +/* Initialize the message class. Must be called as the very first method + * before anything else is called inside this class. + * rgerhards, 2008-01-04 + */ +BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE) + /* request objects we use */ + CHKiRet(objUse(var, CORE_COMPONENT)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + + /* set our own handlers */ + OBJSetMethodHandler(objMethod_SERIALIZE, MsgSerialize); + OBJSetMethodHandler(objMethod_SETPROPERTY, MsgSetProperty); + OBJSetMethodHandler(objMethod_CONSTRUCTION_FINALIZER, msgConstructFinalizer); + OBJSetMethodHandler(objMethod_GETSEVERITY, MsgGetSeverity); + /* initially, we have no need to lock message objects */ + funcLock = MsgLockingDummy; + funcUnlock = MsgLockingDummy; + funcDeleteMutex = MsgLockingDummy; + funcMsgPrepareEnqueue = MsgLockingDummy; +ENDObjClassInit(msg) + +/* + * vi:set ai: + */ diff --git a/runtime/msg.h b/runtime/msg.h new file mode 100644 index 00000000..56ce56bb --- /dev/null +++ b/runtime/msg.h @@ -0,0 +1,178 @@ +/* msg.h + * Header file for all msg-related functions. + * + * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "template.h" /* this is a quirk, but these two are too interdependant... */ + +#ifndef MSG_H_INCLUDED +#define MSG_H_INCLUDED 1 + +#include +#include "obj.h" +#include "syslogd-types.h" +#include "template.h" + +/* rgerhards 2004-11-08: The following structure represents a + * syslog message. + * + * Important Note: + * The message object is used for multiple purposes (once it + * has been created). Once created, it actully is a read-only + * object (though we do not specifically express this). In order + * to avoid multiple copies of the same object, we use a + * reference counter. This counter is set to 1 by the constructer + * and increased by 1 with a call to MsgAddRef(). The destructor + * checks the reference count. If it is more than 1, only the counter + * will be decremented. If it is 1, however, the object is actually + * destroyed. To make this work, it is vital that MsgAddRef() is + * called each time a "copy" is stored somewhere. + */ +struct msg { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + pthread_mutexattr_t mutAttr; + pthread_mutex_t mut; + int iRefCount; /* reference counter (0 = unused) */ + short bParseHOSTNAME; /* should the hostname be parsed from the message? */ + /* background: the hostname is not present on "regular" messages + * received via UNIX domain sockets from the same machine. However, + * it is available when we have a forwarder (e.g. rfc3195d) using local + * sockets. All in all, the parser would need parse templates, that would + * resolve all these issues... rgerhards, 2005-10-06 + */ + flowControl_t flowCtlType; /**< type of flow control we can apply, for enqueueing, needs not to be persisted because + once data has entered the queue, this property is no longer needed. */ + short iSeverity; /* the severity 0..7 */ + uchar *pszSeverity; /* severity as string... */ + int iLenSeverity; /* ... and its length. */ + uchar *pszSeverityStr; /* severity name... */ + int iLenSeverityStr; /* ... and its length. */ + short iFacility; /* Facility code 0 .. 23*/ + uchar *pszFacility; /* Facility as string... */ + int iLenFacility; /* ... and its length. */ + uchar *pszFacilityStr; /* facility name... */ + int iLenFacilityStr; /* ... and its length. */ + uchar *pszPRI; /* the PRI as a string */ + int iLenPRI; /* and its length */ + uchar *pszRawMsg; /* message as it was received on the + * wire. This is important in case we + * need to preserve cryptographic verifiers. + */ + int iLenRawMsg; /* length of raw message */ + uchar *pszMSG; /* the MSG part itself */ + int iLenMSG; /* Length of the MSG part */ + uchar *pszUxTradMsg; /* the traditional UNIX message */ + int iLenUxTradMsg;/* Length of the traditional UNIX message */ + uchar *pszTAG; /* pointer to tag value */ + int iLenTAG; /* Length of the TAG part */ + uchar *pszHOSTNAME; /* HOSTNAME from syslog message */ + int iLenHOSTNAME; /* Length of HOSTNAME */ + uchar *pszRcvFrom; /* System message was received from */ + int iLenRcvFrom; /* Length of pszRcvFrom */ + short iProtocolVersion;/* protocol version of message received 0 - legacy, 1 syslog-protocol) */ + cstr_t *pCSProgName; /* the (BSD) program name */ + cstr_t *pCSStrucData;/* STRUCTURED-DATA */ + cstr_t *pCSAPPNAME; /* APP-NAME */ + cstr_t *pCSPROCID; /* PROCID */ + cstr_t *pCSMSGID; /* MSGID */ + struct syslogTime tRcvdAt;/* time the message entered this program */ + char *pszRcvdAt3164; /* time as RFC3164 formatted string (always 15 charcters) */ + char *pszRcvdAt3339; /* time as RFC3164 formatted string (32 charcters at most) */ + char *pszRcvdAt_MySQL; /* rcvdAt as MySQL formatted string (always 14 charcters) */ + char *pszRcvdAt_PgSQL; /* rcvdAt as PgSQL formatted string (always 21 characters) */ + struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */ + char *pszTIMESTAMP3164; /* TIMESTAMP as RFC3164 formatted string (always 15 charcters) */ + char *pszTIMESTAMP3339; /* TIMESTAMP as RFC3339 formatted string (32 charcters at most) */ + char *pszTIMESTAMP_MySQL;/* TIMESTAMP as MySQL formatted string (always 14 charcters) */ + char *pszTIMESTAMP_PgSQL;/* TIMESTAMP as PgSQL formatted string (always 21 characters) */ + int msgFlags; /* flags associated with this message */ +}; +typedef struct msg msg_t; /* new name */ + +/* function prototypes + */ +PROTOTYPEObjClassInit(msg); +char* getProgramName(msg_t*); +rsRetVal msgConstruct(msg_t **ppThis); +rsRetVal msgDestruct(msg_t **ppM); +msg_t* MsgDup(msg_t* pOld); +msg_t *MsgAddRef(msg_t *pM); +void setProtocolVersion(msg_t *pM, int iNewVersion); +int getProtocolVersion(msg_t *pM); +char *getProtocolVersionString(msg_t *pM); +int getMSGLen(msg_t *pM); +char *getRawMsg(msg_t *pM); +char *getUxTradMsg(msg_t *pM); +char *getMSG(msg_t *pM); +char *getPRI(msg_t *pM); +int getPRIi(msg_t *pM); +char *getTimeReported(msg_t *pM, enum tplFormatTypes eFmt); +char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt); +char *getSeverity(msg_t *pM); +char *getSeverityStr(msg_t *pM); +char *getFacility(msg_t *pM); +char *getFacilityStr(msg_t *pM); +rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME); +char *getAPPNAME(msg_t *pM); +rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID); +int getPROCIDLen(msg_t *pM); +char *getPROCID(msg_t *pM); +rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID); +void MsgAssignTAG(msg_t *pMsg, uchar *pBuf); +void MsgSetTAG(msg_t *pMsg, char* pszTAG); +rsRetVal MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl); +char *getTAG(msg_t *pM); +int getHOSTNAMELen(msg_t *pM); +char *getHOSTNAME(msg_t *pM); +char *getRcvFrom(msg_t *pM); +rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData); +char *getStructuredData(msg_t *pM); +int getProgramNameLen(msg_t *pM); +char *getProgramName(msg_t *pM); +void MsgSetRcvFrom(msg_t *pMsg, char* pszRcvFrom); +void MsgAssignHOSTNAME(msg_t *pMsg, char *pBuf); +void MsgSetHOSTNAME(msg_t *pMsg, char* pszHOSTNAME); +int MsgSetUxTradMsg(msg_t *pMsg, char* pszUxTradMsg); +void MsgSetMSG(msg_t *pMsg, char* pszMSG); +void MsgSetRawMsg(msg_t *pMsg, char* pszRawMsg); +void moveHOSTNAMEtoTAG(msg_t *pM); +char *getMSGID(msg_t *pM); +char *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, + cstr_t *pCSPropName, unsigned short *pbMustBeFreed); +char *textpri(char *pRes, size_t pResLen, int pri); +rsRetVal msgGetMsgVar(msg_t *pThis, cstr_t *pstrPropName, var_t **ppVar); +rsRetVal MsgEnableThreadSafety(void); + +/* The MsgPrepareEnqueue() function is a macro for performance reasons. + * It needs one global variable to work. This is acceptable, as it gains + * us quite some performance and is fully abstracted using this header file. + * The important thing is that no other module is permitted to actually + * access that global variable! -- rgerhards, 2008-01-05 + */ +extern void (*funcMsgPrepareEnqueue)(msg_t *pMsg); +#define MsgPrepareEnqueue(pMsg) funcMsgPrepareEnqueue(pMsg) + +#endif /* #ifndef MSG_H_INCLUDED */ +/* + * vi:set ai: + */ diff --git a/runtime/obj-types.h b/runtime/obj-types.h new file mode 100644 index 00000000..901733c5 --- /dev/null +++ b/runtime/obj-types.h @@ -0,0 +1,406 @@ +/* Some type definitions and macros for the obj object. + * I needed to move them out of the main obj.h, because obj.h's + * prototypes use other data types. However, their .h's rely + * on some of the obj.h data types and macros. So I needed to break + * that loop somehow and I've done that by moving the typedefs + * into this file here. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef OBJ_TYPES_H_INCLUDED +#define OBJ_TYPES_H_INCLUDED + +#include "stringbuf.h" +#include "syslogd-types.h" + +/* property types for obj[De]Serialize() */ +typedef enum { + PROPTYPE_NONE = 0, /* currently no value set */ + PROPTYPE_PSZ = 1, + PROPTYPE_SHORT = 2, + PROPTYPE_INT = 3, + PROPTYPE_LONG = 4, + PROPTYPE_INT64 = 5, + PROPTYPE_CSTR = 6, + PROPTYPE_SYSLOGTIME = 7 +} propType_t; + +typedef unsigned objID_t; + +typedef enum { /* IDs of base methods supported by all objects - used for jump table, so + * they must start at zero and be incremented. -- rgerhards, 2008-01-04 + */ + objMethod_CONSTRUCT = 0, + objMethod_DESTRUCT = 1, + objMethod_SERIALIZE = 2, + objMethod_DESERIALIZE = 3, + objMethod_SETPROPERTY = 4, + objMethod_CONSTRUCTION_FINALIZER = 5, + objMethod_GETSEVERITY = 6, + objMethod_DEBUGPRINT = 7 +} objMethod_t; +#define OBJ_NUM_METHODS 8 /* must be updated to contain the max number of methods supported */ + + +/* the base data type for interfaces + * This MUST be in sync with the ifBEGIN macro + */ +typedef struct interface_s { + int ifVersion; /* must be set to version requested */ + int ifIsLoaded; /* is the interface loaded? (0-no, 1-yes, 2-load failed; if not 1, functions can NOT be called! */ +} interface_t; + + +typedef struct objInfo_s { + uchar *pszID; /* the object ID as a string */ + size_t lenID; /* length of the ID string */ + int iObjVers; + uchar *pszName; + rsRetVal (*objMethods[OBJ_NUM_METHODS])(); + rsRetVal (*QueryIF)(interface_t*); + struct modInfo_s *pModInfo; +} objInfo_t; + + +typedef struct obj { /* the dummy struct that each derived class can be casted to */ + objInfo_t *pObjInfo; +#ifndef NDEBUG /* this means if debug... */ + unsigned int iObjCooCKiE; /* must always be 0xBADEFEE for a valid object */ +#endif + uchar *pszName; /* the name of *this* specific object instance */ +} obj_t; + + +/* macros which must be gloablly-visible (because they are used during definition of + * other objects. + */ +#ifndef NDEBUG /* this means if debug... */ +#include +# define BEGINobjInstance \ + obj_t objData +# define ISOBJ_assert(pObj) \ + do { \ + ASSERT((pObj) != NULL); \ + ASSERT((unsigned) ((obj_t*)(pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \ + } while(0); +# define ISOBJ_TYPE_assert(pObj, objType) \ + do { \ + ASSERT(pObj != NULL); \ + ASSERT((unsigned) ((obj_t*) (pObj))->iObjCooCKiE == (unsigned) 0xBADEFEE); \ + ASSERT(!strcmp((char*)(((obj_t*)pObj)->pObjInfo->pszID), #objType)); \ + } while(0); +#else /* non-debug mode, no checks but much faster */ +# define BEGINobjInstance obj_t objData +# define ISOBJ_TYPE_assert(pObj, objType) +# define ISOBJ_assert(pObj) +#endif + +#define DEFpropSetMethPTR(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType *pVal)\ + { \ + /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ + pThis->prop = pVal; \ + return RS_RET_OK; \ + } +#define PROTOTYPEpropSetMethPTR(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType*) +#define DEFpropSetMeth(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType pVal)\ + { \ + /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ + pThis->prop = pVal; \ + return RS_RET_OK; \ + } +#define DEFpropSetMethFP(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType)\ + { \ + /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ + pThis->prop = pVal; \ + return RS_RET_OK; \ + } +#define PROTOTYPEpropSetMethFP(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType) +#define DEFpropSetMeth(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType pVal)\ + { \ + /* DEV debug: dbgprintf("%sSet%s()\n", #obj, #prop); */\ + pThis->prop = pVal; \ + return RS_RET_OK; \ + } +#define PROTOTYPEpropSetMeth(obj, prop, dataType)\ + rsRetVal obj##Set##prop(obj##_t *pThis, dataType pVal) +#define INTERFACEpropSetMeth(obj, prop, dataType)\ + rsRetVal (*Set##prop)(obj##_t *pThis, dataType) +/* class initializer */ +#define PROTOTYPEObjClassInit(objName) rsRetVal objName##ClassInit(struct modInfo_s*) +/* below: objName must be the object name (e.g. vm, strm, ...) and ISCORE must be + * 1 if the module is a statically linked core module and 0 if it is a + * dynamically loaded one. -- rgerhards, 2008-02-29 + */ +#define OBJ_IS_CORE_MODULE 1 /* This should better be renamed to something like "OBJ_IS_NOT_LIBHEAD" or so... ;) */ +#define OBJ_IS_LOADABLE_MODULE 0 +#define BEGINObjClassInit(objName, objVers, objType) \ +rsRetVal objName##ClassInit(struct modInfo_s *pModInfo) \ +{ \ + DEFiRet; \ + if(objType == OBJ_IS_CORE_MODULE) { /* are we a core module? */ \ + CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ \ + } \ + CHKiRet(obj.InfoConstruct(&pObjInfoOBJ, (uchar*) #objName, objVers, \ + (rsRetVal (*)(void*))objName##Construct,\ + (rsRetVal (*)(void*))objName##Destruct,\ + (rsRetVal (*)(interface_t*))objName##QueryInterface, pModInfo)); \ + +#define ENDObjClassInit(objName) \ + iRet = obj.RegisterObj((uchar*)#objName, pObjInfoOBJ); \ +finalize_it: \ + RETiRet; \ +} + +/* ... and now the same for abstract classes. + * TODO: consolidate the two -- rgerhards, 2008-02-29 + */ +#define BEGINAbstractObjClassInit(objName, objVers, objType) \ +rsRetVal objName##ClassInit(struct modInfo_s *pModInfo) \ +{ \ + DEFiRet; \ + if(objType == OBJ_IS_CORE_MODULE) { /* are we a core module? */ \ + CHKiRet(objGetObjInterface(&obj)); /* this provides the root pointer for all other queries */ \ + } \ + CHKiRet(obj.InfoConstruct(&pObjInfoOBJ, (uchar*) #objName, objVers, \ + NULL,\ + NULL,\ + (rsRetVal (*)(interface_t*))objName##QueryInterface, pModInfo)); + +#define ENDObjClassInit(objName) \ + iRet = obj.RegisterObj((uchar*)#objName, pObjInfoOBJ); \ +finalize_it: \ + RETiRet; \ +} + + +/* now come the class exit. This is to be called immediately before the class is + * unloaded (actual unload for plugins, program termination for core modules) + * gerhards, 2008-03-10 + */ +#define PROTOTYPEObjClassExit(objName) rsRetVal objName##ClassExit(void) +#define BEGINObjClassExit(objName, objType) \ +rsRetVal objName##ClassExit(void) \ +{ \ + DEFiRet; + +#define CODESTARTObjClassExit(objName) + +#define ENDObjClassExit(objName) \ + iRet = obj.UnregisterObj((uchar*)#objName); \ + RETiRet; \ +} + +/* this defines both the constructor and initializer + * rgerhards, 2008-01-10 + */ +#define BEGINobjConstruct(obj) \ + rsRetVal obj##Initialize(obj##_t __attribute__((unused)) *pThis) \ + { \ + DEFiRet; + +#define ENDobjConstruct(obj) \ + /* use finalize_it: before calling the macro (if you need it)! */ \ + RETiRet; \ + } \ + rsRetVal obj##Construct(obj##_t **ppThis) \ + { \ + DEFiRet; \ + obj##_t *pThis; \ + \ + ASSERT(ppThis != NULL); \ + \ + if((pThis = (obj##_t *)calloc(1, sizeof(obj##_t))) == NULL) { \ + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); \ + } \ + objConstructSetObjInfo(pThis); \ + \ + obj##Initialize(pThis); \ + \ + finalize_it: \ + OBJCONSTRUCT_CHECK_SUCCESS_AND_CLEANUP \ + RETiRet; \ + } + + +/* this defines the destructor. The important point is that the base object + * destructor is called. The upper-level class shall destruct all of its + * properties, but not the instance itself. This is freed here by the + * framework (we need an intact pointer because we need to free the + * obj_t structures inside it). A pointer to the object pointer must be + * parse, because it is re-set to NULL (this, for example, is important in + * cancellation handlers). The object pointer is always named pThis. + * The object is always freed, even if there is some error while + * Cancellation is blocked during destructors, as this could have fatal + * side-effects. However, this also means the upper-level object should + * not perform any lenghty processing. + * IMPORTANT: if the upper level object requires some situations where the + * object shall not be destructed (e.g. via reference counting), then + * it shall set pThis to NULL, which prevents destruction of the + * object. + * processing. + * rgerhards, 2008-01-30 + */ +#define BEGINobjDestruct(OBJ) \ + rsRetVal OBJ##Destruct(OBJ##_t **ppThis) \ + { \ + DEFiRet; \ + int iCancelStateSave; \ + OBJ##_t *pThis; + +#define CODESTARTobjDestruct(OBJ) \ + ASSERT(ppThis != NULL); \ + pThis = *ppThis; \ + ISOBJ_TYPE_assert(pThis, OBJ); \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); + +#define ENDobjDestruct(OBJ) \ + goto finalize_it; /* prevent compiler warning ;) */ \ + /* no more code here! */ \ + finalize_it: \ + if(pThis != NULL) { \ + obj.DestructObjSelf((obj_t*) pThis); \ + free(pThis); \ + *ppThis = NULL; \ + } \ + pthread_setcancelstate(iCancelStateSave, NULL); \ + RETiRet; \ + } + + +/* this defines the debug print entry point. DebugPrint is optional. If + * it is provided, the object should output some meaningful information + * via the debug system. + * rgerhards, 2008-02-20 + */ +#define PROTOTYPEObjDebugPrint(obj) rsRetVal obj##DebugPrint(obj##_t *pThis) +#define INTERFACEObjDebugPrint(obj) rsRetVal (*DebugPrint)(obj##_t *pThis) +#define BEGINobjDebugPrint(obj) \ + rsRetVal obj##DebugPrint(obj##_t *pThis) \ + { \ + DEFiRet; \ + +#define CODESTARTobjDebugPrint(obj) \ + ASSERT(pThis != NULL); \ + ISOBJ_TYPE_assert(pThis, obj); \ + +#define ENDobjDebugPrint(obj) \ + RETiRet; \ + } + +/* ------------------------------ object loader system ------------------------------ * + * The following code is the early beginning of a dynamic object loader system. The + * root idea is that all objects will become dynamically loadable libraries over time, + * which is necessary to get a clean plug-in interface where every plugin can access + * rsyslog's rich object model via simple and quite portable methods. + * + * To do so, each object defines one or more interfaces. They are essentially structures + * with function (method) pointers. Anyone interested in calling an object must first + * obtain the interface and can then call through it. + * + * The interface data type must always be called _if_t, as this is expected + * by the macros. Having consitent naming is also easier for the programmer. By default, + * macros create a static variable named like the object in each calling objects + * static data block. + * + * To facilitate moving to this system, I begin to implement some hooks, which + * allows to use interfaces today (when the rest of the infrastructure is not yet + * there). This is in the hope that it will ease migration to the full-fledged system + * once we are ready to work on that. + * rgerhards, 2008-02-21 + */ + +/* this defines the QueryInterface print entry point. Over time, it should be + * present in all objects. + */ +//#define PROTOTYPEObjQueryInterface(obj) rsRetVal obj##QueryInterface(obj##_if_t *pThis) +#define BEGINobjQueryInterface(obj) \ + rsRetVal obj##QueryInterface(obj##_if_t *pIf) \ + { \ + DEFiRet; \ + +#define CODESTARTobjQueryInterface(obj) \ + ASSERT(pIf != NULL); + +#define ENDobjQueryInterface(obj) \ + RETiRet; \ + } + + +/* the following macros should be used to define interfaces inside the + * header files. + */ +#define BEGINinterface(obj) \ + typedef struct obj##_if_s {\ + ifBEGIN; /* This MUST always be the first interface member */ +#define ENDinterface(obj) \ + } obj##_if_t; + +/* the following macro is used to get access to an object (not an instance, + * just the class itself!). It must be called before any of the object's + * methods can be accessed. The MYLIB part is the name of my library, or NULL if + * the caller is a core module. Using the right value here is important to get + * the reference counting correct (object accesses from the same library must + * not be counted because that would cause a library plugin to never unload, as + * its ClassExit() entry points are only called if no object is referenced, which + * would never happen as the library references itself. + * rgerhards, 2008-03-11 + */ +#define CORE_COMPONENT NULL /* use this to indicate this is a core component */ +#define DONT_LOAD_LIB NULL /* do not load a library to obtain object interface (currently same as CORE_COMPONENT) */ +/*#define objUse(objName, MYLIB, FILENAME) \ + obj.UseObj(__FILE__, (uchar*)#objName, MYLIB, (uchar*)FILENAME, (void*) &objName) +*/ +#define objUse(objName, FILENAME) \ + obj.UseObj(__FILE__, (uchar*)#objName, (uchar*)FILENAME, (void*) &objName) +#define objRelease(objName, FILENAME) \ + obj.ReleaseObj(__FILE__, (uchar*)#objName, (uchar*) FILENAME, (void*) &objName) + +/* defines data that must always be present at the very begin of the interface structure */ +#define ifBEGIN \ + int ifVersion; /* must be set to version requested */ \ + int ifIsLoaded; /* is the interface loaded? (0-no, 1-yes; if no, functions can NOT be called! */ + + +/* use the following define some place in your static data (suggested right at + * the beginning + */ +#define DEFobjCurrIf(obj) \ + static obj##_if_t obj = { .ifVersion = obj##CURR_IF_VERSION, .ifIsLoaded = 0 }; + +/* define the prototypes for a class - when we use interfaces, we just have few + * functions that actually need to be non-static. + */ +#define PROTOTYPEObj(obj) \ + PROTOTYPEObjClassInit(obj); \ + PROTOTYPEObjClassExit(obj); + +/* ------------------------------ end object loader system ------------------------------ */ + + +#include "modules.h" +#endif /* #ifndef OBJ_TYPES_H_INCLUDED */ diff --git a/runtime/obj.c b/runtime/obj.c new file mode 100644 index 00000000..8f2f99e3 --- /dev/null +++ b/runtime/obj.c @@ -0,0 +1,1336 @@ +/* obj.c + * + * This file implements a generic object "class". All other classes can + * use the service of this base class here to include auto-destruction and + * other capabilities in a generic manner. + * + * As of 2008-02-29, I (rgerhards) am adding support for dynamically loadable + * objects. In essence, each object will soon be available via its interface, + * only. Before any object's code is accessed (including global static methods), + * the caller needs to obtain an object interface. To do so, it needs to provide + * the object name and the file where the object is expected to reside in. A + * file may not be given, in which case the object is expected to reside in + * the rsyslog core. The caller than receives an interface pointer which can + * be utilized to access all the object's methods. This method enables rsyslog + * to load library modules on demand. In order to keep overhead low, callers + * should request object interface only once in the object Init function and + * free them when they exit. The only exception is when a caller needs to + * access an object only conditional, in which case a pointer to its interface + * shall be aquired as need first arises but still be released only on exit + * or when there definitely is no further need. The whole idea is to limit + * the very performance-intense act of dynamically loading an objects library. + * Of course, it is possible to violate this suggestion, but than you should + * have very good reasoning to do so. + * + * Please note that there is one trick we need to do. Each object queries + * the object interfaces and it does so via objUse(). objUse, however, is + * part of the obj object's interface (implemented via the file you are + * just reading). So in order to obtain a pointer to objUse, we need to + * call it - obviously not possible. One solution would be that objUse is + * hardcoded into all callers. That, however, would bring us into slight + * trouble with actually dynamically loaded modules, as we should NOT + * rely on the OS loader to resolve symbols back to the caller (this + * is a feature not universally available and highly importable). Of course, + * we can solve this with a pHostQueryEtryPoint() call. It still sounds + * somewhat unnatural to call a regular interface function via a special + * method. So what we do instead is define a special function called + * objGetObjInterface() which delivers our own interface. That function + * than will be defined global and be queriable via pHostQueryEtryPoint(). + * I agree, technically this is much the same, but from an architecture + * point of view it looks cleaner (at least to me). + * + * Please note that there is another egg-hen problem: we use a linked list, + * which is provided by the linkedList object. However, we need to + * initialize the linked list before we can provide the UseObj() + * functionality. That, in turn, would probably be required by the + * linkedList object. So the solution is to use a backdoor just to + * init the linked list and from then on use the usual interfaces. + * + * File begun on 2008-01-04 by RGerhards + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include +#include +#include +#include +#include + +/* how many objects are supported by rsyslogd? */ +#define OBJ_NUM_IDS 100 /* TODO change to a linked list? info: 16 were currently in use 2008-02-29 */ + +#include "rsyslog.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "obj.h" +#include "stream.h" +#include "modules.h" +#include "errmsg.h" +#include "cfsysline.h" + +/* static data */ +DEFobjCurrIf(obj) /* we define our own interface, as this is expected by some macros! */ +DEFobjCurrIf(var) +DEFobjCurrIf(module) +DEFobjCurrIf(errmsg) +static objInfo_t *arrObjInfo[OBJ_NUM_IDS]; /* array with object information pointers */ + + +/* cookies for serialized lines */ +#define COOKIE_OBJLINE '<' +#define COOKIE_PROPLINE '+' +#define COOKIE_ENDLINE '>' +#define COOKIE_BLANKLINE '.' + +/* forward definitions */ +static rsRetVal FindObjInfo(cstr_t *pszObjName, objInfo_t **ppInfo); + +/* methods */ + +/* This is a dummy method to be used when a standard method has not been + * implemented by an object. Having it allows us to simply call via the + * jump table without any NULL pointer checks - which gains quite + * some performance. -- rgerhards, 2008-01-04 + */ +static rsRetVal objInfoNotImplementedDummy(void __attribute__((unused)) *pThis) +{ + return RS_RET_NOT_IMPLEMENTED; +} + +/* and now the macro to check if something is not implemented + * must be provided an objInfo_t pointer. + */ +#define objInfoIsImplemented(pThis, method) \ + (pThis->objMethods[method] != objInfoNotImplementedDummy) + +/* construct an object Info object. Each class shall do this on init. The + * resulting object shall be cached during the lifetime of the class and each + * object shall receive a reference. A constructor and destructor MUST be provided for all + * objects, thus they are in the parameter list. + * pszID is the identifying object name and must point to constant pool memory. It is never freed. + */ +static rsRetVal +InfoConstruct(objInfo_t **ppThis, uchar *pszID, int iObjVers, + rsRetVal (*pConstruct)(void *), rsRetVal (*pDestruct)(void *), + rsRetVal (*pQueryIF)(interface_t*), modInfo_t *pModInfo) +{ + DEFiRet; + int i; + objInfo_t *pThis; + + assert(ppThis != NULL); + + if((pThis = calloc(1, sizeof(objInfo_t))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + pThis->pszID = pszID; + pThis->lenID = strlen((char*)pszID); + pThis->pszName = (uchar*)strdup((char*)pszID); /* it's OK if we have NULL ptr, GetName() will deal with that! */ + pThis->iObjVers = iObjVers; + pThis->QueryIF = pQueryIF; + pThis->pModInfo = pModInfo; + + pThis->objMethods[0] = pConstruct; + pThis->objMethods[1] = pDestruct; + for(i = 2 ; i < OBJ_NUM_METHODS ; ++i) { + pThis->objMethods[i] = objInfoNotImplementedDummy; + } + + *ppThis = pThis; + +finalize_it: + RETiRet; +} + + +/* destruct the objInfo object - must be done only when no more instances exist. + * rgerhards, 2008-03-10 + */ +static rsRetVal +InfoDestruct(objInfo_t **ppThis) +{ + DEFiRet; + objInfo_t *pThis; + + assert(ppThis != NULL); + pThis = *ppThis; + assert(pThis != NULL); + + if(pThis->pszName != NULL) + free(pThis->pszName); + free(pThis); + *ppThis = NULL; + + RETiRet; +} + + +/* set a method handler */ +static rsRetVal +InfoSetMethod(objInfo_t *pThis, objMethod_t objMethod, rsRetVal (*pHandler)(void*)) +{ + assert(pThis != NULL); + assert(objMethod > 0 && objMethod < OBJ_NUM_METHODS); + pThis->objMethods[objMethod] = pHandler; + + return RS_RET_OK; +} + +/* destruct the base object properties. + * rgerhards, 2008-01-29 + */ +static rsRetVal +DestructObjSelf(obj_t *pThis) +{ + DEFiRet; + + ISOBJ_assert(pThis); + if(pThis->pszName != NULL) { + free(pThis->pszName); + } + + RETiRet; +} + + +/* --------------- object serializiation / deserialization support --------------- */ + + +/* serialize the header of an object + * pszRecType must be either "Obj" (Object) or "OPB" (Object Property Bag) + */ +static rsRetVal objSerializeHeader(strm_t *pStrm, obj_t *pObj, uchar *pszRecType) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pStrm, strm); + ISOBJ_assert(pObj); + assert(!strcmp((char*) pszRecType, "Obj") || !strcmp((char*) pszRecType, "OPB")); + + /* object cookie and serializer version (so far always 1) */ + CHKiRet(strmWriteChar(pStrm, COOKIE_OBJLINE)); + CHKiRet(strmWrite(pStrm, (uchar*) pszRecType, 3)); /* record types are always 3 octets */ + CHKiRet(strmWriteChar(pStrm, ':')); + CHKiRet(strmWriteChar(pStrm, '1')); + + /* object type, version and string length */ + CHKiRet(strmWriteChar(pStrm, ':')); + CHKiRet(strmWrite(pStrm, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)); + CHKiRet(strmWriteChar(pStrm, ':')); + CHKiRet(strmWriteLong(pStrm, objGetVersion(pObj))); + + /* record trailer */ + CHKiRet(strmWriteChar(pStrm, ':')); + CHKiRet(strmWriteChar(pStrm, '\n')); + +finalize_it: + RETiRet; +} + + +/* begin serialization of an object + * rgerhards, 2008-01-06 + */ +static rsRetVal +BeginSerialize(strm_t *pStrm, obj_t *pObj) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pStrm, strm); + ISOBJ_assert(pObj); + + CHKiRet(strmRecordBegin(pStrm)); + CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "Obj")); + +finalize_it: + RETiRet; +} + + +/* begin serialization of an object's property bag + * Note: a property bag is used to serialize some of an objects + * properties, but not necessarily all. A good example is the queue + * object, which at some stage needs to serialize a number of its + * properties, but not the queue data itself. From the object point + * of view, a property bag can not be used to re-instantiate an object. + * Otherwise, the serialization is exactly the same. + * rgerhards, 2008-01-11 + */ +static rsRetVal +BeginSerializePropBag(strm_t *pStrm, obj_t *pObj) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pStrm, strm); + ISOBJ_assert(pObj); + + CHKiRet(strmRecordBegin(pStrm)); + CHKiRet(objSerializeHeader(pStrm, pObj, (uchar*) "OPB")); + +finalize_it: + RETiRet; +} + + +/* append a property + */ +static rsRetVal +SerializeProp(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr) +{ + DEFiRet; + uchar *pszBuf = NULL; + size_t lenBuf = 0; + uchar szBuf[64]; + varType_t vType = VARTYPE_NONE; + + ISOBJ_TYPE_assert(pStrm, strm); + assert(pszPropName != NULL); + + /*dbgprintf("objSerializeProp: strm %p, propName '%s', type %d, pUsr %p\n", pStrm, pszPropName, propType, pUsr);*/ + /* if we have no user pointer, there is no need to write this property. + * TODO: think if that's the righ point of view + * rgerhards, 2008-01-06 + */ + if(pUsr == NULL) { + ABORT_FINALIZE(RS_RET_OK); + } + + /* TODO: use the stream functions for data conversion here - should be quicker */ + + switch(propType) { + case PROPTYPE_PSZ: + pszBuf = (uchar*) pUsr; + lenBuf = strlen((char*) pszBuf); + vType = VARTYPE_STR; + break; + case PROPTYPE_SHORT: + CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((short*) pUsr))); + pszBuf = szBuf; + lenBuf = strlen((char*) szBuf); + vType = VARTYPE_NUMBER; + break; + case PROPTYPE_INT: + CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), (long) *((int*) pUsr))); + pszBuf = szBuf; + lenBuf = strlen((char*) szBuf); + vType = VARTYPE_NUMBER; + break; + case PROPTYPE_LONG: + CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((long*) pUsr))); + pszBuf = szBuf; + lenBuf = strlen((char*) szBuf); + vType = VARTYPE_NUMBER; + break; + case PROPTYPE_INT64: + CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), *((int64*) pUsr))); + pszBuf = szBuf; + lenBuf = strlen((char*) szBuf); + vType = VARTYPE_NUMBER; + break; + case PROPTYPE_CSTR: + pszBuf = rsCStrGetSzStrNoNULL((cstr_t *) pUsr); + lenBuf = rsCStrLen((cstr_t*) pUsr); + vType = VARTYPE_STR; + break; + case PROPTYPE_SYSLOGTIME: + lenBuf = snprintf((char*) szBuf, sizeof(szBuf), "%d:%d:%d:%d:%d:%d:%d:%d:%d:%c:%d:%d", + ((syslogTime_t*)pUsr)->timeType, + ((syslogTime_t*)pUsr)->year, + ((syslogTime_t*)pUsr)->month, + ((syslogTime_t*)pUsr)->day, + ((syslogTime_t*)pUsr)->hour, + ((syslogTime_t*)pUsr)->minute, + ((syslogTime_t*)pUsr)->second, + ((syslogTime_t*)pUsr)->secfrac, + ((syslogTime_t*)pUsr)->secfracPrecision, + ((syslogTime_t*)pUsr)->OffsetMode, + ((syslogTime_t*)pUsr)->OffsetHour, + ((syslogTime_t*)pUsr)->OffsetMinute); + if(lenBuf > sizeof(szBuf) - 1) + ABORT_FINALIZE(RS_RET_PROVIDED_BUFFER_TOO_SMALL); + vType = VARTYPE_SYSLOGTIME; + pszBuf = szBuf; + break; + default: + dbgprintf("invalid PROPTYPE %d\n", propType); + break; + } + + /* cookie */ + CHKiRet(strmWriteChar(pStrm, COOKIE_PROPLINE)); + /* name */ + CHKiRet(strmWrite(pStrm, pszPropName, strlen((char*)pszPropName))); + CHKiRet(strmWriteChar(pStrm, ':')); + /* type */ + CHKiRet(strmWriteLong(pStrm, (int) vType)); + CHKiRet(strmWriteChar(pStrm, ':')); + /* length */ + CHKiRet(strmWriteLong(pStrm, lenBuf)); + CHKiRet(strmWriteChar(pStrm, ':')); + + /* data */ + CHKiRet(strmWrite(pStrm, (uchar*) pszBuf, lenBuf)); + + /* trailer */ + CHKiRet(strmWriteChar(pStrm, ':')); + CHKiRet(strmWriteChar(pStrm, '\n')); + +finalize_it: + RETiRet; +} + + +/* end serialization of an object. The caller receives a + * standard C string, which he must free when no longer needed. + */ +static rsRetVal +EndSerialize(strm_t *pStrm) +{ + DEFiRet; + + assert(pStrm != NULL); + + CHKiRet(strmWriteChar(pStrm, COOKIE_ENDLINE)); + CHKiRet(strmWrite(pStrm, (uchar*) "End\n", sizeof("END\n") - 1)); + CHKiRet(strmWriteChar(pStrm, COOKIE_BLANKLINE)); + CHKiRet(strmWriteChar(pStrm, '\n')); + + CHKiRet(strmRecordEnd(pStrm)); + +finalize_it: + RETiRet; +} + + +/* define a helper to make code below a bit cleaner (and quicker to write) */ +#define NEXTC CHKiRet(strmReadChar(pStrm, &c))//;dbgprintf("c: %c\n", c); + + +/* de-serialize an embedded, non-octect-counted string. This is useful + * for deserializing the object name inside the header. The string is + * terminated by the first occurence of the ':' character. + * rgerhards, 2008-02-29 + */ +static rsRetVal +objDeserializeEmbedStr(cstr_t **ppStr, strm_t *pStrm) +{ + DEFiRet; + uchar c; + cstr_t *pStr = NULL; + + assert(ppStr != NULL); + + CHKiRet(rsCStrConstruct(&pStr)); + + NEXTC; + while(c != ':') { + CHKiRet(rsCStrAppendChar(pStr, c)); + NEXTC; + } + CHKiRet(rsCStrFinish(pStr)); + + *ppStr = pStr; + +finalize_it: + if(iRet != RS_RET_OK && pStr != NULL) + rsCStrDestruct(&pStr); + + RETiRet; +} + + +/* de-serialize a number */ +static rsRetVal objDeserializeNumber(number_t *pNum, strm_t *pStrm) +{ + DEFiRet; + number_t i; + int bIsNegative; + uchar c; + + assert(pNum != NULL); + + NEXTC; + if(c == '-') { + bIsNegative = 1; + NEXTC; + } else { + bIsNegative = 0; + } + + /* we check this so that we get more meaningful error codes */ + if(!isdigit(c)) ABORT_FINALIZE(RS_RET_INVALID_NUMBER); + + i = 0; + while(isdigit(c)) { + i = i * 10 + c - '0'; + NEXTC; + } + + if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER); + + if(bIsNegative) + i *= -1; + + *pNum = i; +finalize_it: + RETiRet; +} + + +/* de-serialize a string, length must be provided but may be 0 */ +static rsRetVal objDeserializeStr(cstr_t **ppCStr, int iLen, strm_t *pStrm) +{ + DEFiRet; + int i; + uchar c; + cstr_t *pCStr = NULL; + + assert(ppCStr != NULL); + assert(iLen >= 0); + + CHKiRet(rsCStrConstruct(&pCStr)); + + NEXTC; + for(i = 0 ; i < iLen ; ++i) { + CHKiRet(rsCStrAppendChar(pCStr, c)); + NEXTC; + } + CHKiRet(rsCStrFinish(pCStr)); + + /* check terminator */ + if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER); + + *ppCStr = pCStr; + +finalize_it: + if(iRet != RS_RET_OK && pCStr != NULL) + rsCStrDestruct(&pCStr); + + RETiRet; +} + + +/* de-serialize a syslogTime -- rgerhards,2008-01-08 */ +#define GETVAL(var) \ + CHKiRet(objDeserializeNumber(&l, pStrm)); \ + pTime->var = l; +static rsRetVal objDeserializeSyslogTime(syslogTime_t *pTime, strm_t *pStrm) +{ + DEFiRet; + number_t l; + uchar c; + + assert(pTime != NULL); + + GETVAL(timeType); + GETVAL(year); + GETVAL(month); + GETVAL(day); + GETVAL(hour); + GETVAL(minute); + GETVAL(second); + GETVAL(secfrac); + GETVAL(secfracPrecision); + /* OffsetMode is a single character! */ + NEXTC; pTime->OffsetMode = c; + NEXTC; if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_DELIMITER); + GETVAL(OffsetHour); + GETVAL(OffsetMinute); + +finalize_it: + RETiRet; +} +#undef GETVAL + +/* de-serialize an object header + * rgerhards, 2008-01-07 + */ +static rsRetVal objDeserializeHeader(uchar *pszRecType, cstr_t **ppstrID, int* poVers, strm_t *pStrm) +{ + DEFiRet; + number_t oVers; + uchar c; + + assert(ppstrID != NULL); + assert(poVers != NULL); + assert(!strcmp((char*) pszRecType, "Obj") || !strcmp((char*) pszRecType, "OPB")); + + /* check header cookie */ + NEXTC; if(c != COOKIE_OBJLINE) ABORT_FINALIZE(RS_RET_INVALID_HEADER); + NEXTC; if(c != pszRecType[0]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE); + NEXTC; if(c != pszRecType[1]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE); + NEXTC; if(c != pszRecType[2]) ABORT_FINALIZE(RS_RET_INVALID_HEADER_RECTYPE); + NEXTC; if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_HEADER); + NEXTC; if(c != '1') ABORT_FINALIZE(RS_RET_INVALID_HEADER_VERS); + NEXTC; if(c != ':') ABORT_FINALIZE(RS_RET_INVALID_HEADER_VERS); + + /* object type and version */ + CHKiRet(objDeserializeEmbedStr(ppstrID, pStrm)); + CHKiRet(objDeserializeNumber(&oVers, pStrm)); + + /* and now we skip over the rest until the delemiting \n */ + NEXTC; + while(c != '\n') { + NEXTC; + } + + *poVers = oVers; + +finalize_it: + RETiRet; +} + + +/* Deserialize a single property. Pointer must be positioned at begin of line. Whole line + * up until the \n is read. + */ +static rsRetVal objDeserializeProperty(var_t *pProp, strm_t *pStrm) +{ + DEFiRet; + number_t i; + number_t iLen; + uchar c; + + assert(pProp != NULL); + + /* check cookie */ + NEXTC; + if(c != COOKIE_PROPLINE) { + /* oops, we've read one char that does not belong to use - unget it first */ + CHKiRet(strmUnreadChar(pStrm, c)); + ABORT_FINALIZE(RS_RET_NO_PROPLINE); + } + + /* get the property name first */ + CHKiRet(rsCStrConstruct(&pProp->pcsName)); + + NEXTC; + while(c != ':') { + CHKiRet(rsCStrAppendChar(pProp->pcsName, c)); + NEXTC; + } + CHKiRet(rsCStrFinish(pProp->pcsName)); + + /* property type */ + CHKiRet(objDeserializeNumber(&i, pStrm)); + pProp->varType = i; + + /* size (needed for strings) */ + CHKiRet(objDeserializeNumber(&iLen, pStrm)); + + /* we now need to deserialize the value */ + switch(pProp->varType) { + case VARTYPE_STR: + CHKiRet(objDeserializeStr(&pProp->val.pStr, iLen, pStrm)); + break; + case VARTYPE_NUMBER: + CHKiRet(objDeserializeNumber(&pProp->val.num, pStrm)); + break; + case VARTYPE_SYSLOGTIME: + CHKiRet(objDeserializeSyslogTime(&pProp->val.vSyslogTime, pStrm)); + break; + default: + dbgprintf("invalid VARTYPE %d\n", pProp->varType); + break; + } + + /* we should now be at the end of the line. So the next char must be \n */ + NEXTC; + if(c != '\n') ABORT_FINALIZE(RS_RET_INVALID_PROPFRAME); + +finalize_it: + RETiRet; +} + + +/* de-serialize an object trailer. This does not get any data but checks if the + * format is ok. + * rgerhards, 2008-01-07 + */ +static rsRetVal objDeserializeTrailer(strm_t *pStrm) +{ + DEFiRet; + uchar c; + + /* check header cookie */ + NEXTC; if(c != COOKIE_ENDLINE) ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + NEXTC; if(c != 'E') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + NEXTC; if(c != 'n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + NEXTC; if(c != 'd') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + NEXTC; if(c != '\n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + NEXTC; if(c != COOKIE_BLANKLINE) ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + NEXTC; if(c != '\n') ABORT_FINALIZE(RS_RET_INVALID_TRAILER); + +finalize_it: + RETiRet; +} + + + +/* This method tries to recover a serial store if it got out of sync. + * To do so, it scans the line beginning cookies and waits for the object + * cookie. If that is found, control is returned. If the store is exhausted, + * we will receive an RS_RET_EOF error as part of NEXTC, which will also + * terminate this function. So we may either return with somehting that + * looks like a valid object or end of store. + * rgerhards, 2008-01-07 + */ +static rsRetVal objDeserializeTryRecover(strm_t *pStrm) +{ + DEFiRet; + uchar c; + int bWasNL; + int bRun; + + assert(pStrm != NULL); + bRun = 1; + bWasNL = 0; + + while(bRun) { + NEXTC; + if(c == '\n') + bWasNL = 1; + else { + if(bWasNL == 1 && c == COOKIE_OBJLINE) + bRun = 0; /* we found it! */ + else + bWasNL = 0; + } + } + + CHKiRet(strmUnreadChar(pStrm, c)); + +finalize_it: + dbgprintf("deserializer has possibly been able to re-sync and recover, state %d\n", iRet); + RETiRet; +} + + +/* De-serialize the properties of an object. This includes processing + * of the trailer. Header must already have been processed. + * rgerhards, 2008-01-11 + */ +static rsRetVal objDeserializeProperties(obj_t *pObj, objInfo_t *pObjInfo, strm_t *pStrm) +{ + DEFiRet; + var_t *pVar = NULL; + + ISOBJ_assert(pObj); + ISOBJ_TYPE_assert(pStrm, strm); + ASSERT(pObjInfo != NULL); + + CHKiRet(var.Construct(&pVar)); + CHKiRet(var.ConstructFinalize(pVar)); + + iRet = objDeserializeProperty(pVar, pStrm); + while(iRet == RS_RET_OK) { + CHKiRet(pObjInfo->objMethods[objMethod_SETPROPERTY](pObj, pVar)); + /* re-init var object - TODO: method of var! */ + rsCStrDestruct(&pVar->pcsName); /* no longer needed */ + if(pVar->varType == VARTYPE_STR) { + if(pVar->val.pStr != NULL) + rsCStrDestruct(&pVar->val.pStr); + } + iRet = objDeserializeProperty(pVar, pStrm); + } + + if(iRet != RS_RET_NO_PROPLINE) + FINALIZE; + + CHKiRet(objDeserializeTrailer(pStrm)); /* do trailer checks */ +finalize_it: + if(pVar != NULL) + var.Destruct(&pVar); + + RETiRet; +} + + +/* De-Serialize an object. + * Params: Pointer to object Pointer (pObj) (like a obj_t**, but can not do that due to compiler warning) + * expected object ID (to check against), a fixup function that can modify the object before it is finalized + * and a user pointer that is to be passed to that function in addition to the object. The fixup function + * pointer may be NULL, in which case none is called. + * The caller must destruct the created object. + * rgerhards, 2008-01-07 + */ +static rsRetVal +Deserialize(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixup)(obj_t*,void*), void *pUsr) +{ + DEFiRet; + rsRetVal iRetLocal; + obj_t *pObj = NULL; + int oVers = 0; /* after all, it is totally useless but takes up some execution time... */ + cstr_t *pstrID = NULL; + objInfo_t *pObjInfo; + + assert(ppObj != NULL); + assert(pszTypeExpected != NULL); + ISOBJ_TYPE_assert(pStrm, strm); + + /* we de-serialize the header. if all goes well, we are happy. However, if + * we experience a problem, we try to recover. We do this by skipping to + * the next object header. This is defined via the line-start cookies. In + * worst case, we exhaust the queue, but then we receive EOF return state, + * from objDeserializeTryRecover(), what will cause us to ultimately give up. + * rgerhards, 2008-07-08 + */ + do { + iRetLocal = objDeserializeHeader((uchar*) "Obj", &pstrID, &oVers, pStrm); + if(iRetLocal != RS_RET_OK) { + dbgprintf("objDeserialize error %d during header processing - trying to recover\n", iRetLocal); + CHKiRet(objDeserializeTryRecover(pStrm)); + } + } while(iRetLocal != RS_RET_OK); + + if(rsCStrSzStrCmp(pstrID, pszTypeExpected, strlen((char*)pszTypeExpected))) // TODO: optimize strlen() - caller shall provide + ABORT_FINALIZE(RS_RET_INVALID_OID); + + CHKiRet(FindObjInfo(pstrID, &pObjInfo)); + + CHKiRet(pObjInfo->objMethods[objMethod_CONSTRUCT](&pObj)); + + /* we got the object, now we need to fill the properties */ + CHKiRet(objDeserializeProperties(pObj, pObjInfo, pStrm)); + + /* check if we need to call a fixup function that modifies the object + * before it is finalized. -- rgerhards, 2008-01-13 + */ + if(fFixup != NULL) + CHKiRet(fFixup(pObj, pUsr)); + + /* we have a valid object, let's finalize our work and return */ + if(objInfoIsImplemented(pObjInfo, objMethod_CONSTRUCTION_FINALIZER)) + CHKiRet(pObjInfo->objMethods[objMethod_CONSTRUCTION_FINALIZER](pObj)); + + *((obj_t**) ppObj) = pObj; + +finalize_it: + if(iRet != RS_RET_OK && pObj != NULL) + free(pObj); // TODO: check if we can call destructor 2008-01-13 rger + + if(pstrID != NULL) + rsCStrDestruct(&pstrID); + + RETiRet; +} + + +/* De-Serialize an object, but treat it as property bag. + * rgerhards, 2008-01-11 + */ +rsRetVal +objDeserializeObjAsPropBag(obj_t *pObj, strm_t *pStrm) +{ + DEFiRet; + rsRetVal iRetLocal; + cstr_t *pstrID = NULL; + int oVers = 0; /* after all, it is totally useless but takes up some execution time... */ + objInfo_t *pObjInfo; + + ISOBJ_assert(pObj); + ISOBJ_TYPE_assert(pStrm, strm); + + /* we de-serialize the header. if all goes well, we are happy. However, if + * we experience a problem, we try to recover. We do this by skipping to + * the next object header. This is defined via the line-start cookies. In + * worst case, we exhaust the queue, but then we receive EOF return state + * from objDeserializeTryRecover(), what will cause us to ultimately give up. + * rgerhards, 2008-07-08 + */ + do { + iRetLocal = objDeserializeHeader((uchar*) "Obj", &pstrID, &oVers, pStrm); + if(iRetLocal != RS_RET_OK) { + dbgprintf("objDeserializeObjAsPropBag error %d during header - trying to recover\n", iRetLocal); + CHKiRet(objDeserializeTryRecover(pStrm)); + } + } while(iRetLocal != RS_RET_OK); + + if(rsCStrSzStrCmp(pstrID, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)) + ABORT_FINALIZE(RS_RET_INVALID_OID); + + CHKiRet(FindObjInfo(pstrID, &pObjInfo)); + + /* we got the object, now we need to fill the properties */ + CHKiRet(objDeserializeProperties(pObj, pObjInfo, pStrm)); + +finalize_it: + if(pstrID != NULL) + rsCStrDestruct(&pstrID); + + RETiRet; +} + + + +/* De-Serialize an object property bag. As a property bag contains only partial properties, + * it is not instanciable. Thus, the caller must provide a pointer of an already-instanciated + * object of the correct type. + * Params: Pointer to object (pObj) + * Pointer to be passed to the function + * The caller must destruct the created object. + * rgerhards, 2008-01-07 + */ +static rsRetVal +DeserializePropBag(obj_t *pObj, strm_t *pStrm) +{ + DEFiRet; + rsRetVal iRetLocal; + cstr_t *pstrID = NULL; + int oVers; + objInfo_t *pObjInfo; + + ISOBJ_assert(pObj); + ISOBJ_TYPE_assert(pStrm, strm); + + /* we de-serialize the header. if all goes well, we are happy. However, if + * we experience a problem, we try to recover. We do this by skipping to + * the next object header. This is defined via the line-start cookies. In + * worst case, we exhaust the queue, but then we receive EOF return state + * from objDeserializeTryRecover(), what will cause us to ultimately give up. + * rgerhards, 2008-07-08 + */ + do { + iRetLocal = objDeserializeHeader((uchar*) "OPB", &pstrID, &oVers, pStrm); + if(iRetLocal != RS_RET_OK) { + dbgprintf("objDeserializePropBag error %d during header - trying to recover\n", iRetLocal); + CHKiRet(objDeserializeTryRecover(pStrm)); + } + } while(iRetLocal != RS_RET_OK); + + if(rsCStrSzStrCmp(pstrID, pObj->pObjInfo->pszID, pObj->pObjInfo->lenID)) + ABORT_FINALIZE(RS_RET_INVALID_OID); + + CHKiRet(FindObjInfo(pstrID, &pObjInfo)); + + /* we got the object, now we need to fill the properties */ + CHKiRet(objDeserializeProperties(pObj, pObjInfo, pStrm)); + +finalize_it: + if(pstrID != NULL) + rsCStrDestruct(&pstrID); + + RETiRet; +} + +#undef NEXTC /* undef helper macro */ + + +/* --------------- end object serializiation / deserialization support --------------- */ + + +/* set the object (instance) name + * rgerhards, 2008-01-29 + * TODO: change the naming to a rsCStr obj! (faster) + */ +static rsRetVal +SetName(obj_t *pThis, uchar *pszName) +{ + DEFiRet; + + if(pThis->pszName != NULL) + free(pThis->pszName); + + pThis->pszName = (uchar*) strdup((char*) pszName); + + if(pThis->pszName == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + +finalize_it: + RETiRet; +} + + +/* get the object (instance) name + * Note that we use a non-standard calling convention. Thus function must never + * fail, else we run into real big problems. So it must make sure that at least someting + * is returned. + * rgerhards, 2008-01-30 + */ +static uchar * +GetName(obj_t *pThis) +{ + uchar *ret; + uchar szName[128]; + + BEGINfunc + ISOBJ_assert(pThis); + + if(pThis->pszName == NULL) { + snprintf((char*)szName, sizeof(szName)/sizeof(uchar), "%s %p", objGetClassName(pThis), pThis); + SetName(pThis, szName); + /* looks strange, but we NEED to re-check because if there was an + * error in objSetName(), the pointer may still be NULL + */ + if(pThis->pszName == NULL) { + ret = objGetClassName(pThis); + } else { + ret = pThis->pszName; + } + } else { + ret = pThis->pszName; + } + + ENDfunc + return ret; +} + + +/* Find the objInfo object for the current object + * rgerhards, 2008-02-29 + */ +static rsRetVal +FindObjInfo(cstr_t *pstrOID, objInfo_t **ppInfo) +{ + DEFiRet; + int bFound; + int i; + + assert(pstrOID != NULL); + assert(ppInfo != NULL); + + bFound = 0; + i = 0; + while(!bFound && i < OBJ_NUM_IDS) { + if(arrObjInfo[i] != NULL && !rsCStrSzStrCmp(pstrOID, arrObjInfo[i]->pszID, arrObjInfo[i]->lenID)) { + bFound = 1; + break; + } + ++i; + } + + if(!bFound) + ABORT_FINALIZE(RS_RET_NOT_FOUND); + + *ppInfo = arrObjInfo[i]; + +finalize_it: + if(iRet == RS_RET_OK) { + /* DEV DEBUG ONLY dbgprintf("caller requested object '%s', found at index %d\n", (*ppInfo)->pszID, i);*/ + /*EMPTY BY INTENSION*/; + } else { + dbgprintf("caller requested object '%s', not found (iRet %d)\n", rsCStrGetSzStr(pstrOID), iRet); + } + + RETiRet; +} + + +/* register a classes' info pointer, so that we can reference it later, if needed to + * (e.g. for de-serialization support). + * rgerhards, 2008-01-07 + * In this function, we look for a free space in the object table. While we do so, we + * also detect if the same object has already been registered, which is not valid. + * rgerhards, 2008-02-29 + */ +static rsRetVal +RegisterObj(uchar *pszObjName, objInfo_t *pInfo) +{ + DEFiRet; + int bFound; + int i; + + assert(pszObjName != NULL); + assert(pInfo != NULL); + + bFound = 0; + i = 0; + while(!bFound && i < OBJ_NUM_IDS && arrObjInfo[i] != NULL) { + if( arrObjInfo[i] != NULL + && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) { + bFound = 1; + break; + } + ++i; + } + + if(bFound) ABORT_FINALIZE(RS_RET_OBJ_ALREADY_REGISTERED); + if(i >= OBJ_NUM_IDS) ABORT_FINALIZE(RS_RET_OBJ_REGISTRY_OUT_OF_SPACE); + + arrObjInfo[i] = pInfo; + /* DEV debug only: dbgprintf("object '%s' successfully registered with index %d, qIF %p\n", pszObjName, i, pInfo->QueryIF); */ + +finalize_it: + if(iRet != RS_RET_OK) { + errmsg.LogError(NO_ERRCODE, "registering object '%s' failed with error code %d", pszObjName, iRet); + } + + RETiRet; +} + + +/* deregister a classes' info pointer, usually called because the class is unloaded. + * After deregistration, the class can no longer be accessed, except if it is reloaded. + * rgerhards, 2008-03-10 + */ +static rsRetVal +UnregisterObj(uchar *pszObjName) +{ + DEFiRet; + int bFound; + int i; + + assert(pszObjName != NULL); + + bFound = 0; + i = 0; + while(!bFound && i < OBJ_NUM_IDS) { + if( arrObjInfo[i] != NULL + && !strcmp((char*)arrObjInfo[i]->pszID, (char*)pszObjName)) { + bFound = 1; + break; + } + ++i; + } + + if(!bFound) + ABORT_FINALIZE(RS_RET_OBJ_NOT_REGISTERED); + + InfoDestruct(&arrObjInfo[i]); + /* DEV debug only: dbgprintf("object '%s' successfully unregistered with index %d\n", pszObjName, i); */ + +finalize_it: + if(iRet != RS_RET_OK) { + dbgprintf("unregistering object '%s' failed with error code %d\n", pszObjName, iRet); + } + + RETiRet; +} + + +/* This function shall be called by anyone who would like to use an object. It will + * try to locate the object, load it into memory if not already present and return + * a pointer to the objects interface. + * rgerhards, 2008-02-29 + */ +static rsRetVal +UseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) +{ + DEFiRet; + cstr_t *pStr = NULL; + objInfo_t *pObjInfo; + + + /* DEV debug only: dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); */ + + if(pIf->ifIsLoaded == 1) { + ABORT_FINALIZE(RS_RET_OK); /* we are already set */ + } + if(pIf->ifIsLoaded == 2) { + ABORT_FINALIZE(RS_RET_LOAD_ERROR); /* we had a load error and can not continue */ + } + + /* we must be careful that we do not enter in infinite loop if an error occurs during + * loading a module. ModLoad emits an error message in such cases and that potentially + * can trigger the same code here. So we initially set the module state to "load error" + * and set it to "fully initialized" when the load succeeded. It's a bit hackish, but + * looks like a good solution. -- rgerhards, 2008-03-07 + */ + pIf->ifIsLoaded = 2; + + CHKiRet(rsCStrConstructFromszStr(&pStr, pObjName)); + iRet = FindObjInfo(pStr, &pObjInfo); + if(iRet == RS_RET_NOT_FOUND) { + /* in this case, we need to see if we can dynamically load the object */ + if(pObjFile == NULL) { + FINALIZE; /* no chance, we have lost... */ + } else { + CHKiRet(module.Load(pObjFile)); + /* NOW, we must find it or we have a problem... */ + CHKiRet(FindObjInfo(pStr, &pObjInfo)); + } + } else if(iRet != RS_RET_OK) { + FINALIZE; /* give up */ + } + + /* if we reach this point, we have a valid pObjInfo */ + if(pObjFile != NULL) { /* NULL means core module */ + module.Use(srcFile, pObjInfo->pModInfo); /* increase refcount */ + } + + CHKiRet(pObjInfo->QueryIF(pIf)); + pIf->ifIsLoaded = 1; /* we are happy */ + +finalize_it: + if(pStr != NULL) + rsCStrDestruct(&pStr); + + RETiRet; +} + + +/* This function shall be called when a caller is done with an object. Its primary + * purpose is to keep the reference count correct, which is highly important for + * modules residing in loadable modules. + * rgerhards, 2008-03-10 + */ +static rsRetVal +ReleaseObj(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf) +{ + DEFiRet; + cstr_t *pStr = NULL; + objInfo_t *pObjInfo; + + + dbgprintf("source file %s requests object '%s', ifIsLoaded %d\n", srcFile, pObjName, pIf->ifIsLoaded); + + if(pObjFile == NULL) + FINALIZE; /* if it is not a lodable module, we do not need to do anything... */ + + if(pIf->ifIsLoaded == 0) { + ABORT_FINALIZE(RS_RET_OK); /* we are already set */ /* TODO: flag an error? */ + } + if(pIf->ifIsLoaded == 2) { + pIf->ifIsLoaded = 0; /* clean up */ + ABORT_FINALIZE(RS_RET_OK); /* we had a load error and can not continue */ + } + + CHKiRet(rsCStrConstructFromszStr(&pStr, pObjName)); + CHKiRet(FindObjInfo(pStr, &pObjInfo)); + + /* if we reach this point, we have a valid pObjInfo */ + //if(pObjInfo->pModInfo != NULL) { /* NULL means core module */ + module.Release(srcFile, &pObjInfo->pModInfo); /* decrease refcount */ + + pIf->ifIsLoaded = 0; /* indicated "no longer valid" */ + +finalize_it: + if(pStr != NULL) + rsCStrDestruct(&pStr); + + RETiRet; +} + + +/* queryInterface function + * rgerhards, 2008-02-29 + */ +BEGINobjQueryInterface(obj) +CODESTARTobjQueryInterface(obj) + if(pIf->ifVersion != objCURR_IF_VERSION) { /* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + + /* ok, we have the right interface, so let's fill it + * Please note that we may also do some backwards-compatibility + * work here (if we can support an older interface version - that, + * of course, also affects the "if" above). + */ + pIf->UseObj = UseObj; + pIf->ReleaseObj = ReleaseObj; + pIf->InfoConstruct = InfoConstruct; + pIf->DestructObjSelf = DestructObjSelf; + pIf->BeginSerializePropBag = BeginSerializePropBag; + pIf->InfoSetMethod = InfoSetMethod; + pIf->BeginSerialize = BeginSerialize; + pIf->SerializeProp = SerializeProp; + pIf->EndSerialize = EndSerialize; + pIf->RegisterObj = RegisterObj; + pIf->UnregisterObj = UnregisterObj; + pIf->Deserialize = Deserialize; + pIf->DeserializePropBag = DeserializePropBag; + pIf->SetName = SetName; + pIf->GetName = GetName; +finalize_it: +ENDobjQueryInterface(obj) + + +/* This function returns a pointer to our own interface. It is used as the + * hook that every object (including dynamically loaded ones) can use to + * obtain a pointer to our interface which than can be used to obtain + * pointers to any other interface in the system. This function must be + * externally visible because of its special nature. + * rgerhards, 2008-02-29 [nice - will have that date the next time in 4 years ;)] + */ +rsRetVal +objGetObjInterface(obj_if_t *pIf) +{ + DEFiRet; + assert(pIf != NULL); + objQueryInterface(pIf); + RETiRet; +} + + +/* exit our class + * rgerhards, 2008-03-11 + */ +rsRetVal +objClassExit(void) +{ + DEFiRet; + /* release objects we no longer need */ + objRelease(var, CORE_COMPONENT); + objRelease(module, CORE_COMPONENT); + objRelease(errmsg, CORE_COMPONENT); + + /* TODO: implement the class exits! */ +#if 0 + errmsgClassInit(pModInfo); + cfsyslineInit(pModInfo); + varClassInit(pModInfo); +#endif + moduleClassExit(); + RETiRet; +} + + +/* initialize our own class + * Please note that this also initializes those classes that we rely on. + * Though this is a bit dirty, we need to do it - otherwise we can't get + * around that bootstrap problem. We need to face the fact the the obj + * class is a little different from the rest of the system, as it provides + * the core class loader functionality. + * rgerhards, 2008-02-29 + */ +rsRetVal +objClassInit(modInfo_t *pModInfo) +{ + DEFiRet; + int i; + + /* first, initialize the object system itself. This must be done + * before any other object is created. + */ + for(i = 0 ; i < OBJ_NUM_IDS ; ++i) { + arrObjInfo[i] = NULL; + } + + /* request objects we use */ + CHKiRet(objGetObjInterface(&obj)); /* get ourselves ;) */ + + /* init classes we use (limit to as few as possible!) */ + CHKiRet(errmsgClassInit(pModInfo)); + CHKiRet(cfsyslineInit()); + CHKiRet(varClassInit(pModInfo)); + CHKiRet(moduleClassInit(pModInfo)); + CHKiRet(objUse(var, CORE_COMPONENT)); + CHKiRet(objUse(module, CORE_COMPONENT)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + +finalize_it: + RETiRet; +} + +/* vi:set ai: + */ diff --git a/runtime/obj.h b/runtime/obj.h new file mode 100644 index 00000000..dc04203b --- /dev/null +++ b/runtime/obj.h @@ -0,0 +1,125 @@ +/* Definition of the generic obj class module. + * + * This module relies heavily on preprocessor macros in order to + * provide fast execution time AND ease of use. + * + * Each object that uses this base class MUST provide a constructor with + * the following interface: + * + * Destruct(pThis); + * + * A constructor is not necessary (except for some features, e.g. de-serialization). + * If it is provided, it is a three-part constructor (to handle all cases with a + * generic interface): + * + * Construct(&pThis); + * SetProperty(pThis, property_t *); + * ConstructFinalize(pThis); + * + * SetProperty() and ConstructFinalize() may also be called on an object + * instance which has been Construct()'ed outside of this module. + * + * pThis always references to a pointer of the object. + * + * Copyright 2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef OBJ_H_INCLUDED +#define OBJ_H_INCLUDED + +#include "obj-types.h" +#include "var.h" +#include "stream.h" + +/* macros */ +/* the following one is a helper that prevents us from writing the + * ever-same code at the end of Construct() + */ +#define OBJCONSTRUCT_CHECK_SUCCESS_AND_CLEANUP \ + if(iRet == RS_RET_OK) { \ + *ppThis = pThis; \ + } else { \ + if(pThis != NULL) \ + free(pThis); \ + } + +#define objSerializeSCALAR_VAR(strm, propName, propType, var) \ + CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) &var)); +#define objSerializeSCALAR(strm, propName, propType) \ + CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) &pThis->propName)); +#define objSerializePTR(strm, propName, propType) \ + CHKiRet(obj.SerializeProp(strm, (uchar*) #propName, PROPTYPE_##propType, (void*) pThis->propName)); +#define DEFobjStaticHelpers \ + static objInfo_t *pObjInfoOBJ = NULL; \ + DEFobjCurrIf(obj) + + +#define objGetClassName(pThis) (((obj_t*) (pThis))->pObjInfo->pszID) +#define objGetVersion(pThis) (((obj_t*) (pThis))->pObjInfo->iObjVers) +/* the next macro MUST be called in Constructors: */ +#ifndef NDEBUG /* this means if debug... */ +# define objConstructSetObjInfo(pThis) \ + ASSERT(((obj_t*) (pThis))->pObjInfo == NULL); \ + ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ; \ + ((obj_t*) (pThis))->iObjCooCKiE = 0xBADEFEE +#else +# define objConstructSetObjInfo(pThis) ((obj_t*) (pThis))->pObjInfo = pObjInfoOBJ +#endif +#define objDestruct(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_DESTRUCT])(&pThis) +#define objSerialize(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_SERIALIZE]) +#define objGetSeverity(pThis, piSever) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_GETSEVERITY])(pThis, piSever) +#define objDebugPrint(pThis) (((obj_t*) (pThis))->pObjInfo->objMethods[objMethod_DEBUGPRINT])(pThis) + +#define OBJSetMethodHandler(methodID, pHdlr) \ + CHKiRet(obj.InfoSetMethod(pObjInfoOBJ, methodID, (rsRetVal (*)(void*)) pHdlr)) + +/* interfaces */ +BEGINinterface(obj) /* name must also be changed in ENDinterface macro! */ + rsRetVal (*UseObj)(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf); + rsRetVal (*ReleaseObj)(char *srcFile, uchar *pObjName, uchar *pObjFile, interface_t *pIf); + rsRetVal (*InfoConstruct)(objInfo_t **ppThis, uchar *pszID, int iObjVers, + rsRetVal (*pConstruct)(void *), rsRetVal (*pDestruct)(void *), + rsRetVal (*pQueryIF)(interface_t*), modInfo_t*); + rsRetVal (*DestructObjSelf)(obj_t *pThis); + rsRetVal (*BeginSerializePropBag)(strm_t *pStrm, obj_t *pObj); + rsRetVal (*InfoSetMethod)(objInfo_t *pThis, objMethod_t objMethod, rsRetVal (*pHandler)(void*)); + rsRetVal (*BeginSerialize)(strm_t *pStrm, obj_t *pObj); + rsRetVal (*SerializeProp)(strm_t *pStrm, uchar *pszPropName, propType_t propType, void *pUsr); + rsRetVal (*EndSerialize)(strm_t *pStrm); + rsRetVal (*RegisterObj)(uchar *pszObjName, objInfo_t *pInfo); + rsRetVal (*UnregisterObj)(uchar *pszObjName); + rsRetVal (*Deserialize)(void *ppObj, uchar *pszTypeExpected, strm_t *pStrm, rsRetVal (*fFixup)(obj_t*,void*), void *pUsr); + rsRetVal (*DeserializePropBag)(obj_t *pObj, strm_t *pStrm); + rsRetVal (*SetName)(obj_t *pThis, uchar *pszName); + uchar * (*GetName)(obj_t *pThis); +ENDinterface(obj) +#define objCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ + + +/* prototypes */ +/* the following define *is* necessary, because it provides the root way of obtaining + * interfaces (at some place we need to start our query... + */ +rsRetVal objGetObjInterface(obj_if_t *pIf); +PROTOTYPEObjClassInit(obj); +PROTOTYPEObjClassExit(obj); + +#endif /* #ifndef OBJ_H_INCLUDED */ diff --git a/runtime/objomsr.c b/runtime/objomsr.c new file mode 100644 index 00000000..21d284f3 --- /dev/null +++ b/runtime/objomsr.c @@ -0,0 +1,145 @@ +/* objomsr.c + * Implementation of the omsr (omodStringRequest) object. + * + * File begun on 2007-07-27 by RGerhards + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include +#include +#include +#include + +#include "rsyslog.h" +#include "objomsr.h" + + +/* destructor + */ +rsRetVal OMSRdestruct(omodStringRequest_t *pThis) +{ + int i; + + assert(pThis != NULL); + /* free the strings */ + if(pThis->ppTplName != NULL) { + for(i = 0 ; i < pThis->iNumEntries ; ++i) { + if(pThis->ppTplName[i] != NULL) { + free(pThis->ppTplName[i]); + } + } + free(pThis->ppTplName); + } + if(pThis->piTplOpts != NULL) + free(pThis->piTplOpts); + free(pThis); + + return RS_RET_OK; +} + + +/* constructor + */ +rsRetVal OMSRconstruct(omodStringRequest_t **ppThis, int iNumEntries) +{ + omodStringRequest_t *pThis; + DEFiRet; + + assert(ppThis != NULL); + assert(iNumEntries >= 0); + if((pThis = calloc(1, sizeof(omodStringRequest_t))) == NULL) { + iRet = RS_RET_OUT_OF_MEMORY; + goto abort_it; + } + + /* got the structure, so fill it */ + pThis->iNumEntries = iNumEntries; + /* allocate string for template name array. The individual strings will be + * allocated as the code progresses (we do not yet know the string sizes) + */ + if((pThis->ppTplName = calloc(iNumEntries, sizeof(uchar*))) == NULL) { + OMSRdestruct(pThis); + pThis = NULL; + iRet = RS_RET_OUT_OF_MEMORY; + goto abort_it; + } + /* allocate the template options array. */ + if((pThis->piTplOpts = calloc(iNumEntries, sizeof(int))) == NULL) { + OMSRdestruct(pThis); + pThis = NULL; + iRet = RS_RET_OUT_OF_MEMORY; + goto abort_it; + } + +abort_it: + *ppThis = pThis; + RETiRet; +} + +/* set a template name and option to the object. Index must be given. The pTplName must be + * pointing to memory that can be freed. If in doubt, the caller must strdup() the value. + */ +rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, int iTplOpts) +{ + assert(pThis != NULL); + assert(pTplName != NULL); + assert(iEntry < pThis->iNumEntries); + + if(pThis->ppTplName[iEntry] != NULL) + free(pThis->ppTplName[iEntry]); + pThis->ppTplName[iEntry] = pTplName; + pThis->piTplOpts[iEntry] = iTplOpts; + + return RS_RET_OK; +} + + +/* get number of entries for this object + */ +int OMSRgetEntryCount(omodStringRequest_t *pThis) +{ + assert(pThis != NULL); + return pThis->iNumEntries; +} + + +/* return data for a specific entry. All data returned is + * read-only and lasts only as long as the object lives. If the caller + * needs it for an extended period of time, the caller must copy the + * strings. Please note that the string pointer may be NULL, which is the + * case when it was never set. + */ +int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int *piTplOpts) +{ + assert(pThis != NULL); + assert(ppTplName != NULL); + assert(piTplOpts != NULL); + assert(iEntry < pThis->iNumEntries); + + *ppTplName = pThis->ppTplName[iEntry]; + *piTplOpts = pThis->piTplOpts[iEntry]; + + return RS_RET_OK; +} +/* vim:set ai: + */ diff --git a/runtime/objomsr.h b/runtime/objomsr.h new file mode 100644 index 00000000..2255e4f3 --- /dev/null +++ b/runtime/objomsr.h @@ -0,0 +1,46 @@ +/* Definition of the omsr (omodStringRequest) object. + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ + +#ifndef OBJOMSR_H_INCLUDED +#define OBJOMSR_H_INCLUDED + +/* define flags for required template options */ +#define OMSR_NO_RQD_TPL_OPTS 0 +#define OMSR_RQD_TPL_OPT_SQL 1 +/* next option is 2, 4, 8, ... */ + +struct omodStringRequest_s { /* strings requested by output module for doAction() */ + int iNumEntries; /* number of array entries for data elements below */ + uchar **ppTplName; /* pointer to array of template names */ + int *piTplOpts;/* pointer to array of check-options when pulling template */ +}; +typedef struct omodStringRequest_s omodStringRequest_t; + +/* prototypes */ +rsRetVal OMSRdestruct(omodStringRequest_t *pThis); +rsRetVal OMSRconstruct(omodStringRequest_t **ppThis, int iNumEntries); +rsRetVal OMSRsetEntry(omodStringRequest_t *pThis, int iEntry, uchar *pTplName, int iTplOpts); +int OMSRgetEntryCount(omodStringRequest_t *pThis); +int OMSRgetEntry(omodStringRequest_t *pThis, int iEntry, uchar **ppTplName, int *piTplOpts); + +#endif /* #ifndef OBJOMSR_H_INCLUDED */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h new file mode 100644 index 00000000..2bc7f904 --- /dev/null +++ b/runtime/rsyslog.h @@ -0,0 +1,272 @@ +/* This is the header file for the rsyslog runtime. It must be included + * if someone intends to use the runtime. + * + * Begun 2005-09-15 RGerhards + * + * Copyright (C) 2005-2008 by Rainer Gerhards and Adiscon GmbH + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef INCLUDED_RSYSLOG_H +#define INCLUDED_RSYSLOG_H + +/* ############################################################# * + * # Config Settings # * + * ############################################################# */ +#define RS_STRINGBUF_ALLOC_INCREMENT 128 + +/* ############################################################# * + * # End Config Settings # * + * ############################################################# */ + +#ifndef NOLARGEFILE +# undef _LARGEFILE_SOURCE +# undef _LARGEFILE64_SOURCE +# undef _FILE_OFFSET_BITS +# define _LARGEFILE_SOURCE +# define _LARGEFILE64_SOURCE +# define _FILE_OFFSET_BITS 64 +#endif + +/* define some base data types */ +typedef struct thrdInfo thrdInfo_t; + +/* some universal 64 bit define... */ +typedef long long int64; +typedef long long unsigned uint64; +typedef int64 number_t; /* type to use for numbers - TODO: maybe an autoconf option? */ + +#ifdef __hpux +typedef unsigned int u_int32_t; /* TODO: is this correct? */ +typedef int socklen_t; +#endif + +/* settings for flow control + * TODO: is there a better place for them? -- rgerhards, 2008-03-14 + */ +typedef enum { + eFLOWCTL_NO_DELAY = 0, /**< UDP and other non-delayable sources */ + eFLOWCTL_LIGHT_DELAY = 1, /**< some light delay possible, but no extended period of time */ + eFLOWCTL_FULL_DELAY = 2 /**< delay possible for extended period of time */ +} flowControl_t; + + +/* The error codes below are orginally "borrowed" from + * liblogging. As such, we reserve values up to -2999 + * just in case we need to borrow something more ;) +*/ +enum rsRetVal_ /** return value. All methods return this if not specified otherwise */ +{ + RS_RET_NOT_IMPLEMENTED = -7, /**< implementation is missing (probably internal error or lazyness ;)) */ + RS_RET_OUT_OF_MEMORY = -6, /**< memory allocation failed */ + RS_RET_PROVIDED_BUFFER_TOO_SMALL = -50,/**< the caller provided a buffer, but the called function sees the size of this buffer is too small - operation not carried out */ + RS_RET_TRUE = -1, /**< to indicate a true state (can be used as TRUE, legacy) */ + RS_RET_FALSE = -2, /**< to indicate a false state (can be used as FALSE, legacy) */ + RS_RET_NO_IRET = -8, /**< This is a trick for the debuging system - it means no iRet is provided */ + RS_RET_ERR = -3000, /**< generic failure */ + RS_TRUNCAT_TOO_LARGE = -3001, /**< truncation operation where too many chars should be truncated */ + RS_RET_FOUND_AT_STRING_END = -3002, /**< some value found, but at the last pos of string */ + RS_RET_NOT_FOUND = -3003, /**< some requested value not found */ + RS_RET_MISSING_TRAIL_QUOTE = -3004, /**< an expected trailing quote is missing */ + RS_RET_NO_DIGIT = -3005, /**< an digit was expected, but none found (mostly parsing) */ + RS_RET_NO_MORE_DATA = -3006, /**< insufficient data, e.g. end of string during parsing */ + RS_RET_INVALID_IP = -3007, /**< invalid ip found where valid was expected */ + RS_RET_OBJ_CREATION_FAILED = - 3008, /**< the creation of an object failed (no details available) */ + RS_RET_PARAM_ERROR = -1000, /**< invalid parameter in call to function */ + RS_RET_MISSING_INTERFACE = -1001,/**< interface version mismatch, required missing */ + RS_RET_INVALID_CORE_INTERFACE = -1002,/**< interface provided by host invalid, can not be used */ + RS_RET_ENTRY_POINT_NOT_FOUND = -1003,/**< a requested entry point was not found */ + RS_RET_MODULE_ENTRY_POINT_NOT_FOUND = -1004,/**< a entry point requested from a module was not present in it */ + RS_RET_OBJ_NOT_AVAILABLE = -1005,/**< something could not be completed because the required object is not available*/ + RS_RET_LOAD_ERROR = -1006,/**< we had an error loading the object/interface and can not continue */ + RS_RET_MODULE_STILL_REFERENCED = -1007,/**< module could not be unloaded because it still is referenced by someone */ + RS_RET_OBJ_UNKNOWN = -1008,/**< object is unknown where required */ + RS_RET_OBJ_NOT_REGISTERED = -1009,/**< tried to unregister an object that is not registered */ + /* return states for config file processing */ + RS_RET_NONE = -2000, /**< some value is not available - not necessarily an error */ + RS_RET_CONFLINE_UNPROCESSED = -2001,/**< config line was not processed, pass to other module */ + RS_RET_DISCARDMSG = -2002, /**< discard message (no error state, processing request!) */ + RS_RET_INCOMPATIBLE = -2003, /**< function not compatible with requested feature */ + RS_RET_NOENTRY = -2004, /**< do not create an entry for (whatever) - not necessary an error */ + RS_RET_NO_SQL_STRING = -2005, /**< string is not suitable for use as SQL */ + RS_RET_DISABLE_ACTION = -2006, /**< action requests that it be disabled */ + RS_RET_SUSPENDED = -2007, /**< something was suspended, not neccesarily an error */ + RS_RET_RQD_TPLOPT_MISSING = -2008,/**< a required template option is missing */ + RS_RET_INVALID_VALUE = -2009,/**< some value is invalid (e.g. user-supplied data) */ + RS_RET_INVALID_INT = -2010,/**< invalid integer */ + RS_RET_INVALID_CMD = -2011,/**< invalid command */ + RS_RET_VAL_OUT_OF_RANGE = -2012, /**< value out of range */ + RS_RET_FOPEN_FAILURE = -2013, /**< failure during fopen, for example file not found - see errno */ + RS_RET_END_OF_LINKEDLIST = -2014, /**< end of linked list, not an error, but a status */ + RS_RET_CHAIN_NOT_PERMITTED = -2015, /**< chaining (e.g. of config command handlers) not permitted */ + RS_RET_INVALID_PARAMS = -2016,/**< supplied parameters are invalid */ + RS_RET_EMPTY_LIST = -2017, /**< linked list is empty */ + RS_RET_FINISHED = -2018, /**< some opertion is finished, not an error state */ + RS_RET_INVALID_SOURCE = -2019, /**< source (address) invalid for some reason */ + RS_RET_ADDRESS_UNKNOWN = -2020, /**< an address is unknown - not necessarily an error */ + RS_RET_MALICIOUS_ENTITY = -2021, /**< there is an malicious entity involved */ + RS_RET_NO_KERNEL_LOGSRC = -2022, /**< no source for kernel logs can be obtained */ + RS_RET_TCP_SEND_ERROR = -2023, /**< error during TCP send process */ + RS_RET_GSS_SEND_ERROR = -2024, /**< error during GSS (via TCP) send process */ + RS_RET_TCP_SOCKCREATE_ERR = -2025, /**< error during creation of TCP socket */ + RS_RET_GSS_SENDINIT_ERROR = -2024, /**< error during GSS (via TCP) send initialization process */ + RS_RET_QUEUE_FULL = -2025, /**< queue is full, operation could not be completed */ + RS_RET_EOF = -2026, /**< end of file reached, not necessarily an error */ + RS_RET_IO_ERROR = -2027, /**< some kind of IO error happened */ + RS_RET_INVALID_OID = -2028, /**< invalid object ID */ + RS_RET_INVALID_HEADER = -2029, /**< invalid header */ + RS_RET_INVALID_HEADER_VERS = -2030, /**< invalid header version */ + RS_RET_INVALID_DELIMITER = -2031, /**< invalid delimiter, e.g. between params */ + RS_RET_INVALID_PROPFRAME = -2032, /**< invalid framing in serialized property */ + RS_RET_NO_PROPLINE = -2033, /**< line is not a property line */ + RS_RET_INVALID_TRAILER = -2034, /**< invalid trailer */ + RS_RET_VALUE_TOO_LOW = -2035, /**< a provided value is too low */ + RS_RET_FILE_PREFIX_MISSING = -2036, /**< a required file prefix (parameter?) is missing */ + RS_RET_INVALID_HEADER_RECTYPE = -2037, /**< invalid record type in header or invalid header */ + RS_RET_QTYPE_MISMATCH = -2038, /**< different qType when reading back a property type */ + RS_RET_NO_FILE_ACCESS = -2039, /**< covers EACCES error on file open() */ + RS_RET_FILE_NOT_FOUND = -2040, /**< file not found */ + RS_RET_TIMED_OUT = -2041, /**< timeout occured (not necessarily an error) */ + RS_RET_QSIZE_ZERO = -2042, /**< queue size is zero where this is not supported */ + RS_RET_ALREADY_STARTING = -2043, /**< something (a thread?) is already starting - not necessarily an error */ + RS_RET_NO_MORE_THREADS = -2044, /**< no more threads available, not necessarily an error */ + RS_RET_NO_FILEPREFIX = -2045, /**< file prefix is not specified where one is needed */ + RS_RET_CONFIG_ERROR = -2046, /**< there is a problem with the user-provided config settigs */ + RS_RET_OUT_OF_DESRIPTORS = -2047, /**< a descriptor table's space has been exhausted */ + RS_RET_NO_DRIVERS = -2048, /**< a required drivers missing */ + RS_RET_NO_DRIVERNAME = -2049, /**< driver name missing where one was required */ + RS_RET_EOS = -2050, /**< end of stream (of whatever) */ + RS_RET_SYNTAX_ERROR = -2051, /**< syntax error, eg. during parsing */ + RS_RET_INVALID_OCTAL_DIGIT = -2052, /**< invalid octal digit during parsing */ + RS_RET_INVALID_HEX_DIGIT = -2053, /**< invalid hex digit during parsing */ + RS_RET_INTERFACE_NOT_SUPPORTED = -2054, /**< interface not supported */ + RS_RET_OUT_OF_STACKSPACE = -2055, /**< a stack data structure is exhausted and can not be grown */ + RS_RET_STACK_EMPTY = -2056, /**< a pop was requested on a stack, but the stack was already empty */ + RS_RET_INVALID_VMOP = -2057, /**< invalid virtual machine instruction */ + RS_RET_INVALID_VAR = -2058, /**< a var_t or its content is unsuitable, eg. VARTYPE_NONE */ + RS_RET_INVALID_NUMBER = -2059, /**< number invalid during parsing */ + RS_RET_NOT_A_NUMBER = -2060, /**< e.g. conversion impossible because the string is not a number */ + RS_RET_OBJ_ALREADY_REGISTERED = -2061, /**< object (name) is already registered */ + RS_RET_OBJ_REGISTRY_OUT_OF_SPACE = -2062, /**< the object registry has run out of space */ + RS_RET_HOST_NOT_PERMITTED = -2063, /**< a host is not permitted to perform an action it requested */ + RS_RET_MODULE_LOAD_ERR = -2064, /**< module could not be loaded */ + RS_RET_MODULE_LOAD_ERR_PATHLEN = -2065, /**< module could not be loaded - path to long */ + RS_RET_MODULE_LOAD_ERR_DLOPEN = -2066, /**< module could not be loaded - problem in dlopen() */ + RS_RET_MODULE_LOAD_ERR_NO_INIT = -2067, /**< module could not be loaded - init() missing */ + RS_RET_MODULE_LOAD_ERR_INIT_FAILED = -2068, /**< module could not be loaded - init() failed */ + RS_RET_NO_SOCKET = -2069, /**< socket could not be obtained or was not provided */ + RS_RET_SMTP_ERROR = -2070, /**< error during SMTP transation */ + RS_RET_MAIL_NO_TO = -2071, /**< recipient for mail destination is missing */ + RS_RET_MAIL_NO_FROM = -2072, /**< sender for mail destination is missing */ + RS_RET_INVALID_PRI = -2073, /**< PRI value is invalid */ + + /* RainerScript error messages (range 1000.. 1999) */ + RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ + + /* some generic error/status codes */ + RS_RET_OK_DELETE_LISTENTRY = 1, /**< operation successful, but callee requested the deletion of an entry (special state) */ + RS_RET_TERMINATE_NOW = 2, /**< operation successful, function is requested to terminate (mostly used with threads) */ + RS_RET_NO_RUN = 3, /**< operation successful, but function does not like to be executed */ + RS_RET_OK = 0 /**< operation successful */ +}; +typedef enum rsRetVal_ rsRetVal; /**< friendly type for global return value */ + +/* some helpful macros to work with srRetVals. + * Be sure to call the to-be-returned variable always "iRet" and + * the function finalizer always "finalize_it". + */ +#define CHKiRet(code) if((iRet = code) != RS_RET_OK) goto finalize_it +/* macro below is to be used if we need our own handling, eg for cleanup */ +#define CHKiRet_Hdlr(code) if((iRet = code) != RS_RET_OK) +/* macro below is to handle failing malloc/calloc/strdup... which we almost always handle in the same way... */ +#define CHKmalloc(operation) if((operation) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY) +/* macro below is used in conjunction with CHKiRet_Hdlr, else use ABORT_FINALIZE */ +#define FINALIZE goto finalize_it; +#define DEFiRet BEGINfunc rsRetVal iRet = RS_RET_OK +#define RETiRet do{ ENDfuncIRet return iRet; }while(0) + +#define ABORT_FINALIZE(errCode) \ + do { \ + iRet = errCode; \ + goto finalize_it; \ + } while (0) + +/** Object ID. These are for internal checking. Each + * object is assigned a specific ID. This is contained in + * all Object structs (just like C++ RTTI). We can use + * this field to see if we have been passed a correct ID. + * Other than that, there is currently no other use for + * the object id. + */ +enum rsObjectID +{ + OIDrsFreed = -1, /**< assigned, when an object is freed. If this + * is seen during a method call, this is an + * invalid object pointer! + */ + OIDrsInvalid = 0, /**< value created by calloc(), so do not use ;) */ + /* The 0x3412 is a debug aid. It helps us find object IDs in memory + * dumps (on X86, this is 1234 in the dump ;) + * If you are on an embedded device and you would like to save space + * make them 1 byte only. + */ + OIDrsCStr = 0x34120001, + OIDrsPars = 0x34120002 +}; +typedef enum rsObjectID rsObjID; + +/* support to set object types */ +#ifdef NDEBUG +#define rsSETOBJTYPE(pObj, type) +#define rsCHECKVALIDOBJECT(x, type) +#else +#define rsSETOBJTYPE(pObj, type) pObj->OID = type; +#define rsCHECKVALIDOBJECT(x, type) {assert(x != NULL); assert(x->OID == type);} +#endif + +/** + * This macro should be used to free objects. + * It aids in interpreting dumps during debugging. + */ +#ifdef NDEBUG +#define RSFREEOBJ(x) free(x) +#else +#define RSFREEOBJ(x) {(x)->OID = OIDrsFreed; free(x);} +#endif + +/* get rid of the unhandy "unsigned char" + */ +typedef unsigned char uchar; + +/* for the time being, we do our own portability handling here. It + * looks like autotools either does not yet support checks for it, or + * I wasn't smart enough to find them ;) rgerhards, 2007-07-18 + */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* The following prototype is convenient, even though it may not be the 100% correct place.. -- rgerhards 2008-01-07 */ +void dbgprintf(char *, ...) __attribute__((format(printf, 1, 2))); + +#include "debug.h" + +#endif /* multi-include protection */ +/* vim:set ai: + */ diff --git a/runtime/srUtils.h b/runtime/srUtils.h new file mode 100644 index 00000000..81d20357 --- /dev/null +++ b/runtime/srUtils.h @@ -0,0 +1,126 @@ +/*! \file srUtils.h + * \brief General, small utilities that fit nowhere else. + * + * \author Rainer Gerhards + * \date 2003-09-09 + * Coding begun. + * + * Copyright 2003-2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef __SRUTILS_H_INCLUDED__ +#define __SRUTILS_H_INCLUDED__ 1 + + +/* syslog names */ +#ifndef LOG_MAKEPRI +# define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) +#endif +#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ +#define TABLE_NOPRI 0 /* Value to indicate no priority in f_pmask */ +#define TABLE_ALLPRI 0xFF /* Value to indicate all priorities in f_pmask */ +#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */ + +typedef struct syslogName_s { + char *c_name; + int c_val; +} syslogName_t; + +extern syslogName_t syslogPriNames[]; +extern syslogName_t syslogFacNames[]; + +/** + * A reimplementation of itoa(), as this is not available + * on all platforms. We used the chance to make an interface + * that fits us well, so it is no longer plain itoa(). + * + * This method works with the US-ASCII alphabet. If you port this + * to e.g. EBCDIC, you need to make a small adjustment. Keep in mind, + * that on the wire it MUST be US-ASCII, so basically all you need + * to do is replace the constant '0' with 0x30 ;). + * + * \param pBuf Caller-provided buffer that will receive the + * generated ASCII string. + * + * \param iLenBuf Length of the caller-provided buffer. + * + * \param iToConv The integer to be converted. + */ +rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv); + +/** + * A method to duplicate a string for which the length is known. + * Len must be the length in characters WITHOUT the trailing + * '\0' byte. + * rgerhards, 2007-07-10 + */ +unsigned char *srUtilStrDup(unsigned char *pOld, size_t len); +/** + * A method to create a directory and all its missing parents for + * a given file name. Please not that the rightmost element is + * considered to be a file name and thus NO directory is being created + * for it. + * added 2007-07-17 by rgerhards + */ +int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode, uid_t uid, gid_t gid, int bFailOnChown); +int execProg(uchar *program, int bWait, uchar *arg); +void skipWhiteSpace(uchar **pp); +rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName, + size_t lenFName, long lNum, int lNumDigits); +int getNumberDigits(long lNum); +rsRetVal timeoutComp(struct timespec *pt, long iTimeout); +long timeoutVal(struct timespec *pt); +void mutexCancelCleanup(void *arg); +void srSleep(int iSeconds, int iuSeconds); +char *rs_strerror_r(int errnum, char *buf, size_t buflen); +int decodeSyslogName(uchar *name, syslogName_t *codetab); + +/* mutex operations */ +/* some macros to cancel-safe lock a mutex (it will automatically be released + * when the thread is cancelled. This needs to be done as macros because + * pthread_cleanup_push sometimes is a macro that can not be used inside a function. + * It's a bit ugly, but works well... rgerhards, 2008-01-20 + */ +#define DEFVARS_mutex_cancelsafeLock int iCancelStateSave +#define mutex_cancelsafe_lock(mut) \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \ + d_pthread_mutex_lock(mut); \ + pthread_cleanup_push(mutexCancelCleanup, mut); \ + pthread_setcancelstate(iCancelStateSave, NULL); +#define mutex_cancelsafe_unlock(mut) pthread_cleanup_pop(1) + +/* some useful constants */ +#define MUTEX_ALREADY_LOCKED 0 +#define LOCK_MUTEX 1 +#define DEFVARS_mutexProtection\ + int iCancelStateSave; \ + int bLockedOpIsLocked=0 +#define BEGIN_MTX_PROTECTED_OPERATIONS(mut, bMustLock) \ + if(bMustLock == LOCK_MUTEX) { \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \ + d_pthread_mutex_lock(mut); \ + bLockedOpIsLocked = 1; \ + } +#define END_MTX_PROTECTED_OPERATIONS(mut) \ + if(bLockedOpIsLocked) { \ + d_pthread_mutex_unlock(mut); \ + pthread_setcancelstate(iCancelStateSave, NULL); \ + } +#endif diff --git a/runtime/srutils.c b/runtime/srutils.c new file mode 100644 index 00000000..93908767 --- /dev/null +++ b/runtime/srutils.c @@ -0,0 +1,509 @@ +/**\file srUtils.c + * \brief General utilties that fit nowhere else. + * + * The namespace for this file is "srUtil". + * + * \author Rainer Gerhards + * \date 2003-09-09 + * Coding begun. + * + * Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include "rsyslog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TRUE 1 +#define FALSE 0 +#include "srUtils.h" +#include "syslogd.h" +#include "obj.h" + + +/* here we host some syslog specific names. There currently is no better place + * to do it, but over here is also not ideal... -- rgerhards, 2008-02-14 + * rgerhards, 2008-04-16: note in LGPL move: the code tables below exist in + * the same way in BSD, so it is not a problem to move them from GPLv3 to LGPL. + */ +syslogName_t syslogPriNames[] = { + {"alert", LOG_ALERT}, + {"crit", LOG_CRIT}, + {"debug", LOG_DEBUG}, + {"emerg", LOG_EMERG}, + {"err", LOG_ERR}, + {"error", LOG_ERR}, /* DEPRECATED */ + {"info", LOG_INFO}, + {"none", INTERNAL_NOPRI}, /* INTERNAL */ + {"notice", LOG_NOTICE}, + {"panic", LOG_EMERG}, /* DEPRECATED */ + {"warn", LOG_WARNING}, /* DEPRECATED */ + {"warning", LOG_WARNING}, + {"*", TABLE_ALLPRI}, + {NULL, -1} +}; + +#ifndef LOG_AUTHPRIV +# define LOG_AUTHPRIV LOG_AUTH +#endif +syslogName_t syslogFacNames[] = { + {"auth", LOG_AUTH}, + {"authpriv", LOG_AUTHPRIV}, + {"cron", LOG_CRON}, + {"daemon", LOG_DAEMON}, + {"kern", LOG_KERN}, + {"lpr", LOG_LPR}, + {"mail", LOG_MAIL}, + {"mark", LOG_MARK}, /* INTERNAL */ + {"news", LOG_NEWS}, + {"security", LOG_AUTH}, /* DEPRECATED */ + {"syslog", LOG_SYSLOG}, + {"user", LOG_USER}, + {"uucp", LOG_UUCP}, +#if defined(LOG_FTP) + {"ftp", LOG_FTP}, +#endif + {"local0", LOG_LOCAL0}, + {"local1", LOG_LOCAL1}, + {"local2", LOG_LOCAL2}, + {"local3", LOG_LOCAL3}, + {"local4", LOG_LOCAL4}, + {"local5", LOG_LOCAL5}, + {"local6", LOG_LOCAL6}, + {"local7", LOG_LOCAL7}, + {NULL, -1}, +}; + +/* ################################################################# * + * private members * + * ################################################################# */ + +/* As this is not a "real" object, there won't be any private + * members in this file. + */ + +/* ################################################################# * + * public members * + * ################################################################# */ + +rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv) +{ + int i; + int bIsNegative; + char szBuf[64]; /* sufficiently large for my lifespan and those of my children... ;) */ + + assert(pBuf != NULL); + assert(iLenBuf > 1); /* This is actually an app error and as thus checked for... */ + + if(iToConv < 0) + { + bIsNegative = TRUE; + iToConv *= -1; + } + else + bIsNegative = FALSE; + + /* first generate a string with the digits in the reverse direction */ + i = 0; + do + { + szBuf[i++] = iToConv % 10 + '0'; + iToConv /= 10; + } while(iToConv > 0); /* warning: do...while()! */ + --i; /* undo last increment - we were pointing at NEXT location */ + + /* make sure we are within bounds... */ + if(i + 2 > iLenBuf) /* +2 because: a) i starts at zero! b) the \0 byte */ + return RS_RET_PROVIDED_BUFFER_TOO_SMALL; + + /* then move it to the right direction... */ + if(bIsNegative == TRUE) + *pBuf++ = '-'; + while(i >= 0) + *pBuf++ = szBuf[i--]; + *pBuf = '\0'; /* terminate it!!! */ + + return RS_RET_OK; +} + +uchar *srUtilStrDup(uchar *pOld, size_t len) +{ + uchar *pNew; + + assert(pOld != NULL); + + if((pNew = malloc(len + 1)) != NULL) + memcpy(pNew, pOld, len + 1); + + return pNew; +} + + +/* creates a path recursively + * Return 0 on success, -1 otherwise. On failure, errno + * hold the last OS error. + * Param "mode" holds the mode that all non-existing directories + * are to be created with. + */ +int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode, + uid_t uid, gid_t gid, int bFailOnChownFail) +{ + uchar *p; + uchar *pszWork; + size_t len; + int bErr = 0; + + assert(szFile != NULL); + assert(lenFile > 0); + + len = lenFile + 1; /* add one for '\0'-byte */ + if((pszWork = malloc(sizeof(uchar) * len)) == NULL) + return -1; + memcpy(pszWork, szFile, len); + for(p = pszWork+1 ; *p ; p++) + if(*p == '/') { + /* temporarily terminate string, create dir and go on */ + *p = '\0'; + if(access((char*)pszWork, F_OK)) { + if(mkdir((char*)pszWork, mode) == 0) { + if(uid != (uid_t) -1 || gid != (gid_t) -1) { + /* we need to set owner/group */ + if(chown((char*)pszWork, uid, gid) != 0) + if(bFailOnChownFail) + bErr = 1; + /* silently ignore if configured + * to do so. + */ + } + } else + bErr = 1; + if(bErr) { + int eSave = errno; + free(pszWork); + errno = eSave; + return -1; + } + } + *p = '/'; + } + free(pszWork); + return 0; +} + + +/* execute a program with a single argument + * returns child pid if everything ok, 0 on failure. if + * it fails, errno is set. if it fails after the fork(), the caller + * can not be notfied for obvious reasons. if bwait is set to 1, + * the code waits until the child terminates - that potentially takes + * a lot of time. + * implemented 2007-07-20 rgerhards + */ +int execProg(uchar *program, int bWait, uchar *arg) +{ + int pid; + int sig; + struct sigaction sigAct; + + dbgprintf("exec program '%s' with param '%s'\n", program, arg); + pid = fork(); + if (pid < 0) { + return 0; + } + + if(pid) { /* Parent */ + if(bWait) + if(waitpid(pid, NULL, 0) == -1) + if(errno != ECHILD) { + /* we do not use logerror(), because + * that might bring us into an endless + * loop. At some time, we may + * reconsider this behaviour. + */ + dbgprintf("could not wait on child after executing '%s'", + (char*)program); + } + return pid; + } + /* Child */ + alarm(0); /* create a clean environment before we exec the real child */ + + memset(&sigAct, 0, sizeof(sigAct)); + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = SIG_DFL; + + for(sig = 1 ; sig < NSIG ; ++sig) + sigaction(sig, &sigAct, NULL); + + execlp((char*)program, (char*) program, (char*)arg, NULL); + /* In the long term, it's a good idea to implement some enhanced error + * checking here. However, it can not easily be done. For starters, we + * may run into endless loops if we log to syslog. The next problem is + * that output is typically not seen by the user. For the time being, + * we use no error reporting, which is quite consitent with the old + * system() way of doing things. rgerhards, 2007-07-20 + */ + perror("exec"); + exit(1); /* not much we can do in this case */ +} + + +/* skip over whitespace in a standard C string. The + * provided pointer is advanced to the first non-whitespace + * charater or the \0 byte, if there is none. It is never + * moved past the \0. + */ +void skipWhiteSpace(uchar **pp) +{ + register uchar *p; + + assert(pp != NULL); + assert(*pp != NULL); + + p = *pp; + while(*p && isspace((int) *p)) + ++p; + *pp = p; +} + + +/* generate a file name from four parts: + * /. + * If number is negative, it is not used. If any of the strings is + * NULL, an empty string is used instead. Length must be provided. + * lNumDigits is the minimum number of digits that lNum should have. This + * is to pretty-print the file name, e.g. lNum = 3, lNumDigits= 4 will + * result in "0003" being used inside the file name. Set lNumDigits to 0 + * to use as few space as possible. + * rgerhards, 2008-01-03 + */ +rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName, + size_t lenFName, long lNum, int lNumDigits) +{ + DEFiRet; + uchar *pName; + uchar *pNameWork; + size_t lenName; + uchar szBuf[128]; /* buffer for number */ + char szFmtBuf[32]; /* buffer for snprintf format */ + size_t lenBuf; + + if(lNum < 0) { + szBuf[0] = '\0'; + lenBuf = 0; + } else { + if(lNumDigits > 0) { + snprintf(szFmtBuf, sizeof(szFmtBuf), ".%%0%dld", lNumDigits); + lenBuf = snprintf((char*)szBuf, sizeof(szBuf), szFmtBuf, lNum); + } else + lenBuf = snprintf((char*)szBuf, sizeof(szBuf), ".%ld", lNum); + } + + lenName = lenDirName + 1 + lenFName + lenBuf + 1; /* last +1 for \0 char! */ + if((pName = malloc(sizeof(uchar) * lenName)) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + /* got memory, now construct string */ + memcpy(pName, pDirName, lenDirName); + pNameWork = pName + lenDirName; + *pNameWork++ = '/'; + memcpy(pNameWork, pFName, lenFName); + pNameWork += lenFName; + if(lenBuf > 0) { + memcpy(pNameWork, szBuf, lenBuf); + pNameWork += lenBuf; + } + *pNameWork = '\0'; + + *ppName = pName; + +finalize_it: + RETiRet; +} + +/* get the number of digits required to represent a given number. We use an + * iterative approach as we do not like to draw in the floating point + * library just for log(). -- rgerhards, 2008-01-10 + */ +int getNumberDigits(long lNum) +{ + int iDig; + + if(lNum == 0) + iDig = 1; + else + for(iDig = 0 ; lNum != 0 ; ++iDig) + lNum /= 10; + + return iDig; +} + + +/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait() + * rgerhards, 2008-01-14 + */ +rsRetVal +timeoutComp(struct timespec *pt, long iTimeout) +{ + assert(pt != NULL); + /* compute timeout */ + clock_gettime(CLOCK_REALTIME, pt); + pt->tv_nsec += (iTimeout % 1000) * 1000000; /* think INTEGER arithmetic! */ + if(pt->tv_nsec > 999999999) { /* overrun? */ + pt->tv_nsec -= 1000000000; + } + pt->tv_sec += iTimeout / 1000; + return RS_RET_OK; /* so far, this is static... */ +} + + +/* This function is kind of the reverse of timeoutComp() - it takes an absolute + * timeout value and computes how far this is in the future. If the value is already + * in the past, 0 is returned. The return value is in ms. + * rgerhards, 2008-01-25 + */ +long +timeoutVal(struct timespec *pt) +{ + struct timespec t; + long iTimeout; + + assert(pt != NULL); + /* compute timeout */ + clock_gettime(CLOCK_REALTIME, &t); + iTimeout = (pt->tv_nsec - t.tv_nsec) / 1000000; + iTimeout += (pt->tv_sec - t.tv_sec) * 1000; + + if(iTimeout < 0) + iTimeout = 0; + + return iTimeout; +} + + +/* cancellation cleanup handler - frees provided mutex + * rgerhards, 2008-01-14 + */ +void +mutexCancelCleanup(void *arg) +{ + BEGINfunc + assert(arg != NULL); + d_pthread_mutex_unlock((pthread_mutex_t*) arg); + ENDfunc +} + + +/* rsSleep() - a fairly portable way to to sleep. It + * will wake up when + * a) the wake-time is over + * rgerhards, 2008-01-28 + */ +void +srSleep(int iSeconds, int iuSeconds) +{ + struct timeval tvSelectTimeout; + + BEGINfunc + tvSelectTimeout.tv_sec = iSeconds; + tvSelectTimeout.tv_usec = iuSeconds; /* micro seconds */ + select(0, NULL, NULL, NULL, &tvSelectTimeout); + ENDfunc +} + + +/* From varmojfekoj's mail on why he provided rs_strerror_r(): + * There are two problems with strerror_r(): + * I see you've rewritten some of the code which calls it to use only + * the supplied buffer; unfortunately the GNU implementation sometimes + * doesn't use the buffer at all and returns a pointer to some + * immutable string instead, as noted in the man page. + * + * The other problem is that on some systems strerror_r() has a return + * type of int. + * + * So I've written a wrapper function rs_strerror_r(), which should + * take care of all this and be used instead. + * + * Added 2008-01-30 + */ +char *rs_strerror_r(int errnum, char *buf, size_t buflen) { +#ifdef __hpux + char *pszErr; + pszErr = strerror(errnum); + snprintf(buf, buflen, "%s", pszErr); +#else +# ifdef STRERROR_R_CHAR_P + char *p = strerror_r(errnum, buf, buflen); + if (p != buf) { + strncpy(buf, p, buflen); + buf[buflen - 1] = '\0'; + } +# else + strerror_r(errnum, buf, buflen); +# endif +#endif /* #ifdef __hpux */ + return buf; +} + + +/* Decode a symbolic name to a numeric value + */ +int decodeSyslogName(uchar *name, syslogName_t *codetab) +{ + register syslogName_t *c; + register uchar *p; + uchar buf[80]; + + ASSERT(name != NULL); + ASSERT(codetab != NULL); + + dbgprintf("symbolic name: %s", name); + if (isdigit((int) *name)) + { + dbgprintf("\n"); + return (atoi((char*) name)); + } + strncpy((char*) buf, (char*) name, 79); + for (p = buf; *p; p++) + if (isupper((int) *p)) + *p = tolower((int) *p); + for (c = codetab; c->c_name; c++) + if (!strcmp((char*) buf, (char*) c->c_name)) + { + dbgprintf(" ==> %d\n", c->c_val); + return (c->c_val); + } + return (-1); +} + + +/* vim:set ai: + */ diff --git a/runtime/stringbuf.c b/runtime/stringbuf.c new file mode 100644 index 00000000..93d1e1ef --- /dev/null +++ b/runtime/stringbuf.c @@ -0,0 +1,1080 @@ +/* This is the byte-counted string class for rsyslog. It is a replacement + * for classical \0 terminated string functions. We introduce it in + * the hope it will make the program more secure, obtain some performance + * and, most importantly, lay they foundation for syslog-protocol, which + * requires strings to be able to handle embedded \0 characters. + * Please see syslogd.c for license information. + * All functions in this "class" start with rsCStr (rsyslog Counted String). + * begun 2005-09-07 rgerhards + * + * Copyright (C) 2007-2008 by Rainer Gerhards and Adiscon GmbH + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include "rsyslog.h" +#include "stringbuf.h" +#include "srUtils.h" +#include "regexp.h" +#include "obj.h" + + +/* ################################################################# * + * private members * + * ################################################################# */ + +/* static data */ +DEFobjCurrIf(obj) +DEFobjCurrIf(regexp) + +/* ################################################################# * + * public members * + * ################################################################# */ + + +rsRetVal rsCStrConstruct(cstr_t **ppThis) +{ + DEFiRet; + cstr_t *pThis; + + ASSERT(ppThis != NULL); + + if((pThis = (cstr_t*) calloc(1, sizeof(cstr_t))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + rsSETOBJTYPE(pThis, OIDrsCStr); + pThis->pBuf = NULL; + pThis->pszBuf = NULL; + pThis->iBufSize = 0; + pThis->iStrLen = 0; + pThis->iAllocIncrement = RS_STRINGBUF_ALLOC_INCREMENT; + *ppThis = pThis; + +finalize_it: + RETiRet; +} + + +/* construct from sz string + * rgerhards 2005-09-15 + */ +rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz) +{ + DEFiRet; + cstr_t *pThis; + + assert(ppThis != NULL); + + CHKiRet(rsCStrConstruct(&pThis)); + + pThis->iBufSize = pThis->iStrLen = strlen((char*)(char *) sz); + if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { + RSFREEOBJ(pThis); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + /* we do NOT need to copy the \0! */ + memcpy(pThis->pBuf, sz, pThis->iStrLen); + + *ppThis = pThis; + +finalize_it: + RETiRet; +} + +/* construct from CStr object. only the counted string is + * copied, not the szString. + * rgerhards 2005-10-18 + */ +rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom) +{ + DEFiRet; + cstr_t *pThis; + + assert(ppThis != NULL); + rsCHECKVALIDOBJECT(pFrom, OIDrsCStr); + + CHKiRet(rsCStrConstruct(&pThis)); + + pThis->iBufSize = pThis->iStrLen = pFrom->iStrLen; + if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { + RSFREEOBJ(pThis); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + /* copy properties */ + memcpy(pThis->pBuf, pFrom->pBuf, pThis->iStrLen); + + *ppThis = pThis; +finalize_it: + RETiRet; +} + + +void rsCStrDestruct(cstr_t **ppThis) +{ + cstr_t *pThis = *ppThis; + + /* rgerhards 2005-10-19: The free of pBuf was contained in conditional compilation. + * The code was only compiled if STRINGBUF_TRIM_ALLOCSIZE was set to 1. I honestly + * do not know why it was so, I think it was an artifact. Anyhow, I have changed this + * now. Should there any issue occur, this comment hopefully will shed some light + * on what happened. I re-verified, and this function has never before been called + * by anyone. So changing it can have no impact for obvious reasons... + * + * rgerhards, 2008-02-20: I changed the interface to the new calling conventions, where + * the destructor receives a pointer to the object, so that it can set it to NULL. + */ + if(pThis->pBuf != NULL) { + free(pThis->pBuf); + } + + if(pThis->pszBuf != NULL) { + free(pThis->pszBuf); + } + + RSFREEOBJ(pThis); + *ppThis = NULL; +} + + +/* extend the string buffer if its size is insufficient. + * Param iMinNeeded is the minumum free space needed. If it is larger + * than the default alloc increment, space for at least this amount is + * allocated. In practice, a bit more is allocated because we envision that + * some more characters may be added after these. + * rgerhards, 2008-01-07 + */ +static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded) +{ + DEFiRet; + uchar *pNewBuf; + size_t iNewSize; + + /* first compute the new size needed */ + if(iMinNeeded > pThis->iAllocIncrement) { + /* we allocate "n" iAllocIncrements. Usually, that should + * leave some room after the absolutely needed one. It also + * reduces memory fragmentation. Note that all of this are + * integer operations (very important to understand what is + * going on)! Parenthesis are for better readibility. + */ + iNewSize = ((iMinNeeded / pThis->iAllocIncrement) + 1) * pThis->iAllocIncrement; + } else { + iNewSize = pThis->iBufSize + pThis->iAllocIncrement; + } + iNewSize += pThis->iBufSize; /* add current size */ + + /* and then allocate and copy over */ + /* DEV debugging only: dbgprintf("extending string buffer, old %d, new %d\n", pThis->iBufSize, iNewSize); */ + if((pNewBuf = (uchar*) malloc(iNewSize * sizeof(uchar))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + memcpy(pNewBuf, pThis->pBuf, pThis->iBufSize); + pThis->iBufSize = iNewSize; + if(pThis->pBuf != NULL) { + free(pThis->pBuf); + } + pThis->pBuf = pNewBuf; + +finalize_it: + RETiRet; +} + + +/* append a string of known length. In this case, we make sure we do at most + * one additional memory allocation. + * I optimized this function to use memcpy(), among others. Consider it a + * rewrite (which may be good to know in case of bugs) -- rgerhards, 2008-01-07 + */ +rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen) +{ + DEFiRet; + + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + assert(psz != NULL); + + /* does the string fit? */ + if(pThis->iStrLen + iStrLen > pThis->iBufSize) { + CHKiRet(rsCStrExtendBuf(pThis, iStrLen)); /* need more memory! */ + } + + /* ok, now we always have sufficient continues memory to do a memcpy() */ + memcpy(pThis->pBuf + pThis->iStrLen, psz, iStrLen); + pThis->iStrLen += iStrLen; + +finalize_it: + RETiRet; +} + + +/* changed to be a wrapper to rsCStrAppendStrWithLen() so that + * we can save some time when we have the length but do not + * need to change existing code. + * rgerhards, 2007-07-03 + */ +rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz) +{ + return rsCStrAppendStrWithLen(pThis, psz, strlen((char*) psz)); +} + + +/* append the contents of one cstr_t object to another + * rgerhards, 2008-02-25 + */ +rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend) +{ + return rsCStrAppendStrWithLen(pThis, pstrAppend->pBuf, pstrAppend->iStrLen); +} + + +rsRetVal rsCStrAppendInt(cstr_t *pThis, long i) +{ + DEFiRet; + uchar szBuf[32]; + + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), i)); + + iRet = rsCStrAppendStr(pThis, szBuf); +finalize_it: + RETiRet; +} + + +rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c) +{ + DEFiRet; + + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + if(pThis->iStrLen >= pThis->iBufSize) { + CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */ + } + + /* ok, when we reach this, we have sufficient memory */ + *(pThis->pBuf + pThis->iStrLen++) = c; + + /* check if we need to invalidate an sz representation! */ + if(pThis->pszBuf != NULL) { + free(pThis->pszBuf); + pThis->pszBuf = NULL; + } + +finalize_it: + RETiRet; +} + + +/* Sets the string object to the classigal sz-string provided. + * Any previously stored vlaue is discarded. If a NULL pointer + * the the new value (pszNew) is provided, an empty string is + * created (this is NOT an error!). Property iAllocIncrement is + * not modified by this function. + * rgerhards, 2005-10-18 + */ +rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + if(pThis->pBuf != NULL) + free(pThis->pBuf); + if(pThis->pszBuf != NULL) + free(pThis->pszBuf); + if(pszNew == NULL) { + pThis->iStrLen = 0; + pThis->iBufSize = 0; + pThis->pBuf = NULL; + pThis->pszBuf = NULL; + } else { + pThis->iStrLen = strlen((char*)pszNew); + pThis->iBufSize = pThis->iStrLen; + pThis->pszBuf = NULL; + /* iAllocIncrement is NOT modified! */ + + /* now save the new value */ + if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { + RSFREEOBJ(pThis); + return RS_RET_OUT_OF_MEMORY; + } + + /* we do NOT need to copy the \0! */ + memcpy(pThis->pBuf, pszNew, pThis->iStrLen); + } + + return RS_RET_OK; +} + +/* Converts the CStr object to a classical sz string and returns that. + * Same restrictions as in rsCStrGetSzStr() applies (see there!). This + * function here guarantees that a valid string is returned, even if + * the CStr object currently holds a NULL pointer string buffer. If so, + * "" is returned. + * rgerhards 2005-10-19 + * WARNING: The returned pointer MUST NOT be freed, as it may be + * obtained from that constant memory pool (in case of NULL!) + */ +uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + if(pThis->pBuf == NULL) + return (uchar*) ""; + else + return rsCStrGetSzStr(pThis); +} + + +/* Converts the CStr object to a classical zero-terminated C string + * and returns that string. The caller must not free it and must not + * destroy the CStr object as long as the ascii string is used. + * This function may return NULL, if the string is currently NULL. This + * is a feature, not a bug. If you need non-NULL in any case, use + * rsCStrGetSzStrNoNULL() instead. + * rgerhards, 2005-09-15 + */ +uchar* rsCStrGetSzStr(cstr_t *pThis) +{ + size_t i; + + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + if(pThis->pBuf != NULL) + if(pThis->pszBuf == NULL) { + /* we do not yet have a usable sz version - so create it... */ + if((pThis->pszBuf = malloc((pThis->iStrLen + 1) * sizeof(uchar))) == NULL) { + /* TODO: think about what to do - so far, I have no bright + * idea... rgerhards 2005-09-07 + */ + } + else { /* we can create the sz String */ + /* now copy it while doing a sanity check. The string might contain a + * \0 byte. There is no way how a sz string can handle this. For + * the time being, we simply replace it with space - something that + * could definitely be improved (TODO). + * 2005-09-15 rgerhards + */ + for(i = 0 ; i < pThis->iStrLen ; ++i) { + if(pThis->pBuf[i] == '\0') + pThis->pszBuf[i] = ' '; + else + pThis->pszBuf[i] = pThis->pBuf[i]; + } + /* write terminator... */ + pThis->pszBuf[i] = '\0'; + } + } + + return(pThis->pszBuf); +} + + +/* Converts the CStr object to a classical zero-terminated C string, + * returns that string and destroys the CStr object. The returned string + * MUST be freed by the caller. The function might return NULL if + * no memory can be allocated. + * + * TODO: + * This function should at some time become special. The base idea is to + * add one extra byte to the end of the regular buffer, so that we can + * convert it to an szString without the need to copy. The extra memory + * footprint is not hefty, but the performance gain is potentially large. + * To get it done now, I am not doing the optimiziation right now. + * rgerhards, 2005-09-07 + * + * rgerhards, 2007-09-04: I have changed the interface of this function. It now + * returns an rsRetVal, so that we can communicate back if we have an error. + * Using the standard method is much better than returning NULL. Secondly, NULL + * was not actually an error - it was in indication if the string was empty. + * This was needed in some parts of the code, in others not. I have now added + * a second parameter to specify what the caller needs. I hope these changes + * will make it less likely that the function is called incorrectly, what + * previously happend quite often and was the cause of a number of program + * aborts. So the parameters are now: + * pointer to the object, pointer to string-pointer to receive string and + * bRetNULL: 0 - must not return NULL on empty string, return "" in that + * case, 1 - return NULL instead of an empty string. + * PLEASE NOTE: the caller must free the memory returned in ppSz in any case + * (except, of course, if it is NULL). + */ +rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL) +{ + DEFiRet; + uchar* pRetBuf; + + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + assert(ppSz != NULL); + assert(bRetNULL == 0 || bRetNULL == 1); + + if(pThis->pBuf == NULL) { + if(bRetNULL == 0) { + if((pRetBuf = malloc(sizeof(uchar))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + *pRetBuf = '\0'; + } else { + pRetBuf = NULL; + } + } else + pRetBuf = rsCStrGetSzStr(pThis); + + *ppSz = pRetBuf; + +finalize_it: + /* We got it, now free the object ourselfs. Please note + * that we can NOT use the rsCStrDestruct function as it would + * also free the sz String buffer, which we pass on to the user. + */ + if(pThis->pBuf != NULL) + free(pThis->pBuf); + RSFREEOBJ(pThis); + + RETiRet; +} + + +#if STRINGBUF_TRIM_ALLOCSIZE == 1 + /* Only in this mode, we need to trim the string. To do + * so, we must allocate a new buffer of the exact + * string size, and then copy the old one over. + */ + /* WARNING + * STRINGBUF_TRIM_ALLOCSIZE can, in theory, be used to trim + * memory buffers. This part of the code was inherited from + * liblogging (where it is used in a different context) but + * never put to use in rsyslog. The reason is that it is hardly + * imaginable where the extra performance cost is worth the save + * in memory alloc. Then Anders Blomdel rightfully pointed out that + * the code does not work at all - and nobody even know that it + * probably shouldn't. Rather than removing, I deciced to somewhat + * fix the code, so that this feature may be enabled if somebody + * really has a need for it. Be warned, however, that I NEVER + * tested the fix. So if you intend to use this feature, you must + * do full testing before you rely on it. -- rgerhards, 2008-02-12 + */ +rsRetVal rsCStrFinish(cstr_t __attribute__((unused)) *pThis) +{ + DEFiRet; + uchar* pBuf; + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + if((pBuf = malloc((pThis->iStrLen) * sizeof(uchar))) == NULL) + { /* OK, in this case we use the previous buffer. At least + * we have it ;) + */ + } + else + { /* got the new buffer, so let's use it */ + memcpy(pBuf, pThis->pBuf, pThis->iStrLen); + pThis->pBuf = pBuf; + } + + RETiRet; +} +#endif /* #if STRINGBUF_TRIM_ALLOCSIZE == 1 */ + + +void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + assert(iNewIncrement > 0); + + pThis->iAllocIncrement = iNewIncrement; +} + + +/* return the length of the current string + * 2005-09-09 rgerhards + * Please note: this is only a function in a debug build. + * For release builds, it is a macro defined in stringbuf.h. + * This is due to performance reasons. + */ +#ifndef NDEBUG +int rsCStrLen(cstr_t *pThis) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + return(pThis->iStrLen); +} +#endif + +/* Truncate characters from the end of the string. + * rgerhards 2005-09-15 + */ +rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + if(pThis->iStrLen < nTrunc) + return RS_TRUNCAT_TOO_LARGE; + + pThis->iStrLen -= nTrunc; + + if(pThis->pszBuf != NULL) { + /* in this case, we adjust the psz representation + * by writing a new \0 terminator - this is by far + * the fastest way and outweights the additional memory + * required. 2005-9-19 rgerhards. + */ + pThis->pszBuf[pThis->iStrLen] = '\0'; + } + + return RS_RET_OK; +} + +/* Trim trailing whitespace from a given string + */ +rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis) +{ + register int i; + register uchar *pC; + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + i = pThis->iStrLen; + pC = pThis->pBuf + i - 1; + while(i > 0 && isspace((int)*pC)) { + --pC; + --i; + } + /* i now is the new string length! */ + pThis->iStrLen = i; + + return RS_RET_OK; +} + +/* compare two string objects - works like strcmp(), but operates + * on CStr objects. Please note that this version here is + * faster in the majority of cases, simply because it can + * rely on StrLen. + * rgerhards 2005-09-19 + * fixed bug, in which only the last byte was actually compared + * in equal-size strings. + * rgerhards, 2005-09-26 + */ +int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2) +{ + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + rsCHECKVALIDOBJECT(pCS2, OIDrsCStr); + if(pCS1->iStrLen == pCS2->iStrLen) + if(pCS1->iStrLen == 0) + return 0; /* zero-sized string are equal ;) */ + else { /* we now have two non-empty strings of equal + * length, so we need to actually check if they + * are equal. + */ + register size_t i; + for(i = 0 ; i < pCS1->iStrLen ; ++i) { + if(pCS1->pBuf[i] != pCS2->pBuf[i]) + return pCS1->pBuf[i] - pCS2->pBuf[i]; + } + /* if we arrive here, the strings are equal */ + return 0; + } + else + return pCS1->iStrLen - pCS2->iStrLen; +} + + +/* check if a sz-type string starts with a CStr object. This function + * is initially written to support the "startswith" property-filter + * comparison operation. Maybe it also has other needs. + * This functions is modelled after the strcmp() series, thus a + * return value of 0 indicates that the string starts with the + * sequence while -1 indicates it does not! + * rgerhards 2005-10-19 + */ +int rsCStrSzStrStartsWithCStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) +{ + register int i; + int iMax; + + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(psz != NULL); + assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ + if(iLenSz >= pCS1->iStrLen) { + /* we need to checkusing pCS1->iStrLen charactes at maximum, thus + * we move it to iMax. + */ + iMax = pCS1->iStrLen; + if(iMax == 0) + return 0; /* yes, it starts with a zero-sized string ;) */ + else { /* we now have something to compare, so let's do it... */ + for(i = 0 ; i < iMax ; ++i) { + if(psz[i] != pCS1->pBuf[i]) + return psz[i] - pCS1->pBuf[i]; + } + /* if we arrive here, the string actually starts with pCS1 */ + return 0; + } + } + else + return -1; /* pCS1 is less then psz */ +} + + +/* check if a CStr object starts with a sz-type string. + * This functions is modelled after the strcmp() series, thus a + * return value of 0 indicates that the string starts with the + * sequence while -1 indicates it does not! + * rgerhards 2005-09-26 + */ +int rsCStrStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) +{ + register size_t i; + + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(psz != NULL); + assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ + if(pCS1->iStrLen >= iLenSz) { + /* we are using iLenSz below, because we need to check + * iLenSz characters at maximum (start with!) + */ + if(iLenSz == 0) + return 0; /* yes, it starts with a zero-sized string ;) */ + else { /* we now have something to compare, so let's do it... */ + for(i = 0 ; i < iLenSz ; ++i) { + if(pCS1->pBuf[i] != psz[i]) + return pCS1->pBuf[i] - psz[i]; + } + /* if we arrive here, the string actually starts with psz */ + return 0; + } + } + else + return -1; /* pCS1 is less then psz */ +} + + +/* The same as rsCStrStartsWithSzStr(), but does a case-insensitive + * comparison. TODO: consolidate the two. + * rgerhards 2008-02-28 + */ +int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) +{ + register size_t i; + + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(psz != NULL); + assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ + if(pCS1->iStrLen >= iLenSz) { + /* we are using iLenSz below, because we need to check + * iLenSz characters at maximum (start with!) + */ + if(iLenSz == 0) + return 0; /* yes, it starts with a zero-sized string ;) */ + else { /* we now have something to compare, so let's do it... */ + for(i = 0 ; i < iLenSz ; ++i) { + if(tolower(pCS1->pBuf[i]) != tolower(psz[i])) + return tolower(pCS1->pBuf[i]) - tolower(psz[i]); + } + /* if we arrive here, the string actually starts with psz */ + return 0; + } + } + else + return -1; /* pCS1 is less then psz */ +} + +/* check if a CStr object matches a regex. + * msamia@redhat.com 2007-07-12 + * @return returns 0 if matched + * bug: doesn't work for CStr containing \0 + * rgerhards, 2007-07-16: bug is no real bug, because rsyslogd ensures there + * never is a \0 *inside* a property string. + * Note that the function returns -1 if regexp functionality is not available. + * TODO: change calling interface! -- rgerhards, 2008-03-07 + */ +int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz) +{ + regex_t preq; + int ret; + + BEGINfunc + + if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) { + regexp.regcomp(&preq, (char*) rsCStrGetSzStr(pCS1), 0); + ret = regexp.regexec(&preq, (char*) psz, 0, NULL, 0); + regexp.regfree(&preq); + } else { + ret = 1; /* simulate "not found" */ + } + + ENDfunc + return ret; +} + + +/* compare a rsCStr object with a classical sz string. This function + * is almost identical to rsCStrZsStrCmp(), but it also takes an offset + * to the CStr object from where the comparison is to start. + * I have thought quite a while if it really makes sense to more or + * less duplicate the code. After all, if you call it with an offset of + * zero, the functionality is exactly the same. So it looks natural to + * just have a single function. However, supporting the offset requires + * some (few) additional integer operations. While they are few, they + * happen at places in the code that is run very frequently. All in all, + * I have opted for performance and thus duplicated the code. I hope + * this is a good, or at least acceptable, compromise. + * rgerhards, 2005-09-26 + * This function also has an offset-pointer which allows to + * specify *where* the compare operation should begin in + * the CStr. If everything is to be compared, it must be set + * to 0. If some leading bytes are to be skipped, it must be set + * to the first index that is to be compared. It must not be + * set higher than the string length (this is considered a + * program bug and will lead to unpredictable results and program aborts). + * rgerhards 2005-09-26 + */ +int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz) +{ + BEGINfunc + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(iOffset < pCS1->iStrLen); + assert(psz != NULL); + assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ + if((pCS1->iStrLen - iOffset) == iLenSz) { + /* we are using iLenSz below, because the lengths + * are equal and iLenSz is faster to access + */ + if(iLenSz == 0) { + return 0; /* zero-sized strings are equal ;) */ + ENDfunc + } else { /* we now have two non-empty strings of equal + * length, so we need to actually check if they + * are equal. + */ + register size_t i; + for(i = 0 ; i < iLenSz ; ++i) { + if(pCS1->pBuf[i+iOffset] != psz[i]) + return pCS1->pBuf[i+iOffset] - psz[i]; + } + /* if we arrive here, the strings are equal */ + return 0; + ENDfunc + } + } + else { + return pCS1->iStrLen - iOffset - iLenSz; + ENDfunc + } +} + + +/* Converts a string to a number. If the string dos not contain a number, + * RS_RET_NOT_A_NUMBER is returned and the contents of pNumber is undefined. + * If all goes well, pNumber contains the number that the string was converted + * to. + */ +rsRetVal +rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber) +{ + DEFiRet; + number_t n; + int bIsNegative; + size_t i; + + ASSERT(pStr != NULL); + ASSERT(pNumber != NULL); + + if(pStr->iStrLen == 0) { + /* can be converted to 0! (by convention) */ + pNumber = 0; + FINALIZE; + } + + /* first skip whitespace (if present) */ + for(i = 0 ; i < pStr->iStrLen && isspace(pStr->pBuf[i]) ; ++i) { + /*DO NOTHING*/ + } + + /* we have a string, so let's check its syntax */ + if(pStr->pBuf[i] == '+') { + ++i; /* skip that char */ + bIsNegative = 0; + } else if(pStr->pBuf[0] == '-') { + ++i; /* skip that char */ + bIsNegative = 1; + } else { + bIsNegative = 0; + } + + /* TODO: octal? hex? */ + n = 0; + while(i < pStr->iStrLen && isdigit(pStr->pBuf[i])) { + n = n * 10 + pStr->pBuf[i] * 10; + ++i; + } + + if(i < pStr->iStrLen) /* non-digits before end of string? */ + ABORT_FINALIZE(RS_RET_NOT_A_NUMBER); + + if(bIsNegative) + n *= -1; + + /* we got it, so return the number */ + *pNumber = n; + +finalize_it: + RETiRet; +} + + +/* Converts a string to a boolen. First tries to convert to a number. If + * that succeeds, we are done (number is then used as boolean value). If + * that fails, we look if the string is "yes" or "true". If so, a value + * of 1 is returned. In all other cases, a value of 0 is returned. Please + * note that we do not have a specific boolean type, so we return a number. + * so, these are + * RS_RET_NOT_A_NUMBER is returned and the contents of pNumber is undefined. + * If all goes well, pNumber contains the number that the string was converted + * to. + */ +rsRetVal +rsCStrConvertToBool(cstr_t *pStr, number_t *pBool) +{ + DEFiRet; + + ASSERT(pStr != NULL); + ASSERT(pBool != NULL); + + iRet = rsCStrConvertToNumber(pStr, pBool); + + if(iRet != RS_RET_NOT_A_NUMBER) { + FINALIZE; /* in any case, we have nothing left to do */ + } + + /* TODO: maybe we can do better than strcasecmp ;) -- overhead! */ + if(!strcasecmp((char*)rsCStrGetSzStr(pStr), "true")) { + *pBool = 1; + } else if(!strcasecmp((char*)rsCStrGetSzStr(pStr), "yes")) { + *pBool = 1; + } else { + *pBool = 0; + } + +finalize_it: + RETiRet; +} + + +/* compare a rsCStr object with a classical sz string. + * Just like rsCStrCStrCmp, just for a different data type. + * There must not only the sz string but also its length be + * provided. If the caller does not know the length he can + * call with + * rsCstrSzStrCmp(pCS, psz, strlen((char*)psz)); + * we are not doing the strlen((char*)) ourselfs as the caller might + * already know the length and in such cases we can save the + * overhead of doing it one more time (strelen() is costly!). + * The bottom line is that the provided length MUST be correct! + * The to sz string pointer must not be NULL! + * rgerhards 2005-09-26 + */ +int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz) +{ + rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); + assert(psz != NULL); + assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ + if(pCS1->iStrLen == iLenSz) + /* we are using iLenSz below, because the lengths + * are equal and iLenSz is faster to access + */ + if(iLenSz == 0) + return 0; /* zero-sized strings are equal ;) */ + else { /* we now have two non-empty strings of equal + * length, so we need to actually check if they + * are equal. + */ + register size_t i; + for(i = 0 ; i < iLenSz ; ++i) { + if(pCS1->pBuf[i] != psz[i]) + return pCS1->pBuf[i] - psz[i]; + } + /* if we arrive here, the strings are equal */ + return 0; + } + else + return pCS1->iStrLen - iLenSz; +} + + +/* Locate the first occurence of this rsCStr object inside a standard sz string. + * Returns the offset (0-bound) of this first occurrence. If not found, -1 is + * returned. Both parameters MUST be given (NULL is not allowed). + * rgerhards 2005-09-19 + */ +int rsCStrLocateInSzStr(cstr_t *pThis, uchar *sz) +{ + int i; + int iMax; + int bFound; + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + assert(sz != NULL); + + if(pThis->iStrLen == 0) + return 0; + + /* compute the largest index where a match could occur - after all, + * the to-be-located string must be able to be present in the + * searched string (it needs its size ;)). + */ + iMax = strlen((char*)sz) - pThis->iStrLen; + + bFound = 0; + i = 0; + while(i <= iMax && !bFound) { + size_t iCheck; + uchar *pComp = sz + i; + for(iCheck = 0 ; iCheck < pThis->iStrLen ; ++iCheck) + if(*(pComp + iCheck) != *(pThis->pBuf + iCheck)) + break; + if(iCheck == pThis->iStrLen) + bFound = 1; /* found! - else it wouldn't be equal */ + else + ++i; /* on to the next try */ + } + + return(bFound ? i : -1); +} + + +/* This is the same as rsCStrLocateInSzStr(), but does a case-insensitve + * comparison. + * TODO: over time, consolidate the two. + * rgerhards, 2008-02-28 + */ +int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz) +{ + int i; + int iMax; + int bFound; + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + assert(sz != NULL); + + if(pThis->iStrLen == 0) + return 0; + + /* compute the largest index where a match could occur - after all, + * the to-be-located string must be able to be present in the + * searched string (it needs its size ;)). + */ + iMax = strlen((char*)sz) - pThis->iStrLen; + + bFound = 0; + i = 0; + while(i <= iMax && !bFound) { + size_t iCheck; + uchar *pComp = sz + i; + for(iCheck = 0 ; iCheck < pThis->iStrLen ; ++iCheck) + if(tolower(*(pComp + iCheck)) != tolower(*(pThis->pBuf + iCheck))) + break; + if(iCheck == pThis->iStrLen) + bFound = 1; /* found! - else it wouldn't be equal */ + else + ++i; /* on to the next try */ + } + + return(bFound ? i : -1); +} + + +#if 0 /* read comment below why this is commented out. In short: for future use! */ +/* locate the first occurence of a standard sz string inside a rsCStr object. + * Returns the offset (0-bound) of this first occurrence. If not found, -1 is + * returned. + * rgerhards 2005-09-19 + * WARNING: I accidently created this function (I later noticed I didn't relly + * need it... I will not remove the function, as it probably is useful + * some time later. However, it is not fully tested, so start with testing + * it before you put it to first use). + */ +int rsCStrLocateSzStr(cstr_t *pThis, uchar *sz) +{ + int iLenSz; + int i; + int iMax; + int bFound; + rsCHECKVALIDOBJECT(pThis, OIDrsCStr); + + if(sz == NULL) + return 0; + + iLenSz = strlen((char*)sz); + if(iLenSz == 0) + return 0; + + /* compute the largest index where a match could occur - after all, + * the to-be-located string must be able to be present in the + * searched string (it needs its size ;)). + */ + iMax = pThis->iStrLen - iLenSz; + + bFound = 0; + i = 0; + while(i < iMax && !bFound) { + int iCheck; + uchar *pComp = pThis->pBuf + i; + for(iCheck = 0 ; iCheck < iLenSz ; ++iCheck) + if(*(pComp + iCheck) != *(sz + iCheck)) + break; + if(iCheck == iLenSz) + bFound = 1; /* found! - else it wouldn't be equal */ + else + ++i; /* on to the next try */ + } + + return(bFound ? i : -1); +} +#endif /* end comment out */ + + +/* our exit function. TODO: remove once converted to a class + * rgerhards, 2008-03-11 + */ +rsRetVal strExit() +{ + DEFiRet; + objRelease(regexp, LM_REGEXP_FILENAME); + RETiRet; +} + + +/* our init function. TODO: remove once converted to a class + */ +rsRetVal strInit() +{ + DEFiRet; + CHKiRet(objGetObjInterface(&obj)); + +finalize_it: + RETiRet; +} + + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + * vi:set ai: + */ diff --git a/runtime/stringbuf.h b/runtime/stringbuf.h new file mode 100644 index 00000000..c1966449 --- /dev/null +++ b/runtime/stringbuf.h @@ -0,0 +1,169 @@ +/*! \file stringbuf.h + * \brief The counted string object + * + * This is the byte-counted string class for rsyslog. It is a replacement + * for classical \0 terminated string functions. We introduce it in + * the hope it will make the program more secure, obtain some performance + * and, most importantly, lay they foundation for syslog-protocol, which + * requires strings to be able to handle embedded \0 characters. + * + * \author Rainer Gerhards + * \date 2005-09-07 + * Initial version begun. + * + * All functions in this "class" start with rsCStr (rsyslog Counted String). + * Copyright 2005 + * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef _STRINGBUF_H_INCLUDED__ +#define _STRINGBUF_H_INCLUDED__ 1 + +/** + * The dynamic string buffer object. + */ +typedef struct cstr_s +{ +#ifndef NDEBUG + rsObjID OID; /**< object ID */ +#endif + uchar *pBuf; /**< pointer to the string buffer, may be NULL if string is empty */ + uchar *pszBuf; /**< pointer to the sz version of the string (after it has been created )*/ + size_t iBufSize; /**< current maximum size of the string buffer */ + size_t iStrLen; /**< length of the string in characters. */ + size_t iAllocIncrement; /**< the amount of bytes the string should be expanded if it needs to */ +} cstr_t; + + +/** + * Construct a rsCStr object. + */ +rsRetVal rsCStrConstruct(cstr_t **ppThis); +rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz); +rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom); + +/** + * Destruct the string buffer object. + */ +void rsCStrDestruct(cstr_t **ppThis); + +/** + * Append a character to an existing string. If necessary, the + * method expands the string buffer. + * + * \param c Character to append to string. + */ +rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c); + +/** + * Truncate "n" number of characters from the end of the + * string. The buffer remains unchanged, just the + * string length is manipulated. This is for performance + * reasons. + */ +rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc); + +rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis); + +/** + * Append a string to the buffer. For performance reasons, + * use rsCStrAppenStrWithLen() if you know the length. + * + * \param psz pointer to string to be appended. Must not be NULL. + */ +rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz); + +/** + * Append a string to the buffer. + * + * \param psz pointer to string to be appended. Must not be NULL. + * \param iStrLen the length of the string pointed to by psz + */ +rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen); + +/** + * Set a new allocation incremet. This will influence + * the allocation the next time the string will be expanded. + * It can be set and changed at any time. If done immediately + * after custructing the StrB object, this will also be + * the inital allocation. + * + * \param iNewIncrement The new increment size + * + * \note It is possible to use a very low increment, e.g. 1 byte. + * This can generate a considerable overhead. We highly + * advise not to use an increment below 32 bytes, except + * if you are very well aware why you are doing it ;) + */ +void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement); +#define rsCStrGetAllocIncrement(pThis) ((pThis)->iAllocIncrement) + +/** + * Append an integer to the string. No special formatting is + * done. + */ +rsRetVal rsCStrAppendInt(cstr_t *pThis, long i); + + +rsRetVal strExit(void); /* TODO: remove once we have a real object interface! */ +uchar* rsCStrGetSzStr(cstr_t *pThis); +uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis); +rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew); +rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL); +int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2); +int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz); +int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz); +int rsCStrLocateSzStr(cstr_t *pCStr, uchar *sz); +int rsCStrLocateInSzStr(cstr_t *pThis, uchar *sz); +int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz); +int rsCStrStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz); +int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz); +int rsCStrSzStrStartsWithCStr(cstr_t *pCS1, uchar *psz, size_t iLenSz); +int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz); +rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber); +rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool); +rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend); + +/* now come inline-like functions */ +#ifdef NDEBUG +# define rsCStrLen(x) ((int)((x)->iStrLen)) +#else + int rsCStrLen(cstr_t *pThis); +#endif + +#if STRINGBUF_TRIM_ALLOCSIZE != 1 +/* This is the normal case (see comment in rsCStrFinish!). In those cases, the function + * simply needs to do nothing, so that we can save us the function call. + * rgerhards, 2008-02-12 + */ +# define rsCStrFinish(pThis) RS_RET_OK +#else + /** + * Finish the string buffer dynamic allocation. + */ + rsRetVal rsCStrFinish(cstr_t *pThis); +#endif + +#define rsCStrGetBufBeg(x) ((x)->pBuf) + +rsRetVal strInit(); +rsRetVal strExit(); + +#endif /* single include */ diff --git a/runtime/syslogd-types.h b/runtime/syslogd-types.h new file mode 100644 index 00000000..be0dfdd8 --- /dev/null +++ b/runtime/syslogd-types.h @@ -0,0 +1,103 @@ +/* syslogd-type.h + * This file contains type defintions used by syslogd and its modules. + * It is a required input for any module. + * + * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * The rsyslog runtime library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The rsyslog runtime library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the rsyslog runtime library. If not, see . + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. + */ +#ifndef SYSLOGD_TYPES_INCLUDED +#define SYSLOGD_TYPES_INCLUDED 1 + +#include "stringbuf.h" +#include +#if HAVE_SYSLOG_H +#include +#endif + +#define FALSE 0 +#define TRUE 1 + +#ifdef UT_NAMESIZE +# define UNAMESZ UT_NAMESIZE /* length of a login name */ +#else +# define UNAMESZ 8 /* length of a login name */ +#endif +#define MAXUNAMES 20 /* maximum number of user names */ +#define MAXFNAME 200 /* max file pathname length */ + +#define _DB_MAXDBLEN 128 /* maximum number of db */ +#define _DB_MAXUNAMELEN 128 /* maximum number of user name */ +#define _DB_MAXPWDLEN 128 /* maximum number of user's pass */ +#define _DB_DELAYTIMEONERROR 20 /* If an error occur we stop logging until + a delayed time is over */ + + +/* we define features of the syslog code. This features can be used + * to check if modules are compatible with them - and possible other + * applications I do not yet envision. -- rgerhards, 2007-07-24 + */ +typedef enum _syslogFeature { + sFEATURERepeatedMsgReduction = 1 +} syslogFeature; + +/* we define our own facility and severities */ +/* facility and severity codes */ +typedef struct _syslogCode { + char *c_name; + int c_val; +} syslogCODE; + +/* values for host comparisons specified with host selector blocks + * (+host, -host). rgerhards 2005-10-18. + */ +enum _EHostnameCmpMode { + HN_NO_COMP = 0, /* do not compare hostname */ + HN_COMP_MATCH = 1, /* hostname must match */ + HN_COMP_NOMATCH = 2 /* hostname must NOT match */ +}; +typedef enum _EHostnameCmpMode EHostnameCmpMode; + +/* rgerhards 2004-11-11: the following structure represents + * a time as it is used in syslog. + */ +struct syslogTime { + int timeType; /* 0 - unitinialized , 1 - RFC 3164, 2 - syslog-protocol */ + int year; + int month; + int day; + int hour; /* 24 hour clock */ + int minute; + int second; + int secfrac; /* fractional seconds (must be 32 bit!) */ + int secfracPrecision; + char OffsetMode; /* UTC offset + or - */ + char OffsetHour; /* UTC offset in hours */ + int OffsetMinute; /* UTC offset in minutes */ + /* full UTC offset minutes = OffsetHours*60 + OffsetMinute. Then use + * OffsetMode to know the direction. + */ +}; +typedef struct syslogTime syslogTime_t; + +#endif /* #ifndef SYSLOGD_TYPES_INCLUDED */ +/* vi:set ai: + */ diff --git a/srUtils.c b/srUtils.c deleted file mode 100644 index fa451b7e..00000000 --- a/srUtils.c +++ /dev/null @@ -1,506 +0,0 @@ -/**\file srUtils.c - * \brief General utilties that fit nowhere else. - * - * The namespace for this file is "srUtil". - * - * \author Rainer Gerhards - * \date 2003-09-09 - * Coding begun. - * - * Copyright 2003-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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" - -#include "rsyslog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "liblogging-stub.h" -#define TRUE 1 -#define FALSE 0 -#include "srUtils.h" -#include "syslogd.h" -#include "obj.h" - - -/* here we host some syslog specific names. There currently is no better place - * to do it, but over here is also not ideal... -- rgerhards, 2008-02-14 - */ -syslogName_t syslogPriNames[] = { - {"alert", LOG_ALERT}, - {"crit", LOG_CRIT}, - {"debug", LOG_DEBUG}, - {"emerg", LOG_EMERG}, - {"err", LOG_ERR}, - {"error", LOG_ERR}, /* DEPRECATED */ - {"info", LOG_INFO}, - {"none", INTERNAL_NOPRI}, /* INTERNAL */ - {"notice", LOG_NOTICE}, - {"panic", LOG_EMERG}, /* DEPRECATED */ - {"warn", LOG_WARNING}, /* DEPRECATED */ - {"warning", LOG_WARNING}, - {"*", TABLE_ALLPRI}, - {NULL, -1} -}; - -#ifndef LOG_AUTHPRIV -# define LOG_AUTHPRIV LOG_AUTH -#endif -syslogName_t syslogFacNames[] = { - {"auth", LOG_AUTH}, - {"authpriv", LOG_AUTHPRIV}, - {"cron", LOG_CRON}, - {"daemon", LOG_DAEMON}, - {"kern", LOG_KERN}, - {"lpr", LOG_LPR}, - {"mail", LOG_MAIL}, - {"mark", LOG_MARK}, /* INTERNAL */ - {"news", LOG_NEWS}, - {"security", LOG_AUTH}, /* DEPRECATED */ - {"syslog", LOG_SYSLOG}, - {"user", LOG_USER}, - {"uucp", LOG_UUCP}, -#if defined(LOG_FTP) - {"ftp", LOG_FTP}, -#endif - {"local0", LOG_LOCAL0}, - {"local1", LOG_LOCAL1}, - {"local2", LOG_LOCAL2}, - {"local3", LOG_LOCAL3}, - {"local4", LOG_LOCAL4}, - {"local5", LOG_LOCAL5}, - {"local6", LOG_LOCAL6}, - {"local7", LOG_LOCAL7}, - {NULL, -1}, -}; - -/* ################################################################# * - * private members * - * ################################################################# */ - -/* As this is not a "real" object, there won't be any private - * members in this file. - */ - -/* ################################################################# * - * public members * - * ################################################################# */ - -rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv) -{ - int i; - int bIsNegative; - char szBuf[64]; /* sufficiently large for my lifespan and those of my children... ;) */ - - assert(pBuf != NULL); - assert(iLenBuf > 1); /* This is actually an app error and as thus checked for... */ - - if(iToConv < 0) - { - bIsNegative = TRUE; - iToConv *= -1; - } - else - bIsNegative = FALSE; - - /* first generate a string with the digits in the reverse direction */ - i = 0; - do - { - szBuf[i++] = iToConv % 10 + '0'; - iToConv /= 10; - } while(iToConv > 0); /* warning: do...while()! */ - --i; /* undo last increment - we were pointing at NEXT location */ - - /* make sure we are within bounds... */ - if(i + 2 > iLenBuf) /* +2 because: a) i starts at zero! b) the \0 byte */ - return RS_RET_PROVIDED_BUFFER_TOO_SMALL; - - /* then move it to the right direction... */ - if(bIsNegative == TRUE) - *pBuf++ = '-'; - while(i >= 0) - *pBuf++ = szBuf[i--]; - *pBuf = '\0'; /* terminate it!!! */ - - return RS_RET_OK; -} - -uchar *srUtilStrDup(uchar *pOld, size_t len) -{ - uchar *pNew; - - assert(pOld != NULL); - - if((pNew = malloc(len + 1)) != NULL) - memcpy(pNew, pOld, len + 1); - - return pNew; -} - - -/* creates a path recursively - * Return 0 on success, -1 otherwise. On failure, errno - * hold the last OS error. - * Param "mode" holds the mode that all non-existing directories - * are to be created with. - */ -int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode, - uid_t uid, gid_t gid, int bFailOnChownFail) -{ - uchar *p; - uchar *pszWork; - size_t len; - int bErr = 0; - - assert(szFile != NULL); - assert(lenFile > 0); - - len = lenFile + 1; /* add one for '\0'-byte */ - if((pszWork = malloc(sizeof(uchar) * len)) == NULL) - return -1; - memcpy(pszWork, szFile, len); - for(p = pszWork+1 ; *p ; p++) - if(*p == '/') { - /* temporarily terminate string, create dir and go on */ - *p = '\0'; - if(access((char*)pszWork, F_OK)) { - if(mkdir((char*)pszWork, mode) == 0) { - if(uid != (uid_t) -1 || gid != (gid_t) -1) { - /* we need to set owner/group */ - if(chown((char*)pszWork, uid, gid) != 0) - if(bFailOnChownFail) - bErr = 1; - /* silently ignore if configured - * to do so. - */ - } - } else - bErr = 1; - if(bErr) { - int eSave = errno; - free(pszWork); - errno = eSave; - return -1; - } - } - *p = '/'; - } - free(pszWork); - return 0; -} - - -/* execute a program with a single argument - * returns child pid if everything ok, 0 on failure. if - * it fails, errno is set. if it fails after the fork(), the caller - * can not be notfied for obvious reasons. if bwait is set to 1, - * the code waits until the child terminates - that potentially takes - * a lot of time. - * implemented 2007-07-20 rgerhards - */ -int execProg(uchar *program, int bWait, uchar *arg) -{ - int pid; - int sig; - struct sigaction sigAct; - - dbgprintf("exec program '%s' with param '%s'\n", program, arg); - pid = fork(); - if (pid < 0) { - return 0; - } - - if(pid) { /* Parent */ - if(bWait) - if(waitpid(pid, NULL, 0) == -1) - if(errno != ECHILD) { - /* we do not use logerror(), because - * that might bring us into an endless - * loop. At some time, we may - * reconsider this behaviour. - */ - dbgprintf("could not wait on child after executing '%s'", - (char*)program); - } - return pid; - } - /* Child */ - alarm(0); /* create a clean environment before we exec the real child */ - - memset(&sigAct, 0, sizeof(sigAct)); - sigemptyset(&sigAct.sa_mask); - sigAct.sa_handler = SIG_DFL; - - for(sig = 1 ; sig < NSIG ; ++sig) - sigaction(sig, &sigAct, NULL); - - execlp((char*)program, (char*) program, (char*)arg, NULL); - /* In the long term, it's a good idea to implement some enhanced error - * checking here. However, it can not easily be done. For starters, we - * may run into endless loops if we log to syslog. The next problem is - * that output is typically not seen by the user. For the time being, - * we use no error reporting, which is quite consitent with the old - * system() way of doing things. rgerhards, 2007-07-20 - */ - perror("exec"); - exit(1); /* not much we can do in this case */ -} - - -/* skip over whitespace in a standard C string. The - * provided pointer is advanced to the first non-whitespace - * charater or the \0 byte, if there is none. It is never - * moved past the \0. - */ -void skipWhiteSpace(uchar **pp) -{ - register uchar *p; - - assert(pp != NULL); - assert(*pp != NULL); - - p = *pp; - while(*p && isspace((int) *p)) - ++p; - *pp = p; -} - - -/* generate a file name from four parts: - * /. - * If number is negative, it is not used. If any of the strings is - * NULL, an empty string is used instead. Length must be provided. - * lNumDigits is the minimum number of digits that lNum should have. This - * is to pretty-print the file name, e.g. lNum = 3, lNumDigits= 4 will - * result in "0003" being used inside the file name. Set lNumDigits to 0 - * to use as few space as possible. - * rgerhards, 2008-01-03 - */ -rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName, - size_t lenFName, long lNum, int lNumDigits) -{ - DEFiRet; - uchar *pName; - uchar *pNameWork; - size_t lenName; - uchar szBuf[128]; /* buffer for number */ - char szFmtBuf[32]; /* buffer for snprintf format */ - size_t lenBuf; - - if(lNum < 0) { - szBuf[0] = '\0'; - lenBuf = 0; - } else { - if(lNumDigits > 0) { - snprintf(szFmtBuf, sizeof(szFmtBuf), ".%%0%dld", lNumDigits); - lenBuf = snprintf((char*)szBuf, sizeof(szBuf), szFmtBuf, lNum); - } else - lenBuf = snprintf((char*)szBuf, sizeof(szBuf), ".%ld", lNum); - } - - lenName = lenDirName + 1 + lenFName + lenBuf + 1; /* last +1 for \0 char! */ - if((pName = malloc(sizeof(uchar) * lenName)) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - - /* got memory, now construct string */ - memcpy(pName, pDirName, lenDirName); - pNameWork = pName + lenDirName; - *pNameWork++ = '/'; - memcpy(pNameWork, pFName, lenFName); - pNameWork += lenFName; - if(lenBuf > 0) { - memcpy(pNameWork, szBuf, lenBuf); - pNameWork += lenBuf; - } - *pNameWork = '\0'; - - *ppName = pName; - -finalize_it: - RETiRet; -} - -/* get the number of digits required to represent a given number. We use an - * iterative approach as we do not like to draw in the floating point - * library just for log(). -- rgerhards, 2008-01-10 - */ -int getNumberDigits(long lNum) -{ - int iDig; - - if(lNum == 0) - iDig = 1; - else - for(iDig = 0 ; lNum != 0 ; ++iDig) - lNum /= 10; - - return iDig; -} - - -/* compute an absolute time timeout suitable for calls to pthread_cond_timedwait() - * rgerhards, 2008-01-14 - */ -rsRetVal -timeoutComp(struct timespec *pt, long iTimeout) -{ - assert(pt != NULL); - /* compute timeout */ - clock_gettime(CLOCK_REALTIME, pt); - pt->tv_nsec += (iTimeout % 1000) * 1000000; /* think INTEGER arithmetic! */ - if(pt->tv_nsec > 999999999) { /* overrun? */ - pt->tv_nsec -= 1000000000; - } - pt->tv_sec += iTimeout / 1000; - return RS_RET_OK; /* so far, this is static... */ -} - - -/* This function is kind of the reverse of timeoutComp() - it takes an absolute - * timeout value and computes how far this is in the future. If the value is already - * in the past, 0 is returned. The return value is in ms. - * rgerhards, 2008-01-25 - */ -long -timeoutVal(struct timespec *pt) -{ - struct timespec t; - long iTimeout; - - assert(pt != NULL); - /* compute timeout */ - clock_gettime(CLOCK_REALTIME, &t); - iTimeout = (pt->tv_nsec - t.tv_nsec) / 1000000; - iTimeout += (pt->tv_sec - t.tv_sec) * 1000; - - if(iTimeout < 0) - iTimeout = 0; - - return iTimeout; -} - - -/* cancellation cleanup handler - frees provided mutex - * rgerhards, 2008-01-14 - */ -void -mutexCancelCleanup(void *arg) -{ - BEGINfunc - assert(arg != NULL); - d_pthread_mutex_unlock((pthread_mutex_t*) arg); - ENDfunc -} - - -/* rsSleep() - a fairly portable way to to sleep. It - * will wake up when - * a) the wake-time is over - * rgerhards, 2008-01-28 - */ -void -srSleep(int iSeconds, int iuSeconds) -{ - struct timeval tvSelectTimeout; - - BEGINfunc - tvSelectTimeout.tv_sec = iSeconds; - tvSelectTimeout.tv_usec = iuSeconds; /* micro seconds */ - select(0, NULL, NULL, NULL, &tvSelectTimeout); - ENDfunc -} - - -/* From varmojfekoj's mail on why he provided rs_strerror_r(): - * There are two problems with strerror_r(): - * I see you've rewritten some of the code which calls it to use only - * the supplied buffer; unfortunately the GNU implementation sometimes - * doesn't use the buffer at all and returns a pointer to some - * immutable string instead, as noted in the man page. - * - * The other problem is that on some systems strerror_r() has a return - * type of int. - * - * So I've written a wrapper function rs_strerror_r(), which should - * take care of all this and be used instead. - * - * Added 2008-01-30 - */ -char *rs_strerror_r(int errnum, char *buf, size_t buflen) { -#ifdef __hpux - char *pszErr; - pszErr = strerror(errnum); - snprintf(buf, buflen, "%s", pszErr); -#else -# ifdef STRERROR_R_CHAR_P - char *p = strerror_r(errnum, buf, buflen); - if (p != buf) { - strncpy(buf, p, buflen); - buf[buflen - 1] = '\0'; - } -# else - strerror_r(errnum, buf, buflen); -# endif -#endif /* #ifdef __hpux */ - return buf; -} - - -/* Decode a symbolic name to a numeric value - */ -int decodeSyslogName(uchar *name, syslogName_t *codetab) -{ - register syslogName_t *c; - register uchar *p; - uchar buf[80]; - - ASSERT(name != NULL); - ASSERT(codetab != NULL); - - dbgprintf("symbolic name: %s", name); - if (isdigit((int) *name)) - { - dbgprintf("\n"); - return (atoi((char*) name)); - } - strncpy((char*) buf, (char*) name, 79); - for (p = buf; *p; p++) - if (isupper((int) *p)) - *p = tolower((int) *p); - for (c = codetab; c->c_name; c++) - if (!strcmp((char*) buf, (char*) c->c_name)) - { - dbgprintf(" ==> %d\n", c->c_val); - return (c->c_val); - } - return (-1); -} - - -/* vim:set ai: - */ diff --git a/srUtils.h b/srUtils.h deleted file mode 100644 index ebd6518f..00000000 --- a/srUtils.h +++ /dev/null @@ -1,125 +0,0 @@ -/*! \file srUtils.h - * \brief General, small utilities that fit nowhere else. - * - * \author Rainer Gerhards - * \date 2003-09-09 - * Coding begun. - * - * Copyright 2003-2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef __SRUTILS_H_INCLUDED__ -#define __SRUTILS_H_INCLUDED__ 1 - - -/* syslog names */ -#ifndef LOG_MAKEPRI -# define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri)) -#endif -#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */ -#define TABLE_NOPRI 0 /* Value to indicate no priority in f_pmask */ -#define TABLE_ALLPRI 0xFF /* Value to indicate all priorities in f_pmask */ -#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */ - -typedef struct syslogName_s { - char *c_name; - int c_val; -} syslogName_t; - -extern syslogName_t syslogPriNames[]; -extern syslogName_t syslogFacNames[]; - -/** - * A reimplementation of itoa(), as this is not available - * on all platforms. We used the chance to make an interface - * that fits us well, so it is no longer plain itoa(). - * - * This method works with the US-ASCII alphabet. If you port this - * to e.g. EBCDIC, you need to make a small adjustment. Keep in mind, - * that on the wire it MUST be US-ASCII, so basically all you need - * to do is replace the constant '0' with 0x30 ;). - * - * \param pBuf Caller-provided buffer that will receive the - * generated ASCII string. - * - * \param iLenBuf Length of the caller-provided buffer. - * - * \param iToConv The integer to be converted. - */ -rsRetVal srUtilItoA(char *pBuf, int iLenBuf, number_t iToConv); - -/** - * A method to duplicate a string for which the length is known. - * Len must be the length in characters WITHOUT the trailing - * '\0' byte. - * rgerhards, 2007-07-10 - */ -unsigned char *srUtilStrDup(unsigned char *pOld, size_t len); -/** - * A method to create a directory and all its missing parents for - * a given file name. Please not that the rightmost element is - * considered to be a file name and thus NO directory is being created - * for it. - * added 2007-07-17 by rgerhards - */ -int makeFileParentDirs(uchar *szFile, size_t lenFile, mode_t mode, uid_t uid, gid_t gid, int bFailOnChown); -int execProg(uchar *program, int bWait, uchar *arg); -void skipWhiteSpace(uchar **pp); -rsRetVal genFileName(uchar **ppName, uchar *pDirName, size_t lenDirName, uchar *pFName, - size_t lenFName, long lNum, int lNumDigits); -int getNumberDigits(long lNum); -rsRetVal timeoutComp(struct timespec *pt, long iTimeout); -long timeoutVal(struct timespec *pt); -void mutexCancelCleanup(void *arg); -void srSleep(int iSeconds, int iuSeconds); -char *rs_strerror_r(int errnum, char *buf, size_t buflen); -int decodeSyslogName(uchar *name, syslogName_t *codetab); - -/* mutex operations */ -/* some macros to cancel-safe lock a mutex (it will automatically be released - * when the thread is cancelled. This needs to be done as macros because - * pthread_cleanup_push sometimes is a macro that can not be used inside a function. - * It's a bit ugly, but works well... rgerhards, 2008-01-20 - */ -#define DEFVARS_mutex_cancelsafeLock int iCancelStateSave -#define mutex_cancelsafe_lock(mut) \ - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \ - d_pthread_mutex_lock(mut); \ - pthread_cleanup_push(mutexCancelCleanup, mut); \ - pthread_setcancelstate(iCancelStateSave, NULL); -#define mutex_cancelsafe_unlock(mut) pthread_cleanup_pop(1) - -/* some useful constants */ -#define MUTEX_ALREADY_LOCKED 0 -#define LOCK_MUTEX 1 -#define DEFVARS_mutexProtection\ - int iCancelStateSave; \ - int bLockedOpIsLocked=0 -#define BEGIN_MTX_PROTECTED_OPERATIONS(mut, bMustLock) \ - if(bMustLock == LOCK_MUTEX) { \ - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &iCancelStateSave); \ - d_pthread_mutex_lock(mut); \ - bLockedOpIsLocked = 1; \ - } -#define END_MTX_PROTECTED_OPERATIONS(mut) \ - if(bLockedOpIsLocked) { \ - d_pthread_mutex_unlock(mut); \ - pthread_setcancelstate(iCancelStateSave, NULL); \ - } -#endif diff --git a/stringbuf.c b/stringbuf.c deleted file mode 100644 index 4254d5bd..00000000 --- a/stringbuf.c +++ /dev/null @@ -1,1079 +0,0 @@ -/* This is the byte-counted string class for rsyslog. It is a replacement - * for classical \0 terminated string functions. We introduce it in - * the hope it will make the program more secure, obtain some performance - * and, most importantly, lay they foundation for syslog-protocol, which - * requires strings to be able to handle embedded \0 characters. - * Please see syslogd.c for license information. - * All functions in this "class" start with rsCStr (rsyslog Counted String). - * begun 2005-09-07 rgerhards - * - * Copyright (C) 2007 by 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include "rsyslog.h" -#include "stringbuf.h" -#include "srUtils.h" -#include "regexp.h" -#include "obj.h" - - -/* ################################################################# * - * private members * - * ################################################################# */ - -/* static data */ -DEFobjCurrIf(obj) -DEFobjCurrIf(regexp) - -/* ################################################################# * - * public members * - * ################################################################# */ - - -rsRetVal rsCStrConstruct(cstr_t **ppThis) -{ - DEFiRet; - cstr_t *pThis; - - ASSERT(ppThis != NULL); - - if((pThis = (cstr_t*) calloc(1, sizeof(cstr_t))) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - - rsSETOBJTYPE(pThis, OIDrsCStr); - pThis->pBuf = NULL; - pThis->pszBuf = NULL; - pThis->iBufSize = 0; - pThis->iStrLen = 0; - pThis->iAllocIncrement = RS_STRINGBUF_ALLOC_INCREMENT; - *ppThis = pThis; - -finalize_it: - RETiRet; -} - - -/* construct from sz string - * rgerhards 2005-09-15 - */ -rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz) -{ - DEFiRet; - cstr_t *pThis; - - assert(ppThis != NULL); - - CHKiRet(rsCStrConstruct(&pThis)); - - pThis->iBufSize = pThis->iStrLen = strlen((char*)(char *) sz); - if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { - RSFREEOBJ(pThis); - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - - /* we do NOT need to copy the \0! */ - memcpy(pThis->pBuf, sz, pThis->iStrLen); - - *ppThis = pThis; - -finalize_it: - RETiRet; -} - -/* construct from CStr object. only the counted string is - * copied, not the szString. - * rgerhards 2005-10-18 - */ -rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom) -{ - DEFiRet; - cstr_t *pThis; - - assert(ppThis != NULL); - rsCHECKVALIDOBJECT(pFrom, OIDrsCStr); - - CHKiRet(rsCStrConstruct(&pThis)); - - pThis->iBufSize = pThis->iStrLen = pFrom->iStrLen; - if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { - RSFREEOBJ(pThis); - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } - - /* copy properties */ - memcpy(pThis->pBuf, pFrom->pBuf, pThis->iStrLen); - - *ppThis = pThis; -finalize_it: - RETiRet; -} - - -void rsCStrDestruct(cstr_t **ppThis) -{ - cstr_t *pThis = *ppThis; - - /* rgerhards 2005-10-19: The free of pBuf was contained in conditional compilation. - * The code was only compiled if STRINGBUF_TRIM_ALLOCSIZE was set to 1. I honestly - * do not know why it was so, I think it was an artifact. Anyhow, I have changed this - * now. Should there any issue occur, this comment hopefully will shed some light - * on what happened. I re-verified, and this function has never before been called - * by anyone. So changing it can have no impact for obvious reasons... - * - * rgerhards, 2008-02-20: I changed the interface to the new calling conventions, where - * the destructor receives a pointer to the object, so that it can set it to NULL. - */ - if(pThis->pBuf != NULL) { - free(pThis->pBuf); - } - - if(pThis->pszBuf != NULL) { - free(pThis->pszBuf); - } - - RSFREEOBJ(pThis); - *ppThis = NULL; -} - - -/* extend the string buffer if its size is insufficient. - * Param iMinNeeded is the minumum free space needed. If it is larger - * than the default alloc increment, space for at least this amount is - * allocated. In practice, a bit more is allocated because we envision that - * some more characters may be added after these. - * rgerhards, 2008-01-07 - */ -static rsRetVal rsCStrExtendBuf(cstr_t *pThis, size_t iMinNeeded) -{ - DEFiRet; - uchar *pNewBuf; - size_t iNewSize; - - /* first compute the new size needed */ - if(iMinNeeded > pThis->iAllocIncrement) { - /* we allocate "n" iAllocIncrements. Usually, that should - * leave some room after the absolutely needed one. It also - * reduces memory fragmentation. Note that all of this are - * integer operations (very important to understand what is - * going on)! Parenthesis are for better readibility. - */ - iNewSize = ((iMinNeeded / pThis->iAllocIncrement) + 1) * pThis->iAllocIncrement; - } else { - iNewSize = pThis->iBufSize + pThis->iAllocIncrement; - } - iNewSize += pThis->iBufSize; /* add current size */ - - /* and then allocate and copy over */ - /* DEV debugging only: dbgprintf("extending string buffer, old %d, new %d\n", pThis->iBufSize, iNewSize); */ - if((pNewBuf = (uchar*) malloc(iNewSize * sizeof(uchar))) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - memcpy(pNewBuf, pThis->pBuf, pThis->iBufSize); - pThis->iBufSize = iNewSize; - if(pThis->pBuf != NULL) { - free(pThis->pBuf); - } - pThis->pBuf = pNewBuf; - -finalize_it: - RETiRet; -} - - -/* append a string of known length. In this case, we make sure we do at most - * one additional memory allocation. - * I optimized this function to use memcpy(), among others. Consider it a - * rewrite (which may be good to know in case of bugs) -- rgerhards, 2008-01-07 - */ -rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen) -{ - DEFiRet; - - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - assert(psz != NULL); - - /* does the string fit? */ - if(pThis->iStrLen + iStrLen > pThis->iBufSize) { - CHKiRet(rsCStrExtendBuf(pThis, iStrLen)); /* need more memory! */ - } - - /* ok, now we always have sufficient continues memory to do a memcpy() */ - memcpy(pThis->pBuf + pThis->iStrLen, psz, iStrLen); - pThis->iStrLen += iStrLen; - -finalize_it: - RETiRet; -} - - -/* changed to be a wrapper to rsCStrAppendStrWithLen() so that - * we can save some time when we have the length but do not - * need to change existing code. - * rgerhards, 2007-07-03 - */ -rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz) -{ - return rsCStrAppendStrWithLen(pThis, psz, strlen((char*) psz)); -} - - -/* append the contents of one cstr_t object to another - * rgerhards, 2008-02-25 - */ -rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend) -{ - return rsCStrAppendStrWithLen(pThis, pstrAppend->pBuf, pstrAppend->iStrLen); -} - - -rsRetVal rsCStrAppendInt(cstr_t *pThis, long i) -{ - DEFiRet; - uchar szBuf[32]; - - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - CHKiRet(srUtilItoA((char*) szBuf, sizeof(szBuf), i)); - - iRet = rsCStrAppendStr(pThis, szBuf); -finalize_it: - RETiRet; -} - - -rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c) -{ - DEFiRet; - - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - if(pThis->iStrLen >= pThis->iBufSize) { - CHKiRet(rsCStrExtendBuf(pThis, 1)); /* need more memory! */ - } - - /* ok, when we reach this, we have sufficient memory */ - *(pThis->pBuf + pThis->iStrLen++) = c; - - /* check if we need to invalidate an sz representation! */ - if(pThis->pszBuf != NULL) { - free(pThis->pszBuf); - pThis->pszBuf = NULL; - } - -finalize_it: - RETiRet; -} - - -/* Sets the string object to the classigal sz-string provided. - * Any previously stored vlaue is discarded. If a NULL pointer - * the the new value (pszNew) is provided, an empty string is - * created (this is NOT an error!). Property iAllocIncrement is - * not modified by this function. - * rgerhards, 2005-10-18 - */ -rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew) -{ - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - if(pThis->pBuf != NULL) - free(pThis->pBuf); - if(pThis->pszBuf != NULL) - free(pThis->pszBuf); - if(pszNew == NULL) { - pThis->iStrLen = 0; - pThis->iBufSize = 0; - pThis->pBuf = NULL; - pThis->pszBuf = NULL; - } else { - pThis->iStrLen = strlen((char*)pszNew); - pThis->iBufSize = pThis->iStrLen; - pThis->pszBuf = NULL; - /* iAllocIncrement is NOT modified! */ - - /* now save the new value */ - if((pThis->pBuf = (uchar*) malloc(sizeof(uchar) * pThis->iStrLen)) == NULL) { - RSFREEOBJ(pThis); - return RS_RET_OUT_OF_MEMORY; - } - - /* we do NOT need to copy the \0! */ - memcpy(pThis->pBuf, pszNew, pThis->iStrLen); - } - - return RS_RET_OK; -} - -/* Converts the CStr object to a classical sz string and returns that. - * Same restrictions as in rsCStrGetSzStr() applies (see there!). This - * function here guarantees that a valid string is returned, even if - * the CStr object currently holds a NULL pointer string buffer. If so, - * "" is returned. - * rgerhards 2005-10-19 - * WARNING: The returned pointer MUST NOT be freed, as it may be - * obtained from that constant memory pool (in case of NULL!) - */ -uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis) -{ - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - if(pThis->pBuf == NULL) - return (uchar*) ""; - else - return rsCStrGetSzStr(pThis); -} - - -/* Converts the CStr object to a classical zero-terminated C string - * and returns that string. The caller must not free it and must not - * destroy the CStr object as long as the ascii string is used. - * This function may return NULL, if the string is currently NULL. This - * is a feature, not a bug. If you need non-NULL in any case, use - * rsCStrGetSzStrNoNULL() instead. - * rgerhards, 2005-09-15 - */ -uchar* rsCStrGetSzStr(cstr_t *pThis) -{ - size_t i; - - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - if(pThis->pBuf != NULL) - if(pThis->pszBuf == NULL) { - /* we do not yet have a usable sz version - so create it... */ - if((pThis->pszBuf = malloc((pThis->iStrLen + 1) * sizeof(uchar))) == NULL) { - /* TODO: think about what to do - so far, I have no bright - * idea... rgerhards 2005-09-07 - */ - } - else { /* we can create the sz String */ - /* now copy it while doing a sanity check. The string might contain a - * \0 byte. There is no way how a sz string can handle this. For - * the time being, we simply replace it with space - something that - * could definitely be improved (TODO). - * 2005-09-15 rgerhards - */ - for(i = 0 ; i < pThis->iStrLen ; ++i) { - if(pThis->pBuf[i] == '\0') - pThis->pszBuf[i] = ' '; - else - pThis->pszBuf[i] = pThis->pBuf[i]; - } - /* write terminator... */ - pThis->pszBuf[i] = '\0'; - } - } - - return(pThis->pszBuf); -} - - -/* Converts the CStr object to a classical zero-terminated C string, - * returns that string and destroys the CStr object. The returned string - * MUST be freed by the caller. The function might return NULL if - * no memory can be allocated. - * - * TODO: - * This function should at some time become special. The base idea is to - * add one extra byte to the end of the regular buffer, so that we can - * convert it to an szString without the need to copy. The extra memory - * footprint is not hefty, but the performance gain is potentially large. - * To get it done now, I am not doing the optimiziation right now. - * rgerhards, 2005-09-07 - * - * rgerhards, 2007-09-04: I have changed the interface of this function. It now - * returns an rsRetVal, so that we can communicate back if we have an error. - * Using the standard method is much better than returning NULL. Secondly, NULL - * was not actually an error - it was in indication if the string was empty. - * This was needed in some parts of the code, in others not. I have now added - * a second parameter to specify what the caller needs. I hope these changes - * will make it less likely that the function is called incorrectly, what - * previously happend quite often and was the cause of a number of program - * aborts. So the parameters are now: - * pointer to the object, pointer to string-pointer to receive string and - * bRetNULL: 0 - must not return NULL on empty string, return "" in that - * case, 1 - return NULL instead of an empty string. - * PLEASE NOTE: the caller must free the memory returned in ppSz in any case - * (except, of course, if it is NULL). - */ -rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL) -{ - DEFiRet; - uchar* pRetBuf; - - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - assert(ppSz != NULL); - assert(bRetNULL == 0 || bRetNULL == 1); - - if(pThis->pBuf == NULL) { - if(bRetNULL == 0) { - if((pRetBuf = malloc(sizeof(uchar))) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - *pRetBuf = '\0'; - } else { - pRetBuf = NULL; - } - } else - pRetBuf = rsCStrGetSzStr(pThis); - - *ppSz = pRetBuf; - -finalize_it: - /* We got it, now free the object ourselfs. Please note - * that we can NOT use the rsCStrDestruct function as it would - * also free the sz String buffer, which we pass on to the user. - */ - if(pThis->pBuf != NULL) - free(pThis->pBuf); - RSFREEOBJ(pThis); - - RETiRet; -} - - -#if STRINGBUF_TRIM_ALLOCSIZE == 1 - /* Only in this mode, we need to trim the string. To do - * so, we must allocate a new buffer of the exact - * string size, and then copy the old one over. - */ - /* WARNING - * STRINGBUF_TRIM_ALLOCSIZE can, in theory, be used to trim - * memory buffers. This part of the code was inherited from - * liblogging (where it is used in a different context) but - * never put to use in rsyslog. The reason is that it is hardly - * imaginable where the extra performance cost is worth the save - * in memory alloc. Then Anders Blomdel rightfully pointed out that - * the code does not work at all - and nobody even know that it - * probably shouldn't. Rather than removing, I deciced to somewhat - * fix the code, so that this feature may be enabled if somebody - * really has a need for it. Be warned, however, that I NEVER - * tested the fix. So if you intend to use this feature, you must - * do full testing before you rely on it. -- rgerhards, 2008-02-12 - */ -rsRetVal rsCStrFinish(cstr_t __attribute__((unused)) *pThis) -{ - DEFiRet; - uchar* pBuf; - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - if((pBuf = malloc((pThis->iStrLen) * sizeof(uchar))) == NULL) - { /* OK, in this case we use the previous buffer. At least - * we have it ;) - */ - } - else - { /* got the new buffer, so let's use it */ - memcpy(pBuf, pThis->pBuf, pThis->iStrLen); - pThis->pBuf = pBuf; - } - - RETiRet; -} -#endif /* #if STRINGBUF_TRIM_ALLOCSIZE == 1 */ - - -void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement) -{ - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - assert(iNewIncrement > 0); - - pThis->iAllocIncrement = iNewIncrement; -} - - -/* return the length of the current string - * 2005-09-09 rgerhards - * Please note: this is only a function in a debug build. - * For release builds, it is a macro defined in stringbuf.h. - * This is due to performance reasons. - */ -#ifndef NDEBUG -int rsCStrLen(cstr_t *pThis) -{ - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - return(pThis->iStrLen); -} -#endif - -/* Truncate characters from the end of the string. - * rgerhards 2005-09-15 - */ -rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc) -{ - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - if(pThis->iStrLen < nTrunc) - return RS_TRUNCAT_TOO_LARGE; - - pThis->iStrLen -= nTrunc; - - if(pThis->pszBuf != NULL) { - /* in this case, we adjust the psz representation - * by writing a new \0 terminator - this is by far - * the fastest way and outweights the additional memory - * required. 2005-9-19 rgerhards. - */ - pThis->pszBuf[pThis->iStrLen] = '\0'; - } - - return RS_RET_OK; -} - -/* Trim trailing whitespace from a given string - */ -rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis) -{ - register int i; - register uchar *pC; - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - i = pThis->iStrLen; - pC = pThis->pBuf + i - 1; - while(i > 0 && isspace((int)*pC)) { - --pC; - --i; - } - /* i now is the new string length! */ - pThis->iStrLen = i; - - return RS_RET_OK; -} - -/* compare two string objects - works like strcmp(), but operates - * on CStr objects. Please note that this version here is - * faster in the majority of cases, simply because it can - * rely on StrLen. - * rgerhards 2005-09-19 - * fixed bug, in which only the last byte was actually compared - * in equal-size strings. - * rgerhards, 2005-09-26 - */ -int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2) -{ - rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); - rsCHECKVALIDOBJECT(pCS2, OIDrsCStr); - if(pCS1->iStrLen == pCS2->iStrLen) - if(pCS1->iStrLen == 0) - return 0; /* zero-sized string are equal ;) */ - else { /* we now have two non-empty strings of equal - * length, so we need to actually check if they - * are equal. - */ - register size_t i; - for(i = 0 ; i < pCS1->iStrLen ; ++i) { - if(pCS1->pBuf[i] != pCS2->pBuf[i]) - return pCS1->pBuf[i] - pCS2->pBuf[i]; - } - /* if we arrive here, the strings are equal */ - return 0; - } - else - return pCS1->iStrLen - pCS2->iStrLen; -} - - -/* check if a sz-type string starts with a CStr object. This function - * is initially written to support the "startswith" property-filter - * comparison operation. Maybe it also has other needs. - * This functions is modelled after the strcmp() series, thus a - * return value of 0 indicates that the string starts with the - * sequence while -1 indicates it does not! - * rgerhards 2005-10-19 - */ -int rsCStrSzStrStartsWithCStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) -{ - register int i; - int iMax; - - rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); - assert(psz != NULL); - assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ - if(iLenSz >= pCS1->iStrLen) { - /* we need to checkusing pCS1->iStrLen charactes at maximum, thus - * we move it to iMax. - */ - iMax = pCS1->iStrLen; - if(iMax == 0) - return 0; /* yes, it starts with a zero-sized string ;) */ - else { /* we now have something to compare, so let's do it... */ - for(i = 0 ; i < iMax ; ++i) { - if(psz[i] != pCS1->pBuf[i]) - return psz[i] - pCS1->pBuf[i]; - } - /* if we arrive here, the string actually starts with pCS1 */ - return 0; - } - } - else - return -1; /* pCS1 is less then psz */ -} - - -/* check if a CStr object starts with a sz-type string. - * This functions is modelled after the strcmp() series, thus a - * return value of 0 indicates that the string starts with the - * sequence while -1 indicates it does not! - * rgerhards 2005-09-26 - */ -int rsCStrStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) -{ - register size_t i; - - rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); - assert(psz != NULL); - assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ - if(pCS1->iStrLen >= iLenSz) { - /* we are using iLenSz below, because we need to check - * iLenSz characters at maximum (start with!) - */ - if(iLenSz == 0) - return 0; /* yes, it starts with a zero-sized string ;) */ - else { /* we now have something to compare, so let's do it... */ - for(i = 0 ; i < iLenSz ; ++i) { - if(pCS1->pBuf[i] != psz[i]) - return pCS1->pBuf[i] - psz[i]; - } - /* if we arrive here, the string actually starts with psz */ - return 0; - } - } - else - return -1; /* pCS1 is less then psz */ -} - - -/* The same as rsCStrStartsWithSzStr(), but does a case-insensitive - * comparison. TODO: consolidate the two. - * rgerhards 2008-02-28 - */ -int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz) -{ - register size_t i; - - rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); - assert(psz != NULL); - assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ - if(pCS1->iStrLen >= iLenSz) { - /* we are using iLenSz below, because we need to check - * iLenSz characters at maximum (start with!) - */ - if(iLenSz == 0) - return 0; /* yes, it starts with a zero-sized string ;) */ - else { /* we now have something to compare, so let's do it... */ - for(i = 0 ; i < iLenSz ; ++i) { - if(tolower(pCS1->pBuf[i]) != tolower(psz[i])) - return tolower(pCS1->pBuf[i]) - tolower(psz[i]); - } - /* if we arrive here, the string actually starts with psz */ - return 0; - } - } - else - return -1; /* pCS1 is less then psz */ -} - -/* check if a CStr object matches a regex. - * msamia@redhat.com 2007-07-12 - * @return returns 0 if matched - * bug: doesn't work for CStr containing \0 - * rgerhards, 2007-07-16: bug is no real bug, because rsyslogd ensures there - * never is a \0 *inside* a property string. - * Note that the function returns -1 if regexp functionality is not available. - * TODO: change calling interface! -- rgerhards, 2008-03-07 - */ -int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz) -{ - regex_t preq; - int ret; - - BEGINfunc - - if(objUse(regexp, LM_REGEXP_FILENAME) == RS_RET_OK) { - regexp.regcomp(&preq, (char*) rsCStrGetSzStr(pCS1), 0); - ret = regexp.regexec(&preq, (char*) psz, 0, NULL, 0); - regexp.regfree(&preq); - } else { - ret = 1; /* simulate "not found" */ - } - - ENDfunc - return ret; -} - - -/* compare a rsCStr object with a classical sz string. This function - * is almost identical to rsCStrZsStrCmp(), but it also takes an offset - * to the CStr object from where the comparison is to start. - * I have thought quite a while if it really makes sense to more or - * less duplicate the code. After all, if you call it with an offset of - * zero, the functionality is exactly the same. So it looks natural to - * just have a single function. However, supporting the offset requires - * some (few) additional integer operations. While they are few, they - * happen at places in the code that is run very frequently. All in all, - * I have opted for performance and thus duplicated the code. I hope - * this is a good, or at least acceptable, compromise. - * rgerhards, 2005-09-26 - * This function also has an offset-pointer which allows to - * specify *where* the compare operation should begin in - * the CStr. If everything is to be compared, it must be set - * to 0. If some leading bytes are to be skipped, it must be set - * to the first index that is to be compared. It must not be - * set higher than the string length (this is considered a - * program bug and will lead to unpredictable results and program aborts). - * rgerhards 2005-09-26 - */ -int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz) -{ - BEGINfunc - rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); - assert(iOffset < pCS1->iStrLen); - assert(psz != NULL); - assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ - if((pCS1->iStrLen - iOffset) == iLenSz) { - /* we are using iLenSz below, because the lengths - * are equal and iLenSz is faster to access - */ - if(iLenSz == 0) { - return 0; /* zero-sized strings are equal ;) */ - ENDfunc - } else { /* we now have two non-empty strings of equal - * length, so we need to actually check if they - * are equal. - */ - register size_t i; - for(i = 0 ; i < iLenSz ; ++i) { - if(pCS1->pBuf[i+iOffset] != psz[i]) - return pCS1->pBuf[i+iOffset] - psz[i]; - } - /* if we arrive here, the strings are equal */ - return 0; - ENDfunc - } - } - else { - return pCS1->iStrLen - iOffset - iLenSz; - ENDfunc - } -} - - -/* Converts a string to a number. If the string dos not contain a number, - * RS_RET_NOT_A_NUMBER is returned and the contents of pNumber is undefined. - * If all goes well, pNumber contains the number that the string was converted - * to. - */ -rsRetVal -rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber) -{ - DEFiRet; - number_t n; - int bIsNegative; - size_t i; - - ASSERT(pStr != NULL); - ASSERT(pNumber != NULL); - - if(pStr->iStrLen == 0) { - /* can be converted to 0! (by convention) */ - pNumber = 0; - FINALIZE; - } - - /* first skip whitespace (if present) */ - for(i = 0 ; i < pStr->iStrLen && isspace(pStr->pBuf[i]) ; ++i) { - /*DO NOTHING*/ - } - - /* we have a string, so let's check its syntax */ - if(pStr->pBuf[i] == '+') { - ++i; /* skip that char */ - bIsNegative = 0; - } else if(pStr->pBuf[0] == '-') { - ++i; /* skip that char */ - bIsNegative = 1; - } else { - bIsNegative = 0; - } - - /* TODO: octal? hex? */ - n = 0; - while(i < pStr->iStrLen && isdigit(pStr->pBuf[i])) { - n = n * 10 + pStr->pBuf[i] * 10; - ++i; - } - - if(i < pStr->iStrLen) /* non-digits before end of string? */ - ABORT_FINALIZE(RS_RET_NOT_A_NUMBER); - - if(bIsNegative) - n *= -1; - - /* we got it, so return the number */ - *pNumber = n; - -finalize_it: - RETiRet; -} - - -/* Converts a string to a boolen. First tries to convert to a number. If - * that succeeds, we are done (number is then used as boolean value). If - * that fails, we look if the string is "yes" or "true". If so, a value - * of 1 is returned. In all other cases, a value of 0 is returned. Please - * note that we do not have a specific boolean type, so we return a number. - * so, these are - * RS_RET_NOT_A_NUMBER is returned and the contents of pNumber is undefined. - * If all goes well, pNumber contains the number that the string was converted - * to. - */ -rsRetVal -rsCStrConvertToBool(cstr_t *pStr, number_t *pBool) -{ - DEFiRet; - - ASSERT(pStr != NULL); - ASSERT(pBool != NULL); - - iRet = rsCStrConvertToNumber(pStr, pBool); - - if(iRet != RS_RET_NOT_A_NUMBER) { - FINALIZE; /* in any case, we have nothing left to do */ - } - - /* TODO: maybe we can do better than strcasecmp ;) -- overhead! */ - if(!strcasecmp((char*)rsCStrGetSzStr(pStr), "true")) { - *pBool = 1; - } else if(!strcasecmp((char*)rsCStrGetSzStr(pStr), "yes")) { - *pBool = 1; - } else { - *pBool = 0; - } - -finalize_it: - RETiRet; -} - - -/* compare a rsCStr object with a classical sz string. - * Just like rsCStrCStrCmp, just for a different data type. - * There must not only the sz string but also its length be - * provided. If the caller does not know the length he can - * call with - * rsCstrSzStrCmp(pCS, psz, strlen((char*)psz)); - * we are not doing the strlen((char*)) ourselfs as the caller might - * already know the length and in such cases we can save the - * overhead of doing it one more time (strelen() is costly!). - * The bottom line is that the provided length MUST be correct! - * The to sz string pointer must not be NULL! - * rgerhards 2005-09-26 - */ -int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz) -{ - rsCHECKVALIDOBJECT(pCS1, OIDrsCStr); - assert(psz != NULL); - assert(iLenSz == strlen((char*)psz)); /* just make sure during debugging! */ - if(pCS1->iStrLen == iLenSz) - /* we are using iLenSz below, because the lengths - * are equal and iLenSz is faster to access - */ - if(iLenSz == 0) - return 0; /* zero-sized strings are equal ;) */ - else { /* we now have two non-empty strings of equal - * length, so we need to actually check if they - * are equal. - */ - register size_t i; - for(i = 0 ; i < iLenSz ; ++i) { - if(pCS1->pBuf[i] != psz[i]) - return pCS1->pBuf[i] - psz[i]; - } - /* if we arrive here, the strings are equal */ - return 0; - } - else - return pCS1->iStrLen - iLenSz; -} - - -/* Locate the first occurence of this rsCStr object inside a standard sz string. - * Returns the offset (0-bound) of this first occurrence. If not found, -1 is - * returned. Both parameters MUST be given (NULL is not allowed). - * rgerhards 2005-09-19 - */ -int rsCStrLocateInSzStr(cstr_t *pThis, uchar *sz) -{ - int i; - int iMax; - int bFound; - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - assert(sz != NULL); - - if(pThis->iStrLen == 0) - return 0; - - /* compute the largest index where a match could occur - after all, - * the to-be-located string must be able to be present in the - * searched string (it needs its size ;)). - */ - iMax = strlen((char*)sz) - pThis->iStrLen; - - bFound = 0; - i = 0; - while(i <= iMax && !bFound) { - size_t iCheck; - uchar *pComp = sz + i; - for(iCheck = 0 ; iCheck < pThis->iStrLen ; ++iCheck) - if(*(pComp + iCheck) != *(pThis->pBuf + iCheck)) - break; - if(iCheck == pThis->iStrLen) - bFound = 1; /* found! - else it wouldn't be equal */ - else - ++i; /* on to the next try */ - } - - return(bFound ? i : -1); -} - - -/* This is the same as rsCStrLocateInSzStr(), but does a case-insensitve - * comparison. - * TODO: over time, consolidate the two. - * rgerhards, 2008-02-28 - */ -int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz) -{ - int i; - int iMax; - int bFound; - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - assert(sz != NULL); - - if(pThis->iStrLen == 0) - return 0; - - /* compute the largest index where a match could occur - after all, - * the to-be-located string must be able to be present in the - * searched string (it needs its size ;)). - */ - iMax = strlen((char*)sz) - pThis->iStrLen; - - bFound = 0; - i = 0; - while(i <= iMax && !bFound) { - size_t iCheck; - uchar *pComp = sz + i; - for(iCheck = 0 ; iCheck < pThis->iStrLen ; ++iCheck) - if(tolower(*(pComp + iCheck)) != tolower(*(pThis->pBuf + iCheck))) - break; - if(iCheck == pThis->iStrLen) - bFound = 1; /* found! - else it wouldn't be equal */ - else - ++i; /* on to the next try */ - } - - return(bFound ? i : -1); -} - - -#if 0 /* read comment below why this is commented out. In short: for future use! */ -/* locate the first occurence of a standard sz string inside a rsCStr object. - * Returns the offset (0-bound) of this first occurrence. If not found, -1 is - * returned. - * rgerhards 2005-09-19 - * WARNING: I accidently created this function (I later noticed I didn't relly - * need it... I will not remove the function, as it probably is useful - * some time later. However, it is not fully tested, so start with testing - * it before you put it to first use). - */ -int rsCStrLocateSzStr(cstr_t *pThis, uchar *sz) -{ - int iLenSz; - int i; - int iMax; - int bFound; - rsCHECKVALIDOBJECT(pThis, OIDrsCStr); - - if(sz == NULL) - return 0; - - iLenSz = strlen((char*)sz); - if(iLenSz == 0) - return 0; - - /* compute the largest index where a match could occur - after all, - * the to-be-located string must be able to be present in the - * searched string (it needs its size ;)). - */ - iMax = pThis->iStrLen - iLenSz; - - bFound = 0; - i = 0; - while(i < iMax && !bFound) { - int iCheck; - uchar *pComp = pThis->pBuf + i; - for(iCheck = 0 ; iCheck < iLenSz ; ++iCheck) - if(*(pComp + iCheck) != *(sz + iCheck)) - break; - if(iCheck == iLenSz) - bFound = 1; /* found! - else it wouldn't be equal */ - else - ++i; /* on to the next try */ - } - - return(bFound ? i : -1); -} -#endif /* end comment out */ - - -/* our exit function. TODO: remove once converted to a class - * rgerhards, 2008-03-11 - */ -rsRetVal strExit() -{ - DEFiRet; - objRelease(regexp, LM_REGEXP_FILENAME); - RETiRet; -} - - -/* our init function. TODO: remove once converted to a class - */ -rsRetVal strInit() -{ - DEFiRet; - CHKiRet(objGetObjInterface(&obj)); - -finalize_it: - RETiRet; -} - - -/* - * Local variables: - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - * vi:set ai: - */ diff --git a/stringbuf.h b/stringbuf.h deleted file mode 100644 index e44e86e1..00000000 --- a/stringbuf.h +++ /dev/null @@ -1,168 +0,0 @@ -/*! \file stringbuf.h - * \brief The counted string object - * - * This is the byte-counted string class for rsyslog. It is a replacement - * for classical \0 terminated string functions. We introduce it in - * the hope it will make the program more secure, obtain some performance - * and, most importantly, lay they foundation for syslog-protocol, which - * requires strings to be able to handle embedded \0 characters. - * - * \author Rainer Gerhards - * \date 2005-09-07 - * Initial version begun. - * - * All functions in this "class" start with rsCStr (rsyslog Counted String). - * Copyright 2005 - * Rainer Gerhards and Adiscon GmbH. All Rights Reserved. - * - * 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef _STRINGBUF_H_INCLUDED__ -#define _STRINGBUF_H_INCLUDED__ 1 - -/** - * The dynamic string buffer object. - */ -typedef struct cstr_s -{ -#ifndef NDEBUG - rsObjID OID; /**< object ID */ -#endif - uchar *pBuf; /**< pointer to the string buffer, may be NULL if string is empty */ - uchar *pszBuf; /**< pointer to the sz version of the string (after it has been created )*/ - size_t iBufSize; /**< current maximum size of the string buffer */ - size_t iStrLen; /**< length of the string in characters. */ - size_t iAllocIncrement; /**< the amount of bytes the string should be expanded if it needs to */ -} cstr_t; - - -/** - * Construct a rsCStr object. - */ -rsRetVal rsCStrConstruct(cstr_t **ppThis); -rsRetVal rsCStrConstructFromszStr(cstr_t **ppThis, uchar *sz); -rsRetVal rsCStrConstructFromCStr(cstr_t **ppThis, cstr_t *pFrom); - -/** - * Destruct the string buffer object. - */ -void rsCStrDestruct(cstr_t **ppThis); - -/** - * Append a character to an existing string. If necessary, the - * method expands the string buffer. - * - * \param c Character to append to string. - */ -rsRetVal rsCStrAppendChar(cstr_t *pThis, uchar c); - -/** - * Truncate "n" number of characters from the end of the - * string. The buffer remains unchanged, just the - * string length is manipulated. This is for performance - * reasons. - */ -rsRetVal rsCStrTruncate(cstr_t *pThis, size_t nTrunc); - -rsRetVal rsCStrTrimTrailingWhiteSpace(cstr_t *pThis); - -/** - * Append a string to the buffer. For performance reasons, - * use rsCStrAppenStrWithLen() if you know the length. - * - * \param psz pointer to string to be appended. Must not be NULL. - */ -rsRetVal rsCStrAppendStr(cstr_t *pThis, uchar* psz); - -/** - * Append a string to the buffer. - * - * \param psz pointer to string to be appended. Must not be NULL. - * \param iStrLen the length of the string pointed to by psz - */ -rsRetVal rsCStrAppendStrWithLen(cstr_t *pThis, uchar* psz, size_t iStrLen); - -/** - * Set a new allocation incremet. This will influence - * the allocation the next time the string will be expanded. - * It can be set and changed at any time. If done immediately - * after custructing the StrB object, this will also be - * the inital allocation. - * - * \param iNewIncrement The new increment size - * - * \note It is possible to use a very low increment, e.g. 1 byte. - * This can generate a considerable overhead. We highly - * advise not to use an increment below 32 bytes, except - * if you are very well aware why you are doing it ;) - */ -void rsCStrSetAllocIncrement(cstr_t *pThis, int iNewIncrement); -#define rsCStrGetAllocIncrement(pThis) ((pThis)->iAllocIncrement) - -/** - * Append an integer to the string. No special formatting is - * done. - */ -rsRetVal rsCStrAppendInt(cstr_t *pThis, long i); - - -rsRetVal strExit(void); /* TODO: remove once we have a real object interface! */ -uchar* rsCStrGetSzStr(cstr_t *pThis); -uchar* rsCStrGetSzStrNoNULL(cstr_t *pThis); -rsRetVal rsCStrSetSzStr(cstr_t *pThis, uchar *pszNew); -rsRetVal rsCStrConvSzStrAndDestruct(cstr_t *pThis, uchar **ppSz, int bRetNULL); -int rsCStrCStrCmp(cstr_t *pCS1, cstr_t *pCS2); -int rsCStrSzStrCmp(cstr_t *pCS1, uchar *psz, size_t iLenSz); -int rsCStrOffsetSzStrCmp(cstr_t *pCS1, size_t iOffset, uchar *psz, size_t iLenSz); -int rsCStrLocateSzStr(cstr_t *pCStr, uchar *sz); -int rsCStrLocateInSzStr(cstr_t *pThis, uchar *sz); -int rsCStrCaseInsensitiveLocateInSzStr(cstr_t *pThis, uchar *sz); -int rsCStrStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz); -int rsCStrCaseInsensitveStartsWithSzStr(cstr_t *pCS1, uchar *psz, size_t iLenSz); -int rsCStrSzStrStartsWithCStr(cstr_t *pCS1, uchar *psz, size_t iLenSz); -int rsCStrSzStrMatchRegex(cstr_t *pCS1, uchar *psz); -rsRetVal rsCStrConvertToNumber(cstr_t *pStr, number_t *pNumber); -rsRetVal rsCStrConvertToBool(cstr_t *pStr, number_t *pBool); -rsRetVal rsCStrAppendCStr(cstr_t *pThis, cstr_t *pstrAppend); - -/* now come inline-like functions */ -#ifdef NDEBUG -# define rsCStrLen(x) ((int)((x)->iStrLen)) -#else - int rsCStrLen(cstr_t *pThis); -#endif - -#if STRINGBUF_TRIM_ALLOCSIZE != 1 -/* This is the normal case (see comment in rsCStrFinish!). In those cases, the function - * simply needs to do nothing, so that we can save us the function call. - * rgerhards, 2008-02-12 - */ -# define rsCStrFinish(pThis) RS_RET_OK -#else - /** - * Finish the string buffer dynamic allocation. - */ - rsRetVal rsCStrFinish(cstr_t *pThis); -#endif - -#define rsCStrGetBufBeg(x) ((x)->pBuf) - -rsRetVal strInit(); -rsRetVal strExit(); - -#endif /* single include */ diff --git a/syslogd-types.h b/syslogd-types.h deleted file mode 100644 index 9aea3778..00000000 --- a/syslogd-types.h +++ /dev/null @@ -1,104 +0,0 @@ -/* syslogd-type.h - * This file contains type defintions used by syslogd and its modules. - * It is a required input for any module. - * - * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) - * - * Copyright 2007 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 . - * - * A copy of the GPL can be found in the file "COPYING" in this distribution. - */ -#ifndef SYSLOGD_TYPES_INCLUDED -#define SYSLOGD_TYPES_INCLUDED 1 - -#include "stringbuf.h" -//#include "net.h" -#include -#if HAVE_SYSLOG_H -#include -#endif - -#define FALSE 0 -#define TRUE 1 - -#ifdef UT_NAMESIZE -# define UNAMESZ UT_NAMESIZE /* length of a login name */ -#else -# define UNAMESZ 8 /* length of a login name */ -#endif -#define MAXUNAMES 20 /* maximum number of user names */ -#define MAXFNAME 200 /* max file pathname length */ - -#define _DB_MAXDBLEN 128 /* maximum number of db */ -#define _DB_MAXUNAMELEN 128 /* maximum number of user name */ -#define _DB_MAXPWDLEN 128 /* maximum number of user's pass */ -#define _DB_DELAYTIMEONERROR 20 /* If an error occur we stop logging until - a delayed time is over */ - - -/* we define features of the syslog code. This features can be used - * to check if modules are compatible with them - and possible other - * applications I do not yet envision. -- rgerhards, 2007-07-24 - */ -typedef enum _syslogFeature { - sFEATURERepeatedMsgReduction = 1 -} syslogFeature; - -/* we define our own facility and severities */ -/* facility and severity codes */ -typedef struct _syslogCode { - char *c_name; - int c_val; -} syslogCODE; - -/* values for host comparisons specified with host selector blocks - * (+host, -host). rgerhards 2005-10-18. - */ -enum _EHostnameCmpMode { - HN_NO_COMP = 0, /* do not compare hostname */ - HN_COMP_MATCH = 1, /* hostname must match */ - HN_COMP_NOMATCH = 2 /* hostname must NOT match */ -}; -typedef enum _EHostnameCmpMode EHostnameCmpMode; - -/* rgerhards 2004-11-11: the following structure represents - * a time as it is used in syslog. - */ -struct syslogTime { - int timeType; /* 0 - unitinialized , 1 - RFC 3164, 2 - syslog-protocol */ - int year; - int month; - int day; - int hour; /* 24 hour clock */ - int minute; - int second; - int secfrac; /* fractional seconds (must be 32 bit!) */ - int secfracPrecision; - char OffsetMode; /* UTC offset + or - */ - char OffsetHour; /* UTC offset in hours */ - int OffsetMinute; /* UTC offset in minutes */ - /* full UTC offset minutes = OffsetHours*60 + OffsetMinute. Then use - * OffsetMode to know the direction. - */ -}; -typedef struct syslogTime syslogTime_t; - -#endif /* #ifndef SYSLOGD_TYPES_INCLUDED */ -/* - * vi:set ai: - */ diff --git a/threads.h b/threads.h index aa6a5c28..78924d95 100644 --- a/threads.h +++ b/threads.h @@ -23,16 +23,15 @@ #ifndef THREADS_H_INCLUDED #define THREADS_H_INCLUDED - /* the thread object */ -typedef struct thrdInfo { +struct thrdInfo { pthread_mutex_t *mutTermOK; /* Is it ok to terminate that thread now? */ int bIsActive; /* Is thread running? */ int bShallStop; /* set to 1 if the thread should be stopped ? */ rsRetVal (*pUsrThrdMain)(struct thrdInfo*); /* user thread main to be called in new thread */ rsRetVal (*pAfterRun)(struct thrdInfo*); /* cleanup function */ pthread_t thrdID; -} thrdInfo_t; +}; /* prototypes */ rsRetVal thrdExit(void); -- cgit