diff options
-rw-r--r-- | ChangeLog | 22 | ||||
-rw-r--r-- | action.c | 6 | ||||
-rw-r--r-- | action.h | 3 | ||||
-rw-r--r-- | configure.ac | 15 | ||||
-rw-r--r-- | doc/dev_oplugins.html | 5 | ||||
-rw-r--r-- | doc/manual.html | 2 | ||||
-rw-r--r-- | doc/omfile.html | 2 | ||||
-rw-r--r-- | doc/omsnmp.html | 148 | ||||
-rw-r--r-- | doc/property_replacer.html | 244 | ||||
-rw-r--r-- | doc/rsyslog_conf_templates.html | 59 | ||||
-rw-r--r-- | grammar/grammar.y | 17 | ||||
-rw-r--r-- | grammar/lexer.l | 8 | ||||
-rw-r--r-- | grammar/rainerscript.c | 57 | ||||
-rw-r--r-- | grammar/rainerscript.h | 21 | ||||
-rw-r--r-- | plugins/imtcp/imtcp.c | 1 | ||||
-rw-r--r-- | runtime/msg.c | 77 | ||||
-rw-r--r-- | runtime/msg.h | 2 | ||||
-rw-r--r-- | runtime/objomsr.c | 3 | ||||
-rw-r--r-- | runtime/objomsr.h | 7 | ||||
-rw-r--r-- | runtime/rsconf.c | 14 | ||||
-rw-r--r-- | runtime/rsyslog.h | 1 | ||||
-rw-r--r-- | template.c | 576 | ||||
-rw-r--r-- | template.h | 13 | ||||
-rw-r--r-- | tools/Makefile.am | 2 |
24 files changed, 1256 insertions, 49 deletions
@@ -1,5 +1,8 @@ --------------------------------------------------------------------------- -Version 6.5.0 [devel] 2012-0?-?? +Version 6.5.1 [devel] 2012-08-?? +- bugfix: imtcp could abort on exit due to invalid free() +--------------------------------------------------------------------------- +Version 6.5.0 [devel] 2012-08-28 - imrelp now supports non-cancel thread termination (but now requires at least librelp 1.0.1) - implemented freeCnf() module interface @@ -18,12 +21,29 @@ Version 6.5.0 [devel] 2012-0?-?? Thanks to Miloslav Trmač for the patch - $SystemLogParseTrusted config file option Thanks to Milan Bartos for the patch +- added template config directive +- added new uuid message property + Thanks to Jérôme Renard for the idea and patches. + Note: patches were released under ASL 2.0, see + http://bugzilla.adiscon.com/show_bug.cgi?id=353 --------------------------------------------------------------------------- Version 6.4.1 [V6-STABLE] 2012-08-?? - bugfix: multiple main queues with same queue file name were not detected This lead to queue file corruption. While the root cause is a config error, it is a bug that this important and hard to find config error was not detected by rsyslog. +- bugfix: "jsonf" property replacer option did generate invalid JSON + in JSON, we have "fieldname":"value", but the option emitted + "fieldname"="value". Interestingly, this was accepted by a couple + of sinks, most importantly elasticsearch. Now the correct format is + emitted, which causes a remote chance that some things that relied on + the wrong format will break. + Thanks to Miloslav Trmač for the patch +- change $!all-json did emit an empty (thus non-JSON) string if no libee + data was present. It now emits {} and thus valid JSON. There is a + small risk that this may break some things that relied on the previous + inconsistency. + Thanks to Miloslav Trmač for the patch --------------------------------------------------------------------------- Version 6.4.0 [V6-STABLE] 2012-08-20 - THIS IS THE FIRST VERSION OF THE 6.4.x STABLE BRANCH @@ -823,6 +823,9 @@ static rsRetVal prepareDoActionParams(action_t *pAction, batch_obj_t *pElem) case ACT_MSG_PASSING: pElem->staticActParams[i] = (void*) pMsg; break; + case ACT_JSON_PASSING: + // TODO: implement + break; default:dbgprintf("software bug/error: unknown pAction->eParamPassing %d in prepareDoActionParams\n", (int) pAction->eParamPassing); assert(0); /* software bug if this happens! */ @@ -874,6 +877,7 @@ static rsRetVal releaseBatch(action_t *pAction, batch_t *pBatch) break; case ACT_STRING_PASSING: case ACT_MSG_PASSING: + case ACT_JSON_PASSING: /* nothing to do in that case */ /* TODO ... and yet we do something ;) This is considered not * really needed, but I was not bold enough to remove that while @@ -1933,6 +1937,8 @@ addAction(action_t **ppAction, modInfo_t *pMod, void *pModData, pAction->eParamPassing = ACT_ARRAY_PASSING; } else if(iTplOpts & OMSR_TPL_AS_MSG) { pAction->eParamPassing = ACT_MSG_PASSING; + } else if(iTplOpts & OMSR_TPL_AS_JSON) { + pAction->eParamPassing = ACT_JSON_PASSING; } else { pAction->eParamPassing = ACT_STRING_PASSING; } @@ -74,7 +74,8 @@ struct action_s { int f_repeatcount; /* number of "repeated" msgs */ rsRetVal (*submitToActQ)(action_t *, batch_t *);/* function submit message to action queue */ rsRetVal (*qConstruct)(struct queue_s *pThis); - enum { ACT_STRING_PASSING = 0, ACT_ARRAY_PASSING = 1, ACT_MSG_PASSING } + enum { ACT_STRING_PASSING = 0, ACT_ARRAY_PASSING = 1, ACT_MSG_PASSING = 2, + ACT_JSON_PASSING = 3} eParamPassing; /* mode of parameter passing to action */ int iNumTpls; /* number of array entries for template element below */ struct template **ppTpl;/* array of template to use - strings must be passed to doAction diff --git a/configure.ac b/configure.ac index 499b2de7..d0accfeb 100644 --- a/configure.ac +++ b/configure.ac @@ -677,6 +677,21 @@ AM_CONDITIONAL(ENABLE_OMLIBDBI, test x$enable_libdbi = xyes) AC_SUBST(LIBDBI_CFLAGS) AC_SUBST(LIBDBI_LIBS) +# libuuid support +AC_CHECK_HEADERS( + [uuid/uuid.h],, + [AC_MSG_FAILURE([libuuid is missing])] +) +AC_CHECK_LIB( + [uuid], + [uuid_generate], + [LIBUUID_CFLAGS="" + LIBUUID_LIBS="-luuid" + ], + [AC_MSG_FAILURE([libuuid library is missing])] +) +AC_SUBST(LIBUUID_CFLAGS) +AC_SUBST(LIBUUID_LIBS) # SNMP support AC_ARG_ENABLE(snmp, diff --git a/doc/dev_oplugins.html b/doc/dev_oplugins.html index 63c186a3..b33b67f9 100644 --- a/doc/dev_oplugins.html +++ b/doc/dev_oplugins.html @@ -143,6 +143,11 @@ omstdout, you can see how a plugin may deal with the situation. array-passing capability not blindly be used.</b> In such cases, we can not guard the plugin from segfaulting and if the plugin (as currently always) is run within rsyslog's process space, that results in a segfault for rsyslog. So do not do this. +<p>Another possible mode is OMSR_TPL_AS_JSON, where instead of the template +a json-c memory object tree is passed to the module. The module can extract data +via json-c API calls. It MUST NOT modify the provided structure. This mode is +primarily aimed at plugins that need to process tree-like data, as found +for example in MongoDB or ElasticSearch. <h3>Batching of Messages</h3> <p>Starting with rsyslog 4.3.x, batching of output messages is supported. Previously, only a single-message interface was supported. diff --git a/doc/manual.html b/doc/manual.html index de05d7ae..9a8644c9 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -19,7 +19,7 @@ rsyslog support</a> available directly from the source!</p> <p><b>Please visit the <a href="http://www.rsyslog.com/sponsors">rsyslog sponsor's page</a> to honor the project sponsors or become one yourself!</b> We are very grateful for any help towards the project goals.</p> -<p><b>This documentation is for version 6.4.0 (stable branch) of rsyslog.</b> +<p><b>This documentation is for version 6.5.0 (devel branch) of rsyslog.</b> Visit the <i><a href="http://www.rsyslog.com/status">rsyslog status page</a></i></b> to obtain current version information and project status. </p><p><b>If you like rsyslog, you might diff --git a/doc/omfile.html b/doc/omfile.html index bdd1ebc6..23ecc034 100644 --- a/doc/omfile.html +++ b/doc/omfile.html @@ -84,7 +84,7 @@ *.* action(type="omfile" DirCreateMode="0700" FileCreateMode="0644" -File= "/var/log/messages") +File="/var/log/messages") </textarea> <br><br> diff --git a/doc/omsnmp.html b/doc/omsnmp.html index b38a594f..202bb5bb 100644 --- a/doc/omsnmp.html +++ b/doc/omsnmp.html @@ -22,6 +22,153 @@ developer (headers) package installed. </p> <p> </p> <p><b>Configuration Directives</b>:</p> <ul> + <li><strong>transport </strong>(This parameter is optional, the + default value is "udp")<br> + <br> + Defines the transport type you wish to use. Technically we can support all + transport types which are supported by NET-SNMP. <br> + To name a few possible values: <br> + <br> + udp, tcp, udp6, tcp6, icmp, icmp6 ...<br> + <br> + Example: <strong>transport udp<br> + </strong></li> + <li><strong>server</strong><br> + <br> + This can be a hostname or ip address, and is our snmp target host. This + parameter is required, if the snmptarget is not defined, nothing will be + send. <br> + <br> + Example: <strong>server server.domain.xxx</strong><br> + </li> + <li><strong>port </strong>(This parameter is optional, the + default value is "162")<br> + <br> + The port which will be used, common values are port 162 or 161. <br> + <br> + Example: <strong>port 162</strong><br> + </li> + <li><strong>version </strong>(This parameter is optional, the + default value is "1")<br> + <br> + There can only be two choices for this parameter for now. <br> + 0 means SNMPv1 will be used.<br> + 1 means SNMPv2c will be used. <br> + Any other value will default to 1. <br> + <br> + Example: <strong>version 1</strong><br> + </li> + <li><strong>community </strong>(This parameter is optional, the + default value is "public")<br> + <br> + This sets the used SNMP Community.<br> + <br> + Example:<strong> community public<br> + </strong><br> + </li> + <li><strong>trapoid </strong>(This parameter is + optional, the default value is "1.3.6.1.4.1.19406.1.2.1" which means + "ADISCON-MONITORWARE-MIB::syslogtrap")<br> + This configuration parameter is used for <strong>SNMPv2</strong> only.<br> + <br> + This is the OID which defines the trap-type, or notifcation-type rsyslog + uses to send the trap. <br> + In order to decode this OID, you will need to have the + ADISCON-MONITORWARE-MIB and ADISCON-MIB mibs installed on the receiver side. Downloads of these mib files + can be found here: <br> + <a href="http://www.adiscon.org/download/ADISCON-MIB.txt"> + http://www.adiscon.org/download/ADISCON-MIB.txt</a><br> + <a href="http://www.adiscon.org/download/ADISCON-MONITORWARE-MIB.txt"> + http://www.adiscon.org/download/ADISCON-MONITORWARE-MIB.txt</a><br> + <br> + Thanks to the net-snmp + mailinglist for the help and the recommendations ;).<br> + <br> + Example: <strong>trapoid 1.3.6.1.4.1.19406.1.2.1<br> + </strong>If you have this MIBS installed, you can also configured with the + OID Name: <strong>trapoid ADISCON-MONITORWARE-MIB::syslogtrap<br> + </strong> + </li> + <li><strong>messageoid </strong>(This parameter is + optional, the default value is "1.3.6.1.4.1.19406.1.1.2.1" which means + "ADISCON-MONITORWARE-MIB::syslogMsg")<br> + <br> + This OID will be used as a variable, type "OCTET STRING". This variable will + contain up to 255 characters of the original syslog message including syslog header. It is recommend to + use the default OID. <br> + In order to decode this OID, you will need to have the + ADISCON-MONITORWARE-MIB and ADISCON-MIB mibs installed on the receiver side. + To download these custom mibs, see the description of <strong>$actionsnmptrapoid. + </strong><br> + <br> + Example: <strong>messageoid 1.3.6.1.4.1.19406.1.1.2.1<br> + </strong>If you have this MIBS installed, you can also configured with the + OID Name: <strong>messageoid + ADISCON-MONITORWARE-MIB::syslogMsg<br> + </strong><br> + </li> + <li><strong>enterpriseoid </strong>(This parameter is optional, + the default value is "1.3.6.1.4.1.3.1.1" which means "enterprises.cmu.1.1")<br> + <br> + Customize this value if needed. I recommend to use the default value unless + you require to use a different OID. <br> + This configuration parameter is used for <strong>SNMPv1</strong> only. It + has no effect if <strong>SNMPv2</strong> is used. <br> + <br> + Example: <strong>enterpriseoid 1.3.6.1.4.1.3.1.1 <br> + </strong><br> + </li> + <li><strong>specifictype </strong>(This parameter is optional, + the default value is "0")<strong> </strong><br> + <br> + This is the specific trap number. This configuration parameter is used for + <strong>SNMPv1</strong> only. It has no effect if <strong>SNMPv2</strong> is + used. <br> + <br> + Example: <strong>specifictype 0<br> + </strong><br> + </li> + <li><strong>traptype</strong> (This parameter is optional, the + default value is "6" which means SNMP_TRAP_ENTERPRISESPECIFIC) <br> + <br> + There are only 7 Possible trap types defined which can be used here. These + trap types are: <br> + 0 = SNMP_TRAP_COLDSTART<br> + 1 = SNMP_TRAP_WARMSTART<br> + 2 = SNMP_TRAP_LINKDOWN<br> + 3 = SNMP_TRAP_LINKUP<br> + 4 = SNMP_TRAP_AUTHFAIL<br> + 5 = SNMP_TRAP_EGPNEIGHBORLOSS<br> + 6 = SNMP_TRAP_ENTERPRISESPECIFIC<br> + <br> + Any other value will default to 6 automatically. This configuration + parameter is used for <strong>SNMPv1</strong> only. It has no effect if + <strong>SNMPv2</strong> is used. <br> + <br> + Example: <strong>traptype 6</strong><br> + </li> + <li><strong>template </strong>[templateName]<strong> </strong><br> + <br> + sets a new default template for file actions. + </li> +</ul> +<p> </p> +<p><b>Caveats/Known Bugs:</b></p><ul><li>In order to decode the custom OIDs, you + will need to have the adiscon mibs installed. </li></ul> +<p><b>Sample:</b></p> +<p>The following commands send every message as a snmp trap.</p> +<textarea rows="10" cols="60">Module (path="omsnmp") +*.* action( type="omsnmp" +transport="udp" +target="localhost" +targetport="162" +version="1" +community="public") + +</textarea> + +<p><b>Legacy Configuration Directives</b>:</p> +<ul> <li><strong>$actionsnmptransport </strong>(This parameter is optional, the default value is "udp")<br> <br> @@ -164,6 +311,7 @@ $actionsnmpcommunity public *.* :omsnmp: </textarea> + <p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p> <p><font size="2">This documentation is part of the diff --git a/doc/property_replacer.html b/doc/property_replacer.html index 4c92bf4c..c6464a3b 100644 --- a/doc/property_replacer.html +++ b/doc/property_replacer.html @@ -236,7 +236,7 @@ or none if no rule with this field did match. <p>Properties starting with a $-sign are so-called system properties. These do NOT stem from the message but are rather internally-generated.</p> -<h2>Character Positions</h2> +<h2>Legacy Character Positions</h2> <p><b><code>FromChar</code></b> and <b><code>toChar</code></b> are used to build substrings. They specify the offset within the string that should be copied. Offset counting starts at 1, so if you need to @@ -304,15 +304,6 @@ fields in the property is requested. The field number must be placed in the "ToChar" parameter. An example where the 3rd field (delimited by TAB) from the msg property is extracted is as follows: "%msg:F:3%". The same example with semicolon as delimiter is "%msg:F,59:3%".</p> -<p>The use of fields does not permit to select substrings, what is rather -unfortunate. To solve this issue, starting with 6.3.9, fromPos and toPos -can be specified for strings as well. However, the syntax is quite ugly, but -it was the only way to integrate this functonality into the already-existing -system. To do so, use ",fromPos" and ",toPos" during field extraction. -Let's assume you want to extract the substring from position 5 to 9 in the previous -example. Then, the syntax is as follows: "%msg:F,59,5:3,9%". As you can see, -"F,59" means field-mode, with semicolon delimiter and ",5" means starting -at position 5. Then "3,9" means field 3 and string extraction to position 9. <p>Please note that the special characters "F" and "R" are case-sensitive. Only upper case works, lower case will return an error. There are no white spaces permitted inside the sequence (that will lead @@ -342,6 +333,239 @@ It is modeled after perl compatible regular expressions. <h2>Property Options</h2> <b><code>property options</code></b> are +case-insensitive. They are available as of version 6.5.0. +Currently, the following options are defined: +<p></p> +<table> +<tbody> +<tr> +<td><b>Name</b></td> +<td>New format. Name of the template / property / constant.</td> +</tr> +<tr> +<td><b>Outname</b></td> +<td>This field permits to specify a field name for structured-data emitting property replacer options. +If used for a constant a template with line style, unpredictable behaviour can occur.</td> +</tr> +<tr> +<td><b>CaseConversion</b></td> +<td>New format. Additional values below.</td> +</tr> +<tr> +<td>upper</td> +<td>convert property to lowercase only</td> +</tr> +<tr> +<td>lower</td> +<td>convert property text to uppercase only</td> +</tr> +<tr> +<td><b>DateFormat</b></td> +<td>New format, additional parameter is needed. See below.</td> +</tr> +<tr> +<td>mysql</td> +<td>format as mysql date</td> +</tr> +<tr> +<td>pgsql</td> +<td>format as pgsql date</td> +</tr> +<tr> +<td>rfc3164</td> +<td>format as RFC 3164 date</td> +</tr> +<tr> +<tr> +<td valign="top">rfc3164-buggyday</td> +<td>similar to date-rfc3164, but emulates a common coding error: RFC 3164 demands +that a space is written for single-digit days. With this option, a zero is +written instead. This format seems to be used by syslog-ng and the +date-rfc3164-buggyday option can be used in migration scenarios where otherwise +lots of scripts would need to be adjusted. It is recommended <i>not</i> to use this +option when forwarding to remote hosts - they may treat the date as invalid +(especially when parsing strictly according to RFC 3164).</td> +<br><i>This feature was introduced in rsyslog 4.6.2 and v4 versions above and +5.5.3 and all versions above.</i> +</tr> +<tr> +<td>rfc3339</td> +<td>format as RFC 3339 date</td> +</tr> +<tr> +<td>unixtimestamp</td> +<td>format as unix timestamp (seconds since epoch)</td> +</tr> +<tr> +<td>subseconds</td> +<td>just the subseconds of a timestamp (always 0 for a low precision timestamp)</td> +</tr> +<tr> +<td><b>ControlCharacters</b></td> +<td>Option values for how to process control characters</td> +</tr> +<tr> +<td valign="top">escape</td> +<td>replace control characters (ASCII value 127 and values +less then 32) with an escape sequence. The sequnce is +"#<charval>" where charval is the 3-digit decimal value +of the control character. For example, a tabulator would be replaced by +"#009".<br> +Note: using this option requires that <a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a> +is set to off.</td> +</tr> +<tr> +<td valign="top">space</td> +<td>replace control characters by spaces<br> +Note: using this option requires that <a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a> +is set to off.</td> +</tr> +<tr> +<td valign="top">drop</td> +<td>drop control characters - the resulting string will +neither contain control characters, escape sequences nor any other +replacement character like space.<br> +Note: using this option requires that <a href="rsconf1_escapecontrolcharactersonreceive.html">$EscapeControlCharactersOnReceive</a> +is set to off.</td> +</tr> +<tr> +<td><b>SecurePath</b></td> +<td>Option values for securing path templates.</td> +</tr> +<tr> +<td valign="top">drop</td> +<td>Drops slashes inside the field (e.g. "a/b" becomes "ab"). +Useful for secure pathname generation (with dynafiles). +</td> +</tr> +<tr> +<td valign="top">replace</td> +<td>Replace slashes inside the field by an underscore. (e.g. "a/b" becomes "a_b"). +Useful for secure pathname generation (with dynafiles). +</td> +</tr> +<tr> +<td><b>Format</b></td> +<td>Option values for the general output format.</td> +</tr> +<tr> +<td>json</td> +<td>encode the value so that it can be used inside a JSON field. This means +that several characters (according to the JSON spec) are being escaped, for +example US-ASCII LF is replaced by "\n". +The json option cannot be used together with either jsonf or csv options. +</td> +</tr> +<tr> +<td>jsonf</td> +<td><i>(available in 6.3.9+)</i> +This signifies that the property should be expressed as a json <b>f</b>ield. +That means not only the property is written, but rather a complete json field in +the format<br> +"fieldname"="value"</b> +where "filedname" is the assigend field name (or the property name if none was assigned) +and value is the end result of property replacer operation. Note that value supports +all property replacer options, like substrings, case converson and the like. +Values are properly json-escaped. However, field names are (currently) not. It is +expected that proper field names are configured. +The jsonf option cannot be used together with either json or csv options. +</td> +</tr> +<tr> +<td valign="top">csv</td> +<td>formats the resulting field (after all modifications) in CSV format +as specified in <a href="http://www.ietf.org/rfc/rfc4180.txt">RFC 4180</a>. +Rsyslog will always use double quotes. Note that in order to have full CSV-formatted +text, you need to define a proper template. An example is this one: +<br>$template csvline,"%syslogtag:::csv%,%msg:::csv%" +<br>Most importantly, you need to provide the commas between the fields +inside the template. +The csv option cannot be used together with either json or jsonf options. +<br><i>This feature was introduced in rsyslog 4.1.6.</i> +</td> +</tr> +<tr> +<td><b>droplastlf</b></td> +<td>The last LF in the message (if any), is dropped. +Especially useful for PIX.</td> +</tr> +<tr> +<td valign="top"><b>spifno1stsp</b></td> +<td>This option looks scary and should probably not be used by a user. For any field +given, it returns either a single space character or no character at all. Field content +is never returned. A space is returned if (and only if) the first character of the +field's content is NOT a space. This option is kind of a hack to solve a problem rooted +in RFC 3164: 3164 specifies no delimiter between the syslog tag sequence and the actual +message text. Almost all implementation in fact delemit the two by a space. As of +RFC 3164, this space is part of the message text itself. This leads to a problem when +building the message (e.g. when writing to disk or forwarding). Should a delimiting +space be included if the message does not start with one? If not, the tag is immediately +followed by another non-space character, which can lead some log parsers to misinterpret +what is the tag and what the message. The problem finally surfaced when the klog module +was restructured and the tag correctly written. It exists with other message sources, +too. The solution was the introduction of this special property replacer option. Now, +the default template can contain a conditional space, which exists only if the +message does not start with one. While this does not solve all issues, it should +work good enough in the far majority of all cases. If you read this text and have +no idea of what it is talking about - relax: this is a good indication you will never +need this option. Simply forget about it ;) +</td> +</tr> +<tr> +<td></td> +<td></td> +</tr> +<tr> +<td><b>New character position</b></td> +<td>In addition to the above mentioned Character Positions in the legacy format, +positions can be determined by specifying the correct options for the properties. +Again, this is mostly for using the list format.</td> +</tr> +<tr> +<td>position.From</td> +<td>Character position in the property to start from.</td> +</tr> +<tr> +<td>position.To</td> +<td>Character position that determines the end for extraction. If the value is "$" +then the end of the string will be used.</td> +</tr> +<tr> +<td>field.Number</td> +<td>The number of the field, which should be used for the search operation with Regex.</td> +</tr> +<tr> +<td>field.Delimiter</td> +<td>The Character that should delimit a field. Example: ",". Everything in a +property until this character is considered a field.</td> +</tr> +<tr> +<td>regex.Expression</td> +<td>Value to be compared to property.</td> +</tr> +<tr> +<td>regex.Type</td> +<td>Values BRE or ERE</td> +</tr> +<tr> +<td>regex.NoMatchMode</td> +<td>DFLT, BLANK, ZERO, FIELD</td> +</tr> +<tr> +<td>regex.Match</td> +<td>Match to use.</td> +</tr> +<tr> +<td>regex.Submatch</td> +<td>Submatch to use. Values 0-9 whereas 0 = All</td> +</tr> +</tbody> +</table> + + + +<h2>Legacy Property Options</h2> +<b><code>property options</code></b> are case-insensitive. Currently, the following options are defined: <p></p> <table> diff --git a/doc/rsyslog_conf_templates.html b/doc/rsyslog_conf_templates.html index bd0b3253..b97f6609 100644 --- a/doc/rsyslog_conf_templates.html +++ b/doc/rsyslog_conf_templates.html @@ -33,26 +33,31 @@ string generator module, you need to know how to call it. Each such module has a which you need to know (look it up in the module doc or ask the developer). Let's assume that "mystrgen" is the module name. Then you can define a template for that strgen in the following way: + +<blockquote><code>template(name="MyTemplateName" type="plugin" string="mystrgen")</code></blockquote> +<p>Legacy example:</p> <blockquote><code>$template MyTemplateName,=mystrgen</code></blockquote> (Of course, you must have first loaded the module via $ModLoad). -<p>The important part is the equal sign: it tells the rsyslog config parser that +<p>The important part is the equal sign in the legacy format: it tells the rsyslog config parser that no string follows but a strgen module name. <p>There are no additional parameters but the module name supported. This is because there is no way to customize anything inside such a "template" other than by modifying the code of the string generator. <p>So for most use cases, string-generator module based templates are <b>not</b> -the route to take. Usually, us use <b>string based templates</b> instead. +the route to take. Usually, we use <b>string based templates</b> instead. This is what the rest of the documentation now talks about. <p>A template consists of a template directive, a name, the actual template text and optional options. A sample is:</p> +<blockquote><code>template(name="MyTemplateName" type="string" string="Example: Text %property% some more text\n" options)</code></blockquote> +<p>Legacy example:</p> <blockquote><code>$template MyTemplateName,"\7Text %property% some more text\n",<options></code></blockquote> -<p>The "$template" is the template directive. It tells rsyslog +<p>The "template" (legacy: $template) is the template directive. It tells rsyslog that this line contains a template. "MyTemplateName" is the template name. All -other config lines refer to this name. The text within quotes is the +other config lines refer to this name. The text within "string" is the actual template text. The backslash is an escape character, much as it is in C. It does all these "cool" things. For example, \7 rings the bell (this is an ASCII value), \n is a new line. C programmers and perl @@ -69,24 +74,30 @@ on this is below, on some lines of the property replacer.<br> <br> The <options> part is optional. It carries options influencing the template as whole. See details below. Be sure NOT to -mistake template options with property options - the later ones are +mistake template options with property options - the latter ones are processed by the property replacer and apply to a SINGLE property, only (and not the whole template).<br> <br> Template options are case-insensitive. Currently defined are: </p> -<p><b>sql</b> - format the string suitable for a SQL +<p><b>option.sql</b> - format the string suitable for a SQL statement in MySQL format. This will replace single quotes ("'") and the backslash character by their backslash-escaped counterpart ("\'" and "\\") inside each field. Please note that in MySQL configuration, the <code class="literal">NO_BACKSLASH_ESCAPES</code> mode must be turned off for this format to work (this is the default).</p> -<p><b>stdsql</b> - format the string suitable for a +<p><b>option.stdsql</b> - format the string suitable for a SQL statement that is to be sent to a standards-compliant sql server. This will replace single quotes ("'") by two single quotes ("''") inside each field. You must use stdsql together with MySQL if in MySQL configuration the <code class="literal">NO_BACKSLASH_ESCAPES</code> is turned on.</p> +<p><b>option.json</b> - format the string suitable for a +json statement. +This will replace single quotes ("'") by two single quotes ("''") +inside each field.</p> +<p>At no time, multiple template option should be used. This can cause +unpredictable behaviour and is against all logic.</p> <p>Either the <b>sql</b> or <b>stdsql</b> option <b>must</b> be specified when a template is used for writing to a database, otherwise injection might occur. Please note @@ -120,11 +131,41 @@ $template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\n"<br> <br> Properties can be accessed by the <a href="property_replacer.html">property replacer</a> (see there for details).</p> -<p><b>Please note that templates can also by +<p>Templates can be used in the form of a <b>list</b> as well. This has been +introduced with <b>6.5.0</b> The list consists of two parts which are either +a <b>constant</b> or a <b>property</b>. The constants +are taking the part of "text" that you usually enter in string-based templates. +The properties stay variable, as they are a substitute for different values of a +certain type. This type of template is extremely useful for complicated cases, +as it helps you to easily keep an overview over the template. Though, it has +the disadvantage of needing more effort to create it.</p> +<br>Config example: +<br><blockquote><code>template(name="MyTemplate" type="list" option.json="off") { + <br>constant(value="Test: ") + <br>property(name="msg" outname="mymessage") + <br>constant(value=" --!!!-- ") + <br>property(name="timereported" dateFormat="rfc3339" caseConversion="lower") + <br>constant(value="\n") + <br>} +</code></blockquote> +<p>First, the general template option will be defined. The values of the template +itself get defined in the curly brackets. As it can be seen, we have constants +and properties in exchange. Whereas constants will be filled with a value and probably +some options, properties do direct to a property and the options that could be needed +additional format definitions.</p> +<p>We suggest to use separate lines for all constants and properties. This +helps to keep a good overview over the different parts of the template. +Though, writing it in a single line will work, it is much harder to debug +if anything goes wrong with the template. </p> + + +<p><b>Please note that templates can also be used to generate selector lines with dynamic file names.</b> For example, if you would like to split syslog messages from different hosts to different files (one per host), you can define the following template:</p> +<blockquote><code>template (name="DynFile" type="string" string="/var/log/system-%HOSTNAME%.log")</code></blockquote> +<p>Legacy example:</p> <blockquote><code>$template DynFile,"/var/log/system-%HOSTNAME%.log"</code></blockquote> <p>This template can then be used when defining an output @@ -169,7 +210,7 @@ out, but this may happen.</li> is meant to be written to a log file. Do <b>not</b> use for production or remote forwarding.</li> </ul> -<h3>String-based Template Samples</h3> +<h3>Legacy String-based Template Samples</h3> <p>This section provides some sample of what the default formats would look as a text-based template. Hopefully, their description is self-explanatory. Note that each $Template statement is on a <b>single</b> line, but probably broken diff --git a/grammar/grammar.y b/grammar/grammar.y index 402b1a57..8371f854 100644 --- a/grammar/grammar.y +++ b/grammar/grammar.y @@ -49,6 +49,7 @@ extern int yyerror(char*); enum cnfobjType objType; struct cnfobj *obj; struct nvlst *nvlst; + struct objlst *objlst; struct cnfactlst *actlst; struct cnfexpr *expr; struct cnfrule *rule; @@ -63,6 +64,9 @@ extern int yyerror(char*); %token ENDOBJ %token <s> CFSYSLINE %token BEGIN_ACTION +%token BEGIN_PROPERTY +%token BEGIN_CONSTANT +%token BEGIN_TPL %token STOP %token <s> LEGACY_ACTION %token <s> PRIFILT @@ -89,7 +93,8 @@ extern int yyerror(char*); %token CMP_STARTSWITHI %type <nvlst> nv nvlst -%type <obj> obj +%type <obj> obj property constant +%type <objlst> propconst %type <actlst> actlst %type <actlst> act %type <s> cfsysline @@ -128,6 +133,16 @@ conf: /* empty (to end recursion) */ | conf BSD_HOST_SELECTOR { cnfDoBSDHost($2); } obj: BEGINOBJ nvlst ENDOBJ { $$ = cnfobjNew($1, $2); } | BEGIN_ACTION nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_ACTION, $2); } + | BEGIN_TPL nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_TPL, $2); } + | BEGIN_TPL nvlst ENDOBJ '{' propconst '}' + { $$ = cnfobjNew(CNFOBJ_TPL, $2); + $$->subobjs = $5; + } +propconst: { $$ = NULL; } + | propconst property { $$ = objlstAdd($1, $2); } + | propconst constant { $$ = objlstAdd($1, $2); } +property: BEGIN_PROPERTY nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_PROPERTY, $2); } +constant: BEGIN_CONSTANT nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_CONSTANT, $2); } cfsysline: CFSYSLINE { $$ = $1; } nvlst: { $$ = NULL; } | nvlst nv { $2->next = $1; $$ = $2; } diff --git a/grammar/lexer.l b/grammar/lexer.l index e688ffce..c5e7bf7d 100644 --- a/grammar/lexer.l +++ b/grammar/lexer.l @@ -9,7 +9,7 @@ * cases. So while we hope that cfsysline support can be dropped some time in * the future, we will probably keep these useful constructs. * - * Copyright 2011 Rainer Gerhards and Adiscon GmbH. + * Copyright 2011-2012 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -151,6 +151,12 @@ int fileno(FILE *stream); BEGIN INITIAL; } "global"[ \n\t]*"(" { yylval.objType = CNFOBJ_GLOBAL; BEGIN INOBJ; return BEGINOBJ; } +"template"[ \n\t]*"(" { yylval.objType = CNFOBJ_TPL; + BEGIN INOBJ; return BEGIN_TPL; } +"property"[ \n\t]*"(" { yylval.objType = CNFOBJ_PROPERTY; + BEGIN INOBJ; return BEGIN_PROPERTY; } +"constant"[ \n\t]*"(" { yylval.objType = CNFOBJ_CONSTANT; + BEGIN INOBJ; return BEGIN_CONSTANT; } "input"[ \n\t]*"(" { yylval.objType = CNFOBJ_INPUT; BEGIN INOBJ; return BEGINOBJ; } "module"[ \n\t]*"(" { yylval.objType = CNFOBJ_MODULE; diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 3bfb2e07..33630a76 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -98,6 +98,62 @@ readConfFile(FILE *fp, es_str_t **str) es_addChar(str, '\0'); } +struct objlst* +objlstNew(struct cnfobj *o) +{ + struct objlst *lst; + + if((lst = malloc(sizeof(struct objlst))) != NULL) { + lst->next = NULL; + lst->obj = o; + } +dbgprintf("AAAA: creating new objlst\n"); +cnfobjPrint(o); + + return lst; +} + +/* add object to end of object list, always returns pointer to root object */ +struct objlst* +objlstAdd(struct objlst *root, struct cnfobj *o) +{ + struct objlst *l; + struct objlst *newl; + + newl = objlstNew(o); + if(root == 0) { + root = newl; + } else { /* find last, linear search ok, as only during config phase */ + for(l = root ; l->next != NULL ; l = l->next) + ; + l->next = newl; + } + return root; +} + +void +objlstDestruct(struct objlst *lst) +{ + struct objlst *toDel; + + while(lst != NULL) { + toDel = lst; + lst = lst->next; + // TODO: delete object + free(toDel); + } +} + +void +objlstPrint(struct objlst *lst) +{ + dbgprintf("objlst %p:\n", lst); + while(lst != NULL) { + cnfobjPrint(lst->obj); + lst = lst->next; + } +} + struct nvlst* nvlstNew(es_str_t *name, es_str_t *value) { @@ -581,6 +637,7 @@ cnfobjNew(enum cnfobjType objType, struct nvlst *lst) nvlstChkDupes(lst); o->objType = objType; o->nvlst = lst; + o->subobjs = NULL; } return o; diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h index 5ff71bee..4c625cd8 100644 --- a/grammar/rainerscript.h +++ b/grammar/rainerscript.h @@ -16,6 +16,9 @@ enum cnfobjType { CNFOBJ_GLOBAL, CNFOBJ_INPUT, CNFOBJ_MODULE, + CNFOBJ_TPL, + CNFOBJ_PROPERTY, + CNFOBJ_CONSTANT, CNFOBJ_INVALID = 0 }; @@ -35,6 +38,15 @@ cnfobjType2str(enum cnfobjType ot) case CNFOBJ_MODULE: return "module"; break; + case CNFOBJ_TPL: + return "template"; + break; + case CNFOBJ_PROPERTY: + return "property"; + break; + case CNFOBJ_CONSTANT: + return "constant"; + break; default:return "error: invalid cnfobjType"; } } @@ -60,6 +72,12 @@ struct var { struct cnfobj { enum cnfobjType objType; struct nvlst *nvlst; + struct objlst *subobjs; +}; + +struct objlst { + struct objlst *next; + struct cnfobj *obj; }; struct nvlst { @@ -218,6 +236,9 @@ struct cnfparamvals { /* the values we obtained for param descr. */ int cnfParseBuffer(char *buf, unsigned lenBuf); void readConfFile(FILE *fp, es_str_t **str); +struct objlst* objlstNew(struct cnfobj *obj); +void objlstDestruct(struct objlst *lst); +void objlstPrint(struct objlst *lst); struct nvlst* nvlstNew(es_str_t *name, es_str_t *value); void nvlstDestruct(struct nvlst *lst); void nvlstPrint(struct nvlst *lst); diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c index a3365d44..f021307b 100644 --- a/plugins/imtcp/imtcp.c +++ b/plugins/imtcp/imtcp.c @@ -370,7 +370,6 @@ BEGINfreeCnf CODESTARTfreeCnf for(inst = pModConf->root ; inst != NULL ; ) { free(inst->pszBindPort); - free(inst->pBindRuleset); free(inst->pszInputName); del = inst; inst = inst->next; diff --git a/runtime/msg.c b/runtime/msg.c index da751dba..6725203c 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -43,6 +43,7 @@ #if HAVE_MALLOC_H # include <malloc.h> #endif +#include <uuid/uuid.h> #include "rsyslog.h" #include "srUtils.h" #include "stringbuf.h" @@ -541,6 +542,8 @@ propNameStrToID(uchar *pName, propid_t *pPropID) *pPropID = PROP_MSGID; } else if(!strcmp((char*) pName, "parsesuccess")) { *pPropID = PROP_PARSESUCCESS; + } else if(!strcmp((char*) pName, "uuid")) { + *pPropID = PROP_UUID; /* here start system properties (those, that do not relate to the message itself */ } else if(!strcmp((char*) pName, "$now")) { *pPropID = PROP_SYS_NOW; @@ -666,6 +669,8 @@ uchar *propIDToName(propid_t propID) return UCHAR_CONSTANT("$!all-json"); case PROP_SYS_BOM: return UCHAR_CONSTANT("$BOM"); + case PROP_UUID: + return UCHAR_CONSTANT("uuid"); default: return UCHAR_CONSTANT("*invalid property id*"); } @@ -745,6 +750,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->pszRcvdAt_SecFrac[0] = '\0'; pM->pszTIMESTAMP_Unix[0] = '\0'; pM->pszRcvdAt_Unix[0] = '\0'; + pM->pszUUID = NULL; /* DEV debugging only! dbgprintf("msgConstruct\t0x%x, ref 1\n", (int)pM);*/ @@ -875,6 +881,8 @@ CODESTARTobjDestruct(msg) rsCStrDestruct(&pThis->pCSMSGID); if(pThis->event != NULL) ee_deleteEvent(pThis->event); + if(pThis->pszUUID != NULL) + free(pThis->pszUUID); # ifndef HAVE_ATOMIC_BUILTINS MsgUnlock(pThis); # endif @@ -1080,6 +1088,8 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) objSerializePTR(pStrm, pCSPROCID, CSTR); objSerializePTR(pStrm, pCSMSGID, CSTR); + objSerializePTR(pStrm, pszUUID, PSZ); + if(pThis->pRuleset != NULL) { rulesetGetName(pThis->pRuleset); CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRuleset"), PROPTYPE_PSZ, @@ -1242,6 +1252,60 @@ char *getProtocolVersionString(msg_t *pM) return(pM->iProtocolVersion ? "1" : "0"); } +/* note: libuuid seems not to be thread-safe, so we need + * to get some safeguards in place. + */ +static void msgSetUUID(msg_t *pM) +{ + size_t lenRes = sizeof(uuid_t) * 2 + 1; + char hex_char [] = "0123456789ABCDEF"; + unsigned int byte_nbr; + uuid_t uuid; + static pthread_mutex_t mutUUID = PTHREAD_MUTEX_INITIALIZER; + + dbgprintf("[MsgSetUUID] START\n"); + assert(pM != NULL); + + if((pM->pszUUID = (uchar*) MALLOC(lenRes)) == NULL) { + pM->pszUUID = (uchar *)""; + } else { + pthread_mutex_lock(&mutUUID); + uuid_generate(uuid); + pthread_mutex_unlock(&mutUUID); + for (byte_nbr = 0; byte_nbr < sizeof (uuid_t); byte_nbr++) { + pM->pszUUID[byte_nbr * 2 + 0] = hex_char[uuid [byte_nbr] >> 4]; + pM->pszUUID[byte_nbr * 2 + 1] = hex_char[uuid [byte_nbr] & 15]; + } + + dbgprintf("[MsgSetUUID] UUID : %s LEN: %d \n", pM->pszUUID, (int)lenRes); + pM->pszUUID[lenRes] = '\0'; + } + dbgprintf("[MsgSetUUID] END\n"); +} + +void getUUID(msg_t *pM, uchar **pBuf, int *piLen) +{ + dbgprintf("[getUUID] START\n"); + if(pM == NULL) { + dbgprintf("[getUUID] pM is NULL\n"); + *pBuf= UCHAR_CONSTANT(""); + *piLen = 0; + } else { + if(pM->pszUUID == NULL) { + dbgprintf("[getUUID] pM->pszUUID is NULL\n"); + MsgLock(pM); + /* re-query, things may have changed in the mean time... */ + if(pM->pszUUID == NULL) + msgSetUUID(pM); + MsgUnlock(pM); + } else { /* UUID already there we reuse it */ + dbgprintf("[getUUID] pM->pszUUID already exists\n"); + } + *pBuf = pM->pszUUID; + *piLen = sizeof(uuid_t) * 2; + } + dbgprintf("[getUUID] END\n"); +} void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen) @@ -1908,7 +1972,6 @@ static inline char *getStructuredData(msg_t *pM) return (char*) pszRet; } - /* check if we have a ProgramName, and, if not, try to aquire/emulate it. * rgerhards, 2009-06-26 */ @@ -2232,7 +2295,6 @@ finalize_it: RETiRet; } - /* set raw message in message object. Size of message is provided. * The function makes sure that the stored rawmsg is properly * terminated by '\0'. @@ -2515,10 +2577,10 @@ jsonField(struct templateEntry *pTpe, uchar **ppRes, unsigned short *pbMustBeFre pSrc = *ppRes; buflen = (*pBufLen == -1) ? ustrlen(pSrc) : *pBufLen; /* we hope we have only few escapes... */ - dst = es_newStr(buflen+es_strlen(pTpe->data.field.fieldName)+15); + dst = es_newStr(buflen+es_strlen(pTpe->fieldName)+15); es_addChar(&dst, '"'); - es_addStr(&dst, pTpe->data.field.fieldName); - es_addBufConstcstr(&dst, "\"=\""); + es_addStr(&dst, pTpe->fieldName); + es_addBufConstcstr(&dst, "\":\""); CHKiRet(jsonAddVal(pSrc, buflen, &dst)); es_addChar(&dst, '"'); @@ -2676,6 +2738,9 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, case PROP_MSGID: pRes = (uchar*)getMSGID(pMsg); break; + case PROP_UUID: + getUUID(pMsg, &pRes, &bufLen); + break; case PROP_PARSESUCCESS: pRes = (uchar*)getParseSuccess(pMsg); break; @@ -2734,7 +2799,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, if(pMsg->event == NULL) { if(*pbMustBeFreed == 1) free(pRes); - pRes = (uchar*) ""; + pRes = (uchar*) "{}"; *pbMustBeFreed = 0; } else { ee_fmtEventToJSON(pMsg->event, &str); diff --git a/runtime/msg.h b/runtime/msg.h index ed2e9d04..f6b54a77 100644 --- a/runtime/msg.h +++ b/runtime/msg.h @@ -123,6 +123,7 @@ struct msg { char pszRcvdAt_SecFrac[7]; /* same as above. Both are fractional seconds for their respective timestamp */ char pszTIMESTAMP_Unix[12]; /* almost as small as a pointer! */ char pszRcvdAt_Unix[12]; + uchar *pszUUID; /* The message's UUID */ }; @@ -184,7 +185,6 @@ void getRawMsg(msg_t *pM, uchar **pBuf, int *piLen); rsRetVal msgGetCEEVar(msg_t *pThis, cstr_t *propName, var_t **ppVar); es_str_t* msgGetCEEVarNew(msg_t *pMsg, char *name); - /* TODO: remove these five (so far used in action.c) */ uchar *getMSG(msg_t *pM); char *getHOSTNAME(msg_t *pM); diff --git a/runtime/objomsr.c b/runtime/objomsr.c index 7241fa27..9cf3781b 100644 --- a/runtime/objomsr.c +++ b/runtime/objomsr.c @@ -149,7 +149,8 @@ OMSRgetSupportedTplOpts(unsigned long *pOpts) { DEFiRet; assert(pOpts != NULL); - *pOpts = OMSR_RQD_TPL_OPT_SQL | OMSR_TPL_AS_ARRAY | OMSR_TPL_AS_MSG; + *pOpts = OMSR_RQD_TPL_OPT_SQL | OMSR_TPL_AS_ARRAY | OMSR_TPL_AS_MSG + | OMSR_TPL_AS_JSON; RETiRet; } diff --git a/runtime/objomsr.h b/runtime/objomsr.h index 643e02c5..3baccaa3 100644 --- a/runtime/objomsr.h +++ b/runtime/objomsr.h @@ -25,12 +25,13 @@ /* define flags for required template options */ #define OMSR_NO_RQD_TPL_OPTS 0 #define OMSR_RQD_TPL_OPT_SQL 1 -/* only one of OMSR_TPL_AS_ARRAY or _AS_MSG must be specified, if both are given - * results are unpredictable. +/* only one of OMSR_TPL_AS_ARRAY, _AS_MSG, or _AS_JSON must be specified, + * if all are given results are unpredictable. */ #define OMSR_TPL_AS_ARRAY 2 /* introduced in 4.1.6, 2009-04-03 */ #define OMSR_TPL_AS_MSG 4 /* introduced in 5.3.4, 2009-11-02 */ -/* next option is 8, 16, 32, ... */ +#define OMSR_TPL_AS_JSON 8 /* introduced in 6.5.1, 2012-09-02 */ +/* next option is 16, 32, 64, ... */ struct omodStringRequest_s { /* strings requested by output module for doAction() */ int iNumEntries; /* number of array entries for data elements below */ diff --git a/runtime/rsconf.c b/runtime/rsconf.c index bd002353..5d2407ec 100644 --- a/runtime/rsconf.c +++ b/runtime/rsconf.c @@ -66,6 +66,7 @@ #include "parserif.h" #include "modules.h" #include "dirty.h" +#include "template.h" /* static data */ DEFobjStaticHelpers @@ -386,6 +387,8 @@ yyerror(char *s) } void cnfDoObj(struct cnfobj *o) { + int bChkUnuse = 1; + dbgprintf("cnf:global:obj: "); cnfobjPrint(o); switch(o->objType) { @@ -398,8 +401,17 @@ void cnfDoObj(struct cnfobj *o) case CNFOBJ_ACTION: actionProcessCnf(o); break; + case CNFOBJ_TPL: + tplProcessCnf(o); + break; + case CNFOBJ_PROPERTY: + case CNFOBJ_CONSTANT: + /* these types are processed at a later stage */ + bChkUnuse = 0; + break; } - nvlstChkUnused(o->nvlst); + if(bChkUnuse) + nvlstChkUnused(o->nvlst); cnfobjDestruct(o); } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index d802536a..57e8a05c 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -142,6 +142,7 @@ typedef uintTiny propid_t; #define PROP_CEE_ALL_JSON 201 #define PROP_SYS_BOM 159 #define PROP_SYS_UPTIME 160 +#define PROP_UUID 161 /* The error codes below are orginally "borrowed" from @@ -50,6 +50,59 @@ DEFobjCurrIf(obj) DEFobjCurrIf(errmsg) DEFobjCurrIf(strgen) +/* tables for interfacing with the v6 config system */ +static struct cnfparamdescr cnfparamdescr[] = { + { "name", eCmdHdlrString, 1 }, + { "type", eCmdHdlrString, 0 }, + { "string", eCmdHdlrString, 0 }, + { "plugin", eCmdHdlrString, 0 }, + { "option.stdsql", eCmdHdlrBinary, 0 }, + { "option.sql", eCmdHdlrBinary, 0 }, + { "option.json", eCmdHdlrBinary, 0 } +}; +static struct cnfparamblk pblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfparamdescr)/sizeof(struct cnfparamdescr), + cnfparamdescr + }; + +static struct cnfparamdescr cnfparamdescrProperty[] = { + { "name", eCmdHdlrString, 1 }, + { "outname", eCmdHdlrString, 0 }, + { "dateformat", eCmdHdlrString, 0 }, + { "caseconversion", eCmdHdlrString, 0 }, + { "controlcharacters", eCmdHdlrString, 0 }, + { "securepath", eCmdHdlrString, 0 }, + { "format", eCmdHdlrString, 0 }, + { "position.from", eCmdHdlrInt, 0 }, + { "position.to", eCmdHdlrInt, 0 }, + { "field.number", eCmdHdlrInt, 0 }, + { "field.delimiter", eCmdHdlrInt, 0 }, + { "regex.expression", eCmdHdlrString, 0 }, + { "regex.type", eCmdHdlrString, 0 }, + { "regex.nomatchmode", eCmdHdlrString, 0 }, + { "regex.match", eCmdHdlrInt, 0 }, + { "regex.submatch", eCmdHdlrInt, 0 }, + { "droplastlf", eCmdHdlrBinary, 0 }, + { "spifno1stsp", eCmdHdlrBinary, 0 } +}; +static struct cnfparamblk pblkProperty = + { CNFPARAMBLK_VERSION, + sizeof(cnfparamdescrProperty)/sizeof(struct cnfparamdescr), + cnfparamdescrProperty + }; + +static struct cnfparamdescr cnfparamdescrConstant[] = { + { "value", eCmdHdlrString, 1 }, + { "outname", eCmdHdlrString, 0 }, +}; +static struct cnfparamblk pblkConstant = + { CNFPARAMBLK_VERSION, + sizeof(cnfparamdescrConstant)/sizeof(struct cnfparamdescr), + cnfparamdescrConstant + }; + + #ifdef FEATURE_REGEXP DEFobjCurrIf(regexp) static int bFirstRegexpErrmsg = 1; /**< did we already do a "can't load regexp" error message? */ @@ -924,12 +977,12 @@ static int do_Parameter(unsigned char **pp, struct template *pTpl) /* save field name - if none was given, use the property name instead */ if(pStrField == NULL) { - if((pTpe->data.field.fieldName = + if((pTpe->fieldName = es_newStrFromCStr((char*)cstrGetSzStrNoNULL(pStrProp), cstrLen(pStrProp))) == NULL) { return 1; } } else { - if((pTpe->data.field.fieldName = + if((pTpe->fieldName = es_newStrFromCStr((char*)cstrGetSzStrNoNULL(pStrField), cstrLen(pStrField))) == NULL) { return 1; } @@ -1130,6 +1183,521 @@ struct template *tplAddLine(rsconf_t *conf, char* pName, uchar** ppRestOfConfLin return(pTpl); } +static rsRetVal +createConstantTpe(struct template *pTpl, struct cnfobj *o) +{ + struct templateEntry *pTpe; + es_str_t *value; + int i; + struct cnfparamvals *pvals; + es_str_t *outname = NULL; + DEFiRet; + + /* pull params */ + pvals = nvlstGetParams(o->nvlst, &pblkConstant, NULL); + cnfparamsPrint(&pblkConstant, pvals); + + for(i = 0 ; i < pblkConstant.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(pblkConstant.descr[i].name, "value")) { + value = pvals[i].val.d.estr; + } else if(!strcmp(pblkConstant.descr[i].name, "outname")) { + outname = es_strdup(pvals[i].val.d.estr); + } else { + dbgprintf("template:constantTpe: program error, non-handled " + "param '%s'\n", pblkConstant.descr[i].name); + } + } + + /* sanity check */ + + /* apply */ + CHKmalloc(pTpe = tpeConstruct(pTpl)); + es_unescapeStr(value); + pTpe->eEntryType = CONSTANT; + pTpe->fieldName = outname; + pTpe->data.constant.iLenConstant = es_strlen(value); + pTpe->data.constant.pConstant = (uchar*)es_str2cstr(value, NULL); + +finalize_it: + RETiRet; +} + +static rsRetVal +createPropertyTpe(struct template *pTpl, struct cnfobj *o) +{ + struct templateEntry *pTpe; + cstr_t *name; + es_str_t *estrname; + es_str_t *outname = NULL; + int i; + int droplastlf = 0; + int spifno1stsp = 0; + int frompos = -1; + int topos = -1; + int fieldnum = -1; + int fielddelim = 9; /* default is HT (USACSII 9) */ + int re_matchToUse = 0; + int re_submatchToUse = 0; + char *re_expr = NULL; + struct cnfparamvals *pvals; + enum {F_NONE, F_CSV, F_JSON, F_JSONF} formatType = F_NONE; + enum {CC_NONE, CC_ESCAPE, CC_SPACE, CC_DROP} controlchr = CC_NONE; + enum {SP_NONE, SP_DROP, SP_REPLACE} secpath = SP_NONE; + enum tplFormatCaseConvTypes caseconv = tplCaseConvNo; + enum tplFormatTypes datefmt = tplFmtDefault; + enum tplRegexType re_type = TPL_REGEX_BRE; + enum tlpRegexNoMatchType re_nomatchType = TPL_REGEX_NOMATCH_USE_DFLTSTR; + DEFiRet; + + /* pull params */ + pvals = nvlstGetParams(o->nvlst, &pblkProperty, NULL); + cnfparamsPrint(&pblkProperty, pvals); + + for(i = 0 ; i < pblkProperty.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(pblkProperty.descr[i].name, "name")) { + estrname = es_strdup(pvals[i].val.d.estr); + /* TODO: unify strings!!! */ + rsCStrConstructFromszStr(&name, + (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL)); + } else if(!strcmp(pblkProperty.descr[i].name, "droplastlf")) { + droplastlf = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "spifno1stsp")) { + spifno1stsp = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "outname")) { + outname = es_strdup(pvals[i].val.d.estr); + } else if(!strcmp(pblkProperty.descr[i].name, "position.from")) { + frompos = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "position.to")) { + topos = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "field.number")) { + fieldnum = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "field.delimiter")) { + fielddelim = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "regex.expression")) { + re_expr = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblkProperty.descr[i].name, "regex.type")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"BRE", sizeof("BRE")-1)) { + re_type = TPL_REGEX_BRE; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"ERE", sizeof("ERE")-1)) { + re_type = TPL_REGEX_ERE; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid regex.type '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblkProperty.descr[i].name, "regex.nomatchmode")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"DFLT", sizeof("DFLT")-1)) { + re_nomatchType = TPL_REGEX_NOMATCH_USE_DFLTSTR; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"BLANK", sizeof("BLANK")-1)) { + re_nomatchType = TPL_REGEX_NOMATCH_USE_BLANK; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"FIELD", sizeof("FIELD")-1)) { + re_nomatchType = TPL_REGEX_NOMATCH_USE_WHOLE_FIELD; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"ZERO", sizeof("ZERO")-1)) { + re_nomatchType = TPL_REGEX_NOMATCH_USE_ZERO; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid format type '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblkProperty.descr[i].name, "regex.match")) { + re_matchToUse = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "regex.submatch")) { + re_submatchToUse = pvals[i].val.d.n; + } else if(!strcmp(pblkProperty.descr[i].name, "format")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"csv", sizeof("csv")-1)) { + formatType = F_CSV; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"json", sizeof("json")-1)) { + formatType = F_JSON; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"jsonf", sizeof("jsonf")-1)) { + formatType = F_JSONF; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid format type '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblkProperty.descr[i].name, "controlcharacters")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"escape", sizeof("escape")-1)) { + controlchr = CC_ESCAPE; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"space", sizeof("space")-1)) { + controlchr = CC_SPACE; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"drop", sizeof("drop")-1)) { + controlchr = CC_DROP; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid controlcharacter mode '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblkProperty.descr[i].name, "securepath")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"drop", sizeof("drop")-1)) { + secpath = SP_DROP; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"replace", sizeof("replace")-1)) { + secpath = SP_REPLACE; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid securepath mode '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblkProperty.descr[i].name, "caseconversion")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"lower", sizeof("lower")-1)) { + caseconv = tplCaseConvLower; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"upper", sizeof("upper")-1)) { + caseconv = tplCaseConvUpper; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid caseconversion type '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblkProperty.descr[i].name, "dateformat")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"mysql", sizeof("mysql")-1)) { + datefmt = tplFmtMySQLDate; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"pgsql", sizeof("pgsql")-1)) { + datefmt = tplFmtPgSQLDate; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"rfc3164", sizeof("rfc3164")-1)) { + datefmt = tplFmtRFC3164Date; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"rfc3164-buggyday", sizeof("rfc3164-buggyday")-1)) { + datefmt = tplFmtRFC3164BuggyDate; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"rfc3339", sizeof("rfc3339")-1)) { + datefmt = tplFmtRFC3339Date; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"unixtimestamp", sizeof("unixtimestamp")-1)) { + datefmt = tplFmtUnixDate; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"subseconds", sizeof("subseconds")-1)) { + datefmt = tplFmtSecFrac; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid date format '%s' for property", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else { + dbgprintf("template:propertyTpe: program error, non-handled " + "param '%s'\n", pblkProperty.descr[i].name); + } + } + if(outname == NULL) + outname = es_strdup(estrname); + + /* sanity check */ + if(topos == -1 && frompos != -1) + topos = 2000000000; /* large enough ;) */ + if(frompos == -1 && topos != -1) + frompos = 0; + if(topos < frompos) { + errmsg.LogError(0, RS_RET_ERR, "position.to=%d is lower than postion.from=%d\n", + topos, frompos); + ABORT_FINALIZE(RS_RET_ERR); + } + if(fieldnum != -1 && re_expr != NULL) { + errmsg.LogError(0, RS_RET_ERR, "both field extraction and regex extraction " + "specified - this is not possible, remove one"); + ABORT_FINALIZE(RS_RET_ERR); + } + + /* apply */ + CHKmalloc(pTpe = tpeConstruct(pTpl)); + pTpe->eEntryType = FIELD; + CHKiRet(propNameToID(name, &pTpe->data.field.propid)); + if(pTpe->data.field.propid == PROP_CEE) { + /* in CEE case, we need to preserve the actual property name */ + pTpe->data.field.propName = estrname; + } else { + es_deleteStr(estrname); + } + pTpe->data.field.options.bDropLastLF = droplastlf; + pTpe->data.field.options.bSPIffNo1stSP = spifno1stsp; + pTpe->data.field.eCaseConv = caseconv; + switch(formatType) { + case F_NONE: + /* all set ;) */ + break; + case F_CSV: + pTpe->data.field.options.bCSV = 1; + break; + case F_JSON: + pTpe->data.field.options.bJSON = 1; + break; + case F_JSONF: + pTpe->data.field.options.bJSONf = 1; + break; + } + switch(controlchr) { + case CC_NONE: + /* all set ;) */ + break; + case CC_ESCAPE: + pTpe->data.field.options.bEscapeCC = 1; + break; + case CC_SPACE: + pTpe->data.field.options.bSpaceCC = 1; + break; + case CC_DROP: + pTpe->data.field.options.bDropCC = 1; + break; + } + switch(secpath) { + case SP_NONE: + /* all set ;) */ + break; + case SP_DROP: + pTpe->data.field.options.bSecPathDrop = 1; + break; + case SP_REPLACE: + pTpe->data.field.options.bSecPathReplace = 1; + break; + } + pTpe->fieldName = outname; + pTpe->data.field.eDateFormat = datefmt; + if(fieldnum != -1) { + pTpe->data.field.has_fields = 1; + pTpe->data.field.iFieldNr = fieldnum; + pTpe->data.field.field_delim = fielddelim; + } + if(frompos != -1) { + pTpe->data.field.iFromPos = frompos; + pTpe->data.field.iToPos = topos; + } + if(re_expr != NULL) { + rsRetVal iRetLocal; + pTpe->data.field.typeRegex = re_type; + pTpe->data.field.nomatchAction = re_nomatchType; + pTpe->data.field.iMatchToUse = re_matchToUse; + pTpe->data.field.iSubMatchToUse = re_submatchToUse; + pTpe->data.field.has_regex = 1; + if((iRetLocal = objUse(regexp, LM_REGEXP_FILENAME)) == RS_RET_OK) { + int iOptions; + iOptions = (pTpe->data.field.typeRegex == TPL_REGEX_ERE) ? REG_EXTENDED : 0; + if(regexp.regcomp(&(pTpe->data.field.re), (char*) re_expr, iOptions) != 0) { + dbgprintf("error: can not compile regex: '%s'\n", re_expr); + errmsg.LogError(0, NO_ERRCODE, "error compiling regex '%s'", re_expr); + pTpe->data.field.has_regex = 2; + ABORT_FINALIZE(RS_RET_ERR); + } + } else { + /* regexp object could not be loaded */ + if(bFirstRegexpErrmsg) { /* prevent flood of messages, maybe even an endless loop! */ + bFirstRegexpErrmsg = 0; + errmsg.LogError(0, NO_ERRCODE, "regexp library could not be loaded (error %d), " + "regexp ignored", iRetLocal); + } + pTpe->data.field.has_regex = 2; + ABORT_FINALIZE(RS_RET_ERR); + } + } + +finalize_it: + RETiRet; +} + +/* create a template in list mode, is build from sub-objects */ +static rsRetVal +createListTpl(struct template *pTpl, struct cnfobj *o) +{ + struct objlst *lst; + DEFiRet; + + dbgprintf("create template from subobjs\n"); + objlstPrint(o->subobjs); + + for(lst = o->subobjs ; lst != NULL ; lst = lst->next) { + switch(lst->obj->objType) { + case CNFOBJ_PROPERTY: + CHKiRet(createPropertyTpe(pTpl, lst->obj)); + break; + case CNFOBJ_CONSTANT: + CHKiRet(createConstantTpe(pTpl, lst->obj)); + break; + default:dbgprintf("program error: invalid object type %d " + "in createLstTpl\n", lst->obj->objType); + break; + } + nvlstChkUnused(lst->obj->nvlst); + } +finalize_it: + RETiRet; +} + +/* Add a new template via the v6 config system. */ +rsRetVal +tplProcessCnf(struct cnfobj *o) +{ + struct template *pTpl = NULL; + struct cnfparamvals *pvals; + int lenName; + char *name = NULL; + uchar *tplStr = NULL; + uchar *plugin = NULL; + uchar *p; + enum { T_STRING, T_PLUGIN, T_LIST } tplType; + int i; + int o_sql=0, o_stdsql=0, o_json=0; /* options */ + int numopts; + rsRetVal localRet; + DEFiRet; + + pvals = nvlstGetParams(o->nvlst, &pblk, NULL); + cnfparamsPrint(&pblk, pvals); + + for(i = 0 ; i < pblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(pblk.descr[i].name, "name")) { + lenName = es_strlen(pvals[i].val.d.estr); + name = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "type")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"string", sizeof("string")-1)) { + tplType = T_STRING; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"plugin", sizeof("plugin")-1)) { + tplType = T_PLUGIN; + } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"list", sizeof("list")-1)) { + tplType = T_LIST; + } else { + uchar *typeStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + errmsg.LogError(0, RS_RET_ERR, "invalid template type '%s'", + typeStr); + free(typeStr); + ABORT_FINALIZE(RS_RET_ERR); + } + } else if(!strcmp(pblk.descr[i].name, "string")) { + tplStr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "plugin")) { + plugin = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "option.stdsql")) { + o_stdsql = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "option.sql")) { + o_sql = pvals[i].val.d.n; + } else if(!strcmp(pblk.descr[i].name, "option.json")) { + o_json = pvals[i].val.d.n; + } else { + dbgprintf("template: program error, non-handled " + "param '%s'\n", pblk.descr[i].name); + } + } + + /* do config sanity checks */ + if(tplStr == NULL) { + if(tplType == T_STRING) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' of type string needs " + "string parameter", name); + ABORT_FINALIZE(RS_RET_ERR); + } + } else { + if(tplType != T_STRING) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' is not a string " + "template but has a string specified - ignored", name); + } + } + + if(plugin == NULL) { + if(tplType == T_PLUGIN) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' of type plugin needs " + "plugin parameter", name); + ABORT_FINALIZE(RS_RET_ERR); + } + } else { + if(tplType != T_PLUGIN) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' is not a plugin " + "template but has a plugin specified - ignored", name); + } + } + + if(o->subobjs == NULL) { + if(tplType == T_LIST) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' of type list has " + "has no parameters specified", name); + ABORT_FINALIZE(RS_RET_ERR); + } + } else { + if(tplType != T_LIST) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' is not a list " + "template but has parameters specified - ignored", name); + } + } + + numopts = 0; + if(o_sql) ++numopts; + if(o_stdsql) ++numopts; + if(o_json) ++numopts; + if(numopts > 1) { + errmsg.LogError(0, RS_RET_ERR, "template '%s' has multiple incompatible " + "options of sql, stdsql or json specified", name); + ABORT_FINALIZE(RS_RET_ERR); + } + + /* config ok */ + if((pTpl = tplConstruct(loadConf)) == NULL) { + DBGPRINTF("template.c: tplConstruct failed!\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + pTpl->pszName = name; + pTpl->iLenName = lenName; + + switch(tplType) { + case T_STRING: p = tplStr; + while(*p) { + switch(*p) { + case '%': /* parameter */ + ++p; /* eat '%' */ + do_Parameter(&p, pTpl); + break; + default: /* constant */ + do_Constant(&p, pTpl); + break; + } + } + break; + case T_PLUGIN: p = plugin; + /* TODO: the use of tplAddTplMod() can be improved! */ + localRet = tplAddTplMod(pTpl, &p); + if(localRet != RS_RET_OK) { + errmsg.LogError(0, localRet, "template '%s': error %d " + "defining template via plugin (strgen) module", + pTpl->pszName, localRet); + ABORT_FINALIZE(localRet); + } + break; + case T_LIST: createListTpl(pTpl, o); + break; + } + + pTpl->optFormatEscape = NO_ESCAPE; + if(o_stdsql) + pTpl->optFormatEscape = STDSQL_ESCAPE; + else if(o_sql) + pTpl->optFormatEscape = SQL_ESCAPE; + else if(o_json) + pTpl->optFormatEscape = JSON_ESCAPE; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pTpl != NULL) { + /* we simply make the template defunct in this case by setting + * its name to a zero-string. We do not free it, as this would + * require additional code and causes only a very small memory + * consumption. TODO: maybe in next iteration... + */ + *pTpl->pszName = '\0'; + } + } + + RETiRet; +} + /* Find a template object based on name. Search * currently is case-senstive (should we change?). @@ -1194,8 +1762,8 @@ void tplDeleteAll(rsconf_t *conf) } if(pTpeDel->data.field.propName != NULL) es_deleteStr(pTpeDel->data.field.propName); - if(pTpeDel->data.field.fieldName != NULL) - es_deleteStr(pTpeDel->data.field.fieldName); + if(pTpeDel->fieldName != NULL) + es_deleteStr(pTpeDel->fieldName); #endif break; } @@ -58,6 +58,9 @@ enum tplFormatTypes { tplFmtDefault = 0, tplFmtMySQLDate = 1, tplFmtRFC3164Date = 2, tplFmtRFC3339Date = 3, tplFmtPgSQLDate = 4, tplFmtSecFrac = 5, tplFmtRFC3164BuggyDate = 6, tplFmtUnixDate}; enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseConvLower = 2 }; +enum tplRegexType { TPL_REGEX_BRE = 0, /* posix BRE */ + TPL_REGEX_ERE = 1 /* posix ERE */ + }; #include "msg.h" @@ -65,6 +68,7 @@ enum tplFormatCaseConvTypes { tplCaseConvNo = 0, tplCaseConvUpper = 1, tplCaseCo struct templateEntry { struct templateEntry *pNext; enum EntryTypes eEntryType; + es_str_t *fieldName; /**< field name to be used for structured output */ union { struct { uchar *pConstant; /* pointer to constant value */ @@ -80,11 +84,8 @@ struct templateEntry { short has_regex; short iMatchToUse;/* which match should be obtained (10 max) */ short iSubMatchToUse;/* which submatch should be obtained (10 max) */ - enum { - TPL_REGEX_BRE = 0, /* posix BRE */ - TPL_REGEX_ERE = 1 /* posix ERE */ - } typeRegex; - enum { + enum tplRegexType typeRegex; + enum tlpRegexNoMatchType { TPL_REGEX_NOMATCH_USE_DFLTSTR = 0, /* use the (old style) default "**NO MATCH**" string */ TPL_REGEX_NOMATCH_USE_BLANK = 1, /* use a blank string */ TPL_REGEX_NOMATCH_USE_WHOLE_FIELD = 2, /* use the full field contents that we were searching in*/ @@ -99,7 +100,6 @@ struct templateEntry { #endif es_str_t *propName; /**< property name (currently being used for CEE only) */ - es_str_t *fieldName; /**< field name to be used for structured output */ enum tplFormatTypes eDateFormat; enum tplFormatCaseConvTypes eCaseConv; @@ -148,6 +148,7 @@ rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz, size_t *) rsRetVal doEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode); rsRetVal templateInit(); +rsRetVal tplProcessCnf(struct cnfobj *o); #endif /* #ifndef TEMPLATE_H_INCLUDED */ /* vim:set ai: diff --git a/tools/Makefile.am b/tools/Makefile.am index 4e457426..60a2dd61 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -40,7 +40,7 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(CNF_LIBS) # note: it looks like librsyslog.la must be explicitely given on LDDADD, # otherwise dependencies are not properly calculated (resulting in a # potentially incomplete build, a problem we had several times...) -rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(CNF_LIBS) ../grammar/libgrammar.la ../runtime/librsyslog.la +rsyslogd_LDADD = $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(CNF_LIBS) $(LIBUUID_LIBS) ../grammar/libgrammar.la ../runtime/librsyslog.la rsyslogd_LDFLAGS = -export-dynamic if ENABLE_DIAGTOOLS |