diff --git a/SOURCES/rsyslog-7.4.7-rhbz1146237-mmutf8fix.patch b/SOURCES/rsyslog-7.4.7-rhbz1146237-mmutf8fix.patch new file mode 100644 index 0000000..a6a4a32 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1146237-mmutf8fix.patch @@ -0,0 +1,1380 @@ +Backport of the mmutf8fix module from upstream + +8929d2f mmutf8fix: new module to fix invalid UTF-8 sequences +554fc84 mmutf8fix: now supports native UTF-8 multibyte sequence handling +666d301 mmutf8fix: stricter UTF-8 checking +97bda43 bugfix: mmutf8fix did not properly handle invalid UTF-8 at END of message +2517719 mmutf8fix: compile problem & potential misadressing at end of message +d322a9d bugfix: mmutf8fix did not detect two invalid sequences +b03d66c silence may be uninitialized warnings where there is already a guard test. +41e2001 re-enable some compiler warnings that should be dealt with +fcdbdb1 fix some clang static analyzer violations + +diff --git a/Makefile.in b/Makefile.in +index 840fe49..6da9e19 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -86,8 +86,9 @@ sbin_PROGRAMS = + @ENABLE_MMAUDIT_TRUE@am__append_43 = plugins/mmaudit + @ENABLE_MMANON_TRUE@am__append_44 = plugins/mmanon + @ENABLE_MMCOUNT_TRUE@am__append_45 = plugins/mmcount +-@ENABLE_ORACLE_TRUE@am__append_46 = plugins/omoracle +-@ENABLE_GUI_TRUE@am__append_47 = java ++@ENABLE_MMUTF8FIX_TRUE@am__append_46 = plugins/mmutf8fix ++@ENABLE_ORACLE_TRUE@am__append_47 = plugins/omoracle ++@ENABLE_GUI_TRUE@am__append_48 = java + subdir = . + DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.h.in \ +@@ -232,7 +233,8 @@ DIST_SUBDIRS = doc compat runtime grammar . plugins/immark \ + plugins/imptcp plugins/imttcp plugins/imdiag plugins/ommail \ + plugins/omprog plugins/im3195 plugins/mmnormalize \ + plugins/mmjsonparse plugins/mmaudit plugins/mmanon \ +- plugins/mmcount plugins/omoracle java tests ++ plugins/mmcount plugins/omoracle java tests \ ++ plugins/mmutf8fix + DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + distdir = $(PACKAGE)-$(VERSION) + top_distdir = $(distdir) +@@ -533,7 +535,8 @@ SUBDIRS = doc compat runtime grammar . plugins/immark plugins/imuxsock \ + $(am__append_37) $(am__append_38) $(am__append_39) \ + $(am__append_40) $(am__append_41) $(am__append_42) \ + $(am__append_43) $(am__append_44) $(am__append_45) \ +- $(am__append_46) $(am__append_47) tests ++ $(am__append_46) $(am__append_47) $(am__append_48) \ ++ tests + + # make sure "make distcheck" tries to build all modules. This means that + # a developer must always have an environment where every supporting library +# diff --git a/Makefile.am b/Makefile.am +# index 567b876..5714d1f 100644 +# --- a/Makefile.am +# +++ b/Makefile.am +# @@ -241,6 +241,10 @@ if ENABLE_MMANON +# SUBDIRS += plugins/mmanon +# endif +# +# +if ENABLE_MMUTF8FIX +# +SUBDIRS += plugins/mmutf8fix +# +endif +# + +# if ENABLE_ORACLE +# SUBDIRS += plugins/omoracle +# endif +diff --git a/configure b/configure +index c346e52..59fcb1a 100755 +--- a/configure ++++ b/configure +@@ -692,6 +692,8 @@ ENABLE_RELP_FALSE + RELP_CFLAGS + ENABLE_MMCOUNT_FALSE + ENABLE_MMCOUNT_TRUE ++ENABLE_MMUTF8FIX_FALSE ++ENABLE_MMUTF8FIX_TRUE + ENABLE_MMANON_FALSE + ENABLE_MMANON_TRUE + ENABLE_MMAUDIT_FALSE +@@ -980,6 +982,7 @@ enable_mmnormalize + enable_mmaudit + enable_mmanon + enable_mmcount ++enable_mmutf8fix + enable_relp + enable_guardtime + enable_cached_man_pages +@@ -1726,6 +1729,7 @@ Optional Features: + --enable-mmaudit Enable building mmaudit support [default=no] + --enable-mmanon Enable building mmanon support [default=no] + --enable-mmcount Enable message counting [default=no] ++ --enable-mmutf8fix Enable building mmutf8fix support [default=no] + --enable-relp Enable RELP support [default=no] + --enable-guardtime Enable log file signing support (via GuardTime) + [default=no] +@@ -18338,6 +18342,29 @@ fi + + + ++# mmutf8fix ++# Check whether --enable-mmutf8fix was given. ++if test "${enable_mmutf8fix+set}" = set; then : ++ enableval=$enable_mmutf8fix; case "${enableval}" in ++ yes) enable_mmutf8fix="yes" ;; ++ no) enable_mmutf8fix="no" ;; ++ *) as_fn_error $? "bad value ${enableval} for --enable-mmutf8fix" "$LINENO" 5 ;; ++ esac ++else ++ enable_mmutf8fix=no ++ ++fi ++ ++ if test x$enable_mmutf8fix = xyes; then ++ ENABLE_MMUTF8FIX_TRUE= ++ ENABLE_MMUTF8FIX_FALSE='#' ++else ++ ENABLE_MMUTF8FIX_TRUE='#' ++ ENABLE_MMUTF8FIX_FALSE= ++fi ++ ++ ++ + # RELP support + # Check whether --enable-relp was given. + if test "${enable_relp+set}" = set; then : +@@ -20001,7 +20028,7 @@ fi + + # END HIREDIS SUPPORT + +-ac_config_files="$ac_config_files Makefile runtime/Makefile compat/Makefile grammar/Makefile tools/Makefile doc/Makefile plugins/imudp/Makefile plugins/imtcp/Makefile plugins/im3195/Makefile plugins/imgssapi/Makefile plugins/imuxsock/Makefile plugins/imjournal/Makefile plugins/immark/Makefile plugins/imklog/Makefile plugins/imkmsg/Makefile plugins/omhdfs/Makefile plugins/omprog/Makefile plugins/omstdout/Makefile plugins/omjournal/Makefile plugins/pmrfc3164sd/Makefile plugins/pmlastmsg/Makefile plugins/pmcisconames/Makefile plugins/pmsnare/Makefile plugins/pmaixforwardedfrom/Makefile plugins/omruleset/Makefile plugins/omuxsock/Makefile plugins/imfile/Makefile plugins/imsolaris/Makefile plugins/imptcp/Makefile plugins/imttcp/Makefile plugins/impstats/Makefile plugins/imrelp/Makefile plugins/imdiag/Makefile plugins/imzmq3/Makefile plugins/omtesting/Makefile plugins/omgssapi/Makefile plugins/ommysql/Makefile plugins/ompgsql/Makefile plugins/omrelp/Makefile plugins/omlibdbi/Makefile plugins/ommail/Makefile plugins/omsnmp/Makefile plugins/omoracle/Makefile plugins/omudpspoof/Makefile plugins/ommongodb/Makefile plugins/omhiredis/Makefile plugins/omzmq3/Makefile plugins/omrabbitmq/Makefile plugins/mmnormalize/Makefile plugins/mmjsonparse/Makefile plugins/mmaudit/Makefile plugins/mmanon/Makefile plugins/omelasticsearch/Makefile plugins/sm_cust_bindcdr/Makefile plugins/mmsnmptrapd/Makefile plugins/mmcount/Makefile java/Makefile tests/Makefile" ++ac_config_files="$ac_config_files Makefile runtime/Makefile compat/Makefile grammar/Makefile tools/Makefile doc/Makefile plugins/imudp/Makefile plugins/imtcp/Makefile plugins/im3195/Makefile plugins/imgssapi/Makefile plugins/imuxsock/Makefile plugins/imjournal/Makefile plugins/immark/Makefile plugins/imklog/Makefile plugins/imkmsg/Makefile plugins/omhdfs/Makefile plugins/omprog/Makefile plugins/omstdout/Makefile plugins/omjournal/Makefile plugins/pmrfc3164sd/Makefile plugins/pmlastmsg/Makefile plugins/pmcisconames/Makefile plugins/pmsnare/Makefile plugins/pmaixforwardedfrom/Makefile plugins/omruleset/Makefile plugins/omuxsock/Makefile plugins/imfile/Makefile plugins/imsolaris/Makefile plugins/imptcp/Makefile plugins/imttcp/Makefile plugins/impstats/Makefile plugins/imrelp/Makefile plugins/imdiag/Makefile plugins/imzmq3/Makefile plugins/omtesting/Makefile plugins/omgssapi/Makefile plugins/ommysql/Makefile plugins/ompgsql/Makefile plugins/omrelp/Makefile plugins/omlibdbi/Makefile plugins/ommail/Makefile plugins/omsnmp/Makefile plugins/omoracle/Makefile plugins/omudpspoof/Makefile plugins/ommongodb/Makefile plugins/omhiredis/Makefile plugins/omzmq3/Makefile plugins/omrabbitmq/Makefile plugins/mmnormalize/Makefile plugins/mmjsonparse/Makefile plugins/mmaudit/Makefile plugins/mmanon/Makefile plugins/mmutf8fix/Makefile plugins/omelasticsearch/Makefile plugins/sm_cust_bindcdr/Makefile plugins/mmsnmptrapd/Makefile plugins/mmcount/Makefile java/Makefile tests/Makefile" + + cat >confcache <<\_ACEOF + # This file is a shell script that caches the results of configure +@@ -20272,6 +20229,10 @@ if test -z "${ENABLE_MMANON_TRUE}" && test -z "${ENABLE_MMANON_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_MMCOUNT\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi ++if test -z "${ENABLE_MMUTF8FIX_TRUE}" && test -z "${ENABLE_MMUTF8FIX_FALSE}"; then ++ as_fn_error $? "conditional \"ENABLE_MMUTF8FIX\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 ++fi + if test -z "${ENABLE_RELP_TRUE}" && test -z "${ENABLE_RELP_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_RELP\" was never defined. + Usually this means the macro was only invoked conditionally." "$LINENO" 5 +@@ -21324,6 +21355,7 @@ do + "plugins/mmjsonparse/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/mmjsonparse/Makefile" ;; + "plugins/mmaudit/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/mmaudit/Makefile" ;; + "plugins/mmanon/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/mmanon/Makefile" ;; ++ "plugins/mmutf8fix/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/mmutf8fix/Makefile" ;; + "plugins/omelasticsearch/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/omelasticsearch/Makefile" ;; + "plugins/sm_cust_bindcdr/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/sm_cust_bindcdr/Makefile" ;; + "plugins/mmsnmptrapd/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/mmsnmptrapd/Makefile" ;; +@@ -22744,6 +22776,7 @@ echo " mmnormalize module will be compiled: $enable_mmnormalize" + echo " mmjsonparse module will be compiled: $enable_mmjsonparse" + echo " mmjaduit module will be compiled: $enable_mmaudit" + echo " mmsnmptrapd module will be compiled: $enable_mmsnmptrapd" ++echo " mmutf8fix enabled: $enable_mmutf8fix" + echo + echo "---{ strgen modules }---" + echo " sm_cust_bindcdr module will be compiled: $enable_sm_cust_bindcdr" +# diff --git a/configure.ac b/configure.ac +# index 123ed7e..5425897 100644 +# --- a/configure.ac +# +++ b/configure.ac +# @@ -964,6 +964,19 @@ AC_ARG_ENABLE(mmanon, +# AM_CONDITIONAL(ENABLE_MMANON, test x$enable_mmanon = xyes) +# +# +# +# mmutf8fix +# +AC_ARG_ENABLE(mmutf8fix, +# + [AS_HELP_STRING([--enable-mmutf8fix],[Enable building mmutf8fix support @<:@default=no@:>@])], +# + [case "${enableval}" in +# + yes) enable_mmutf8fix="yes" ;; +# + no) enable_mmutf8fix="no" ;; +# + *) AC_MSG_ERROR(bad value ${enableval} for --enable-mmutf8fix) ;; +# + esac], +# + [enable_mmutf8fix=no] +# +) +# +AM_CONDITIONAL(ENABLE_MMUTF8FIX, test x$enable_mmutf8fix = xyes) +# + +# + +# # RELP support +# AC_ARG_ENABLE(relp, +# [AS_HELP_STRING([--enable-relp],[Enable RELP support @<:@default=no@:>@])], +# @@ -1480,6 +1493,7 @@ AC_CONFIG_FILES([Makefile \ +# plugins/mmjsonparse/Makefile \ +# plugins/mmaudit/Makefile \ +# plugins/mmanon/Makefile \ +# + plugins/mmutf8fix/Makefile \ +# plugins/omelasticsearch/Makefile \ +# plugins/sm_cust_bindcdr/Makefile \ +# plugins/mmsnmptrapd/Makefile \ +# @@ -1541,6 +1555,7 @@ echo " mmnormalize module will be compiled: $enable_mmnormalize" +# echo " mmjsonparse module will be compiled: $enable_mmjsonparse" +# echo " mmjaduit module will be compiled: $enable_mmaudit" +# echo " mmsnmptrapd module will be compiled: $enable_mmsnmptrapd" +# +echo " mmutf8fix enabled: $enable_mmutf8fix" +# echo +# echo "---{ strgen modules }---" +# echo " sm_cust_bindcdr module will be compiled: $enable_sm_cust_bindcdr" +diff --git a/doc/mmutf8fix.html b/doc/mmutf8fix.html +new file mode 100644 +index 0000000..f7be26d +--- /dev/null ++++ b/doc/mmutf8fix.html +@@ -0,0 +1,96 @@ ++ ++ ++ ++Fix invalid UTF-8 Sequences (mmutf8fix) ++ ++ ++back ++ ++

Fix invalid UTF-8 Sequences (mmutf8fix)

++

Module Name:    mmutf8fix

++

Author: Rainer Gerhards <rgerhards@adiscon.com>

++

Available since: 7.5.4

++

Description:

++

The mmutf8fix module permits to fix invalid UTF-8 sequences. ++Most often, such invalid sequences result from syslog sources sending ++in non-UTF character sets, e.g. ISO 8859. As syslog does not have a way ++to convey the character set information, these sequences are not properly ++handled. While they are typically uncritical with plain text files, they can ++cause big headache with database sources as well as systems like ElasticSearch. ++

The module supports different "fixing" modes and fixes. The current ++implementation will always replace invalid bytes with a single US ASCII ++character. Additional replacement modes will probably be added in the future, ++depending on user demand. In the longer term ++it could also be evolved into an any-charset-to-UTF8 converter. But ++first let's see if it really gets into widespread enough use. ++ ++

Proper Usage:

++

Some notes are due for proper use of this module. This is a message modification ++module utilizing the action interface, which means you call it like an action. ++This gives great flexibility on the question on when and how to call this module. ++Note that once it has been called, it actually modifies the message. The original ++messsage is then no longer available. However, this does not change any ++properties set, used or extracted before the modification is done. ++

One potential use case is to normalize all messages. This is done by simply calling ++mmutf8fix right in front of all other actions. ++

If only a specific source (or set of sources) is known to cause problems, ++mmutf8fix can be conditionally called only on messages from them. This also offers ++performance benefits. If such multiple sources exists, it probably is a good idea ++to define different listeners for their incoming traffic, bind them to specific ++ruleset and call mmutf8fix as first action in this ++ruleset. ++ ++

Module Configuration Parameters:

++

Currently none. ++

 

++

Action Confguration Parameters:

++ ++ ++

Caveats/Known Bugs: ++

++ ++

Samples:

++

In this snippet, we write one file without fixing UTF-8 and another one ++with the message fixed. Note that once mmutf8fix has run, access to the ++original message is no longer possible. ++

++ ++

In this sample, we fix only message originating from host 10.0.0.1. ++

++ ++

[rsyslog.conf overview] [manual ++index] [rsyslog site]

++

This documentation is part of the ++rsyslog project.
++Copyright © 2013 by Rainer Gerhards and ++Adiscon. Released under the GNU GPL ++version 3 or higher.

++ ++ +diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html +index 18d6b8a..aacd44b 100644 +--- a/doc/rsyslog_conf_modules.html ++++ b/doc/rsyslog_conf_modules.html +@@ -118,6 +118,7 @@ enabled structured log messages. +
  • mmsnmptrapd - uses information provided by snmptrapd inside + the tag to correct the original sender system and priority of messages. Implemented via + the output module interface. ++
  • mmutf8fix - used to fix invalid UTF-8 character sequences + + +

    String Generator Modules

    +diff --git a/plugins/mmutf8fix/Makefile.am b/plugins/mmutf8fix/Makefile.am +new file mode 100644 +index 0000000..2c0f283 +--- /dev/null ++++ b/plugins/mmutf8fix/Makefile.am +@@ -0,0 +1,8 @@ ++pkglib_LTLIBRARIES = mmutf8fix.la ++ ++mmutf8fix_la_SOURCES = mmutf8fix.c ++mmutf8fix_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) ++mmutf8fix_la_LDFLAGS = -module -avoid-version ++mmutf8fix_la_LIBADD = ++ ++EXTRA_DIST = +diff --git a/plugins/mmutf8fix/Makefile.in b/plugins/mmutf8fix/Makefile.in +new file mode 100644 +index 0000000..deadbee +--- /dev/null ++++ b/plugins/mmutf8fix/Makefile.in +@@ -0,0 +1,714 @@ ++# Makefile.in generated by automake 1.13.4 from Makefile.am. ++# @configure_input@ ++ ++# Copyright (C) 1994-2013 Free Software Foundation, Inc. ++ ++# This Makefile.in is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY, to the extent permitted by law; without ++# even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++# PARTICULAR PURPOSE. ++ ++@SET_MAKE@ ++ ++VPATH = @srcdir@ ++am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' ++am__make_running_with_option = \ ++ case $${target_option-} in \ ++ ?) ;; \ ++ *) echo "am__make_running_with_option: internal error: invalid" \ ++ "target option '$${target_option-}' specified" >&2; \ ++ exit 1;; \ ++ esac; \ ++ has_opt=no; \ ++ sane_makeflags=$$MAKEFLAGS; \ ++ if $(am__is_gnu_make); then \ ++ sane_makeflags=$$MFLAGS; \ ++ else \ ++ case $$MAKEFLAGS in \ ++ *\\[\ \ ]*) \ ++ bs=\\; \ ++ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ ++ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ ++ esac; \ ++ fi; \ ++ skip_next=no; \ ++ strip_trailopt () \ ++ { \ ++ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ ++ }; \ ++ for flg in $$sane_makeflags; do \ ++ test $$skip_next = yes && { skip_next=no; continue; }; \ ++ case $$flg in \ ++ *=*|--*) continue;; \ ++ -*I) strip_trailopt 'I'; skip_next=yes;; \ ++ -*I?*) strip_trailopt 'I';; \ ++ -*O) strip_trailopt 'O'; skip_next=yes;; \ ++ -*O?*) strip_trailopt 'O';; \ ++ -*l) strip_trailopt 'l'; skip_next=yes;; \ ++ -*l?*) strip_trailopt 'l';; \ ++ -[dEDm]) skip_next=yes;; \ ++ -[JT]) skip_next=yes;; \ ++ esac; \ ++ case $$flg in \ ++ *$$target_option*) has_opt=yes; break;; \ ++ esac; \ ++ done; \ ++ test $$has_opt = yes ++am__make_dryrun = (target_option=n; $(am__make_running_with_option)) ++am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) ++pkgdatadir = $(datadir)/@PACKAGE@ ++pkgincludedir = $(includedir)/@PACKAGE@ ++pkglibdir = $(libdir)/@PACKAGE@ ++pkglibexecdir = $(libexecdir)/@PACKAGE@ ++am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd ++install_sh_DATA = $(install_sh) -c -m 644 ++install_sh_PROGRAM = $(install_sh) -c ++install_sh_SCRIPT = $(install_sh) -c ++INSTALL_HEADER = $(INSTALL_DATA) ++transform = $(program_transform_name) ++NORMAL_INSTALL = : ++PRE_INSTALL = : ++POST_INSTALL = : ++NORMAL_UNINSTALL = : ++PRE_UNINSTALL = : ++POST_UNINSTALL = : ++build_triplet = @build@ ++host_triplet = @host@ ++subdir = plugins/mmutf8fix ++DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ ++ $(top_srcdir)/depcomp ++ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 ++am__aclocal_m4_deps = $(top_srcdir)/m4/atomic_operations.m4 \ ++ $(top_srcdir)/m4/atomic_operations_64bit.m4 \ ++ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ ++ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ ++ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac ++am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ ++ $(ACLOCAL_M4) ++mkinstalldirs = $(install_sh) -d ++CONFIG_HEADER = $(top_builddir)/config.h ++CONFIG_CLEAN_FILES = ++CONFIG_CLEAN_VPATH_FILES = ++am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; ++am__vpath_adj = case $$p in \ ++ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ ++ *) f=$$p;; \ ++ esac; ++am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; ++am__install_max = 40 ++am__nobase_strip_setup = \ ++ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` ++am__nobase_strip = \ ++ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" ++am__nobase_list = $(am__nobase_strip_setup); \ ++ for p in $$list; do echo "$$p $$p"; done | \ ++ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ ++ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ ++ if (++n[$$2] == $(am__install_max)) \ ++ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ ++ END { for (dir in files) print dir, files[dir] }' ++am__base_list = \ ++ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ ++ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' ++am__uninstall_files_from_dir = { \ ++ test -z "$$files" \ ++ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ ++ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ ++ $(am__cd) "$$dir" && rm -f $$files; }; \ ++ } ++am__installdirs = "$(DESTDIR)$(pkglibdir)" ++LTLIBRARIES = $(pkglib_LTLIBRARIES) ++mmutf8fix_la_DEPENDENCIES = ++am_mmutf8fix_la_OBJECTS = mmutf8fix_la-mmutf8fix.lo ++mmutf8fix_la_OBJECTS = $(am_mmutf8fix_la_OBJECTS) ++AM_V_lt = $(am__v_lt_@AM_V@) ++am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) ++am__v_lt_0 = --silent ++am__v_lt_1 = ++mmutf8fix_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ ++ $(mmutf8fix_la_LDFLAGS) $(LDFLAGS) -o $@ ++AM_V_P = $(am__v_P_@AM_V@) ++am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) ++am__v_P_0 = false ++am__v_P_1 = : ++AM_V_GEN = $(am__v_GEN_@AM_V@) ++am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) ++am__v_GEN_0 = @echo " GEN " $@; ++am__v_GEN_1 = ++AM_V_at = $(am__v_at_@AM_V@) ++am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) ++am__v_at_0 = @ ++am__v_at_1 = ++DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) ++depcomp = $(SHELL) $(top_srcdir)/depcomp ++am__depfiles_maybe = depfiles ++am__mv = mv -f ++COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ ++ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) ++LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ ++ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ ++ $(AM_CFLAGS) $(CFLAGS) ++AM_V_CC = $(am__v_CC_@AM_V@) ++am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) ++am__v_CC_0 = @echo " CC " $@; ++am__v_CC_1 = ++CCLD = $(CC) ++LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ ++ $(AM_LDFLAGS) $(LDFLAGS) -o $@ ++AM_V_CCLD = $(am__v_CCLD_@AM_V@) ++am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) ++am__v_CCLD_0 = @echo " CCLD " $@; ++am__v_CCLD_1 = ++SOURCES = $(mmutf8fix_la_SOURCES) ++DIST_SOURCES = $(mmutf8fix_la_SOURCES) ++am__can_run_installinfo = \ ++ case $$AM_UPDATE_INFO_DIR in \ ++ n|no|NO) false;; \ ++ *) (install-info --version) >/dev/null 2>&1;; \ ++ esac ++am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) ++# Read a list of newline-separated strings from the standard input, ++# and print each of them once, without duplicates. Input order is ++# *not* preserved. ++am__uniquify_input = $(AWK) '\ ++ BEGIN { nonempty = 0; } \ ++ { items[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in items) print i; }; } \ ++' ++# Make sure the list of sources is unique. This is necessary because, ++# e.g., the same source file might be shared among _SOURCES variables ++# for different programs/libraries. ++am__define_uniq_tagged_files = \ ++ list='$(am__tagged_files)'; \ ++ unique=`for i in $$list; do \ ++ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ ++ done | $(am__uniquify_input)` ++ETAGS = etags ++CTAGS = ctags ++DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ++ACLOCAL = @ACLOCAL@ ++AMTAR = @AMTAR@ ++AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ ++AR = @AR@ ++AUTOCONF = @AUTOCONF@ ++AUTOHEADER = @AUTOHEADER@ ++AUTOMAKE = @AUTOMAKE@ ++AWK = @AWK@ ++CC = @CC@ ++CCDEPMODE = @CCDEPMODE@ ++CFLAGS = @CFLAGS@ ++CPP = @CPP@ ++CPPFLAGS = @CPPFLAGS@ ++CURL_CFLAGS = @CURL_CFLAGS@ ++CURL_LIBS = @CURL_LIBS@ ++CYGPATH_W = @CYGPATH_W@ ++CZMQ_CFLAGS = @CZMQ_CFLAGS@ ++CZMQ_LIBS = @CZMQ_LIBS@ ++DEFS = @DEFS@ ++DEPDIR = @DEPDIR@ ++DLLTOOL = @DLLTOOL@ ++DL_LIBS = @DL_LIBS@ ++DSYMUTIL = @DSYMUTIL@ ++DUMPBIN = @DUMPBIN@ ++ECHO_C = @ECHO_C@ ++ECHO_N = @ECHO_N@ ++ECHO_T = @ECHO_T@ ++EGREP = @EGREP@ ++EXEEXT = @EXEEXT@ ++FGREP = @FGREP@ ++GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ ++GNUTLS_LIBS = @GNUTLS_LIBS@ ++GREP = @GREP@ ++GSS_LIBS = @GSS_LIBS@ ++GUARDTIME_CFLAGS = @GUARDTIME_CFLAGS@ ++GUARDTIME_LIBS = @GUARDTIME_LIBS@ ++HAVE_LIBGCRYPT_CONFIG = @HAVE_LIBGCRYPT_CONFIG@ ++HAVE_MYSQL_CONFIG = @HAVE_MYSQL_CONFIG@ ++HAVE_ORACLE_CONFIG = @HAVE_ORACLE_CONFIG@ ++HAVE_PGSQL_CONFIG = @HAVE_PGSQL_CONFIG@ ++HIREDIS_CFLAGS = @HIREDIS_CFLAGS@ ++HIREDIS_LIBS = @HIREDIS_LIBS@ ++IMUDP_LIBS = @IMUDP_LIBS@ ++INSTALL = @INSTALL@ ++INSTALL_DATA = @INSTALL_DATA@ ++INSTALL_PROGRAM = @INSTALL_PROGRAM@ ++INSTALL_SCRIPT = @INSTALL_SCRIPT@ ++INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ ++JSON_C_CFLAGS = @JSON_C_CFLAGS@ ++JSON_C_LIBS = @JSON_C_LIBS@ ++LD = @LD@ ++LDFLAGS = @LDFLAGS@ ++LEX = @LEX@ ++LEXLIB = @LEXLIB@ ++LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ ++LIBDBI_CFLAGS = @LIBDBI_CFLAGS@ ++LIBDBI_LIBS = @LIBDBI_LIBS@ ++LIBEE_CFLAGS = @LIBEE_CFLAGS@ ++LIBEE_LIBS = @LIBEE_LIBS@ ++LIBESTR_CFLAGS = @LIBESTR_CFLAGS@ ++LIBESTR_LIBS = @LIBESTR_LIBS@ ++LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ ++LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ ++LIBLOGGING_CFLAGS = @LIBLOGGING_CFLAGS@ ++LIBLOGGING_LIBS = @LIBLOGGING_LIBS@ ++LIBLOGNORM_CFLAGS = @LIBLOGNORM_CFLAGS@ ++LIBLOGNORM_LIBS = @LIBLOGNORM_LIBS@ ++LIBM = @LIBM@ ++LIBMONGO_CLIENT_CFLAGS = @LIBMONGO_CLIENT_CFLAGS@ ++LIBMONGO_CLIENT_LIBS = @LIBMONGO_CLIENT_LIBS@ ++LIBOBJS = @LIBOBJS@ ++LIBS = @LIBS@ ++LIBSYSTEMD_JOURNAL_CFLAGS = @LIBSYSTEMD_JOURNAL_CFLAGS@ ++LIBSYSTEMD_JOURNAL_LIBS = @LIBSYSTEMD_JOURNAL_LIBS@ ++LIBTOOL = @LIBTOOL@ ++LIBUUID_CFLAGS = @LIBUUID_CFLAGS@ ++LIBUUID_LIBS = @LIBUUID_LIBS@ ++LIPO = @LIPO@ ++LN_S = @LN_S@ ++LTLIBOBJS = @LTLIBOBJS@ ++MAKEINFO = @MAKEINFO@ ++MANIFEST_TOOL = @MANIFEST_TOOL@ ++MKDIR_P = @MKDIR_P@ ++MYSQL_CFLAGS = @MYSQL_CFLAGS@ ++MYSQL_LIBS = @MYSQL_LIBS@ ++NM = @NM@ ++NMEDIT = @NMEDIT@ ++OBJDUMP = @OBJDUMP@ ++OBJEXT = @OBJEXT@ ++ORACLE_CFLAGS = @ORACLE_CFLAGS@ ++ORACLE_LIBS = @ORACLE_LIBS@ ++OTOOL = @OTOOL@ ++OTOOL64 = @OTOOL64@ ++PACKAGE = @PACKAGE@ ++PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ ++PACKAGE_NAME = @PACKAGE_NAME@ ++PACKAGE_STRING = @PACKAGE_STRING@ ++PACKAGE_TARNAME = @PACKAGE_TARNAME@ ++PACKAGE_URL = @PACKAGE_URL@ ++PACKAGE_VERSION = @PACKAGE_VERSION@ ++PATH_SEPARATOR = @PATH_SEPARATOR@ ++PGSQL_CFLAGS = @PGSQL_CFLAGS@ ++PGSQL_LIBS = @PGSQL_LIBS@ ++PKG_CONFIG = @PKG_CONFIG@ ++PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ ++PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ ++PTHREADS_CFLAGS = @PTHREADS_CFLAGS@ ++PTHREADS_LIBS = @PTHREADS_LIBS@ ++RABBITMQ_CFLAGS = @RABBITMQ_CFLAGS@ ++RABBITMQ_LIBS = @RABBITMQ_LIBS@ ++RANLIB = @RANLIB@ ++RELP_CFLAGS = @RELP_CFLAGS@ ++RELP_LIBS = @RELP_LIBS@ ++RSRT_CFLAGS = @RSRT_CFLAGS@ ++RSRT_CFLAGS1 = @RSRT_CFLAGS1@ ++RSRT_LIBS = @RSRT_LIBS@ ++RSRT_LIBS1 = @RSRT_LIBS1@ ++RST2MAN = @RST2MAN@ ++RT_LIBS = @RT_LIBS@ ++SED = @SED@ ++SET_MAKE = @SET_MAKE@ ++SHELL = @SHELL@ ++SNMP_CFLAGS = @SNMP_CFLAGS@ ++SNMP_LIBS = @SNMP_LIBS@ ++SOL_LIBS = @SOL_LIBS@ ++STRIP = @STRIP@ ++UDPSPOOF_CFLAGS = @UDPSPOOF_CFLAGS@ ++UDPSPOOF_LIBS = @UDPSPOOF_LIBS@ ++VERSION = @VERSION@ ++YACC = @YACC@ ++YFLAGS = @YFLAGS@ ++ZLIB_LIBS = @ZLIB_LIBS@ ++abs_builddir = @abs_builddir@ ++abs_srcdir = @abs_srcdir@ ++abs_top_builddir = @abs_top_builddir@ ++abs_top_srcdir = @abs_top_srcdir@ ++ac_ct_AR = @ac_ct_AR@ ++ac_ct_CC = @ac_ct_CC@ ++ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ ++am__include = @am__include@ ++am__leading_dot = @am__leading_dot@ ++am__quote = @am__quote@ ++am__tar = @am__tar@ ++am__untar = @am__untar@ ++bindir = @bindir@ ++build = @build@ ++build_alias = @build_alias@ ++build_cpu = @build_cpu@ ++build_os = @build_os@ ++build_vendor = @build_vendor@ ++builddir = @builddir@ ++datadir = @datadir@ ++datarootdir = @datarootdir@ ++docdir = @docdir@ ++dvidir = @dvidir@ ++exec_prefix = @exec_prefix@ ++have_valgrind = @have_valgrind@ ++host = @host@ ++host_alias = @host_alias@ ++host_cpu = @host_cpu@ ++host_os = @host_os@ ++host_vendor = @host_vendor@ ++htmldir = @htmldir@ ++includedir = @includedir@ ++infodir = @infodir@ ++install_sh = @install_sh@ ++libdir = @libdir@ ++libexecdir = @libexecdir@ ++localedir = @localedir@ ++localstatedir = @localstatedir@ ++mandir = @mandir@ ++mkdir_p = @mkdir_p@ ++moddirs = @moddirs@ ++oldincludedir = @oldincludedir@ ++pdfdir = @pdfdir@ ++prefix = @prefix@ ++program_transform_name = @program_transform_name@ ++psdir = @psdir@ ++sbindir = @sbindir@ ++sharedstatedir = @sharedstatedir@ ++srcdir = @srcdir@ ++sysconfdir = @sysconfdir@ ++systemdsystemunitdir = @systemdsystemunitdir@ ++target_alias = @target_alias@ ++top_build_prefix = @top_build_prefix@ ++top_builddir = @top_builddir@ ++top_srcdir = @top_srcdir@ ++pkglib_LTLIBRARIES = mmutf8fix.la ++mmutf8fix_la_SOURCES = mmutf8fix.c ++mmutf8fix_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) ++mmutf8fix_la_LDFLAGS = -module -avoid-version ++mmutf8fix_la_LIBADD = ++EXTRA_DIST = ++all: all-am ++ ++.SUFFIXES: ++.SUFFIXES: .c .lo .o .obj ++$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) ++ @for dep in $?; do \ ++ case '$(am__configure_deps)' in \ ++ *$$dep*) \ ++ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ ++ && { if test -f $@; then exit 0; else break; fi; }; \ ++ exit 1;; \ ++ esac; \ ++ done; \ ++ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plugins/mmutf8fix/Makefile'; \ ++ $(am__cd) $(top_srcdir) && \ ++ $(AUTOMAKE) --gnu plugins/mmutf8fix/Makefile ++.PRECIOUS: Makefile ++Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status ++ @case '$?' in \ ++ *config.status*) \ ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ ++ *) \ ++ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ ++ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ ++ esac; ++ ++$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++ ++$(top_srcdir)/configure: $(am__configure_deps) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(ACLOCAL_M4): $(am__aclocal_m4_deps) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(am__aclocal_m4_deps): ++ ++install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) ++ @$(NORMAL_INSTALL) ++ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ ++ list2=; for p in $$list; do \ ++ if test -f $$p; then \ ++ list2="$$list2 $$p"; \ ++ else :; fi; \ ++ done; \ ++ test -z "$$list2" || { \ ++ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ ++ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ ++ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ ++ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ ++ } ++ ++uninstall-pkglibLTLIBRARIES: ++ @$(NORMAL_UNINSTALL) ++ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ ++ for p in $$list; do \ ++ $(am__strip_dir) \ ++ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ ++ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ ++ done ++ ++clean-pkglibLTLIBRARIES: ++ -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) ++ @list='$(pkglib_LTLIBRARIES)'; \ ++ locs=`for p in $$list; do echo $$p; done | \ ++ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ ++ sort -u`; \ ++ test -z "$$locs" || { \ ++ echo rm -f $${locs}; \ ++ rm -f $${locs}; \ ++ } ++ ++mmutf8fix.la: $(mmutf8fix_la_OBJECTS) $(mmutf8fix_la_DEPENDENCIES) $(EXTRA_mmutf8fix_la_DEPENDENCIES) ++ $(AM_V_CCLD)$(mmutf8fix_la_LINK) -rpath $(pkglibdir) $(mmutf8fix_la_OBJECTS) $(mmutf8fix_la_LIBADD) $(LIBS) ++ ++mostlyclean-compile: ++ -rm -f *.$(OBJEXT) ++ ++distclean-compile: ++ -rm -f *.tab.c ++ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmutf8fix_la-mmutf8fix.Plo@am__quote@ ++ ++.c.o: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< ++ ++.c.obj: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` ++ ++.c.lo: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< ++ ++mmutf8fix_la-mmutf8fix.lo: mmutf8fix.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mmutf8fix_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mmutf8fix_la-mmutf8fix.lo -MD -MP -MF $(DEPDIR)/mmutf8fix_la-mmutf8fix.Tpo -c -o mmutf8fix_la-mmutf8fix.lo `test -f 'mmutf8fix.c' || echo '$(srcdir)/'`mmutf8fix.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mmutf8fix_la-mmutf8fix.Tpo $(DEPDIR)/mmutf8fix_la-mmutf8fix.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mmutf8fix.c' object='mmutf8fix_la-mmutf8fix.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mmutf8fix_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmutf8fix_la-mmutf8fix.lo `test -f 'mmutf8fix.c' || echo '$(srcdir)/'`mmutf8fix.c ++ ++mostlyclean-libtool: ++ -rm -f *.lo ++ ++clean-libtool: ++ -rm -rf .libs _libs ++ ++ID: $(am__tagged_files) ++ $(am__define_uniq_tagged_files); mkid -fID $$unique ++tags: tags-am ++TAGS: tags ++ ++tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) ++ set x; \ ++ here=`pwd`; \ ++ $(am__define_uniq_tagged_files); \ ++ shift; \ ++ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ ++ test -n "$$unique" || unique=$$empty_fix; \ ++ if test $$# -gt 0; then \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ "$$@" $$unique; \ ++ else \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ $$unique; \ ++ fi; \ ++ fi ++ctags: ctags-am ++ ++CTAGS: ctags ++ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) ++ $(am__define_uniq_tagged_files); \ ++ test -z "$(CTAGS_ARGS)$$unique" \ ++ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ ++ $$unique ++ ++GTAGS: ++ here=`$(am__cd) $(top_builddir) && pwd` \ ++ && $(am__cd) $(top_srcdir) \ ++ && gtags -i $(GTAGS_ARGS) "$$here" ++cscopelist: cscopelist-am ++ ++cscopelist-am: $(am__tagged_files) ++ list='$(am__tagged_files)'; \ ++ case "$(srcdir)" in \ ++ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ ++ *) sdir=$(subdir)/$(srcdir) ;; \ ++ esac; \ ++ for i in $$list; do \ ++ if test -f "$$i"; then \ ++ echo "$(subdir)/$$i"; \ ++ else \ ++ echo "$$sdir/$$i"; \ ++ fi; \ ++ done >> $(top_builddir)/cscope.files ++ ++distclean-tags: ++ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags ++ ++distdir: $(DISTFILES) ++ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ list='$(DISTFILES)'; \ ++ dist_files=`for file in $$list; do echo $$file; done | \ ++ sed -e "s|^$$srcdirstrip/||;t" \ ++ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ ++ case $$dist_files in \ ++ */*) $(MKDIR_P) `echo "$$dist_files" | \ ++ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ ++ sort -u` ;; \ ++ esac; \ ++ for file in $$dist_files; do \ ++ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ ++ if test -d $$d/$$file; then \ ++ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ ++ if test -d "$(distdir)/$$file"; then \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ ++ fi; \ ++ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ ++ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ ++ fi; \ ++ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ ++ else \ ++ test -f "$(distdir)/$$file" \ ++ || cp -p $$d/$$file "$(distdir)/$$file" \ ++ || exit 1; \ ++ fi; \ ++ done ++check-am: all-am ++check: check-am ++all-am: Makefile $(LTLIBRARIES) ++installdirs: ++ for dir in "$(DESTDIR)$(pkglibdir)"; do \ ++ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ ++ done ++install: install-am ++install-exec: install-exec-am ++install-data: install-data-am ++uninstall: uninstall-am ++ ++install-am: all-am ++ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am ++ ++installcheck: installcheck-am ++install-strip: ++ if test -z '$(STRIP)'; then \ ++ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ ++ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ ++ install; \ ++ else \ ++ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ ++ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ ++ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ ++ fi ++mostlyclean-generic: ++ ++clean-generic: ++ ++distclean-generic: ++ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) ++ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) ++ ++maintainer-clean-generic: ++ @echo "This command is intended for maintainers to use" ++ @echo "it deletes files that may require special tools to rebuild." ++clean: clean-am ++ ++clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ ++ mostlyclean-am ++ ++distclean: distclean-am ++ -rm -rf ./$(DEPDIR) ++ -rm -f Makefile ++distclean-am: clean-am distclean-compile distclean-generic \ ++ distclean-tags ++ ++dvi: dvi-am ++ ++dvi-am: ++ ++html: html-am ++ ++html-am: ++ ++info: info-am ++ ++info-am: ++ ++install-data-am: ++ ++install-dvi: install-dvi-am ++ ++install-dvi-am: ++ ++install-exec-am: install-pkglibLTLIBRARIES ++ ++install-html: install-html-am ++ ++install-html-am: ++ ++install-info: install-info-am ++ ++install-info-am: ++ ++install-man: ++ ++install-pdf: install-pdf-am ++ ++install-pdf-am: ++ ++install-ps: install-ps-am ++ ++install-ps-am: ++ ++installcheck-am: ++ ++maintainer-clean: maintainer-clean-am ++ -rm -rf ./$(DEPDIR) ++ -rm -f Makefile ++maintainer-clean-am: distclean-am maintainer-clean-generic ++ ++mostlyclean: mostlyclean-am ++ ++mostlyclean-am: mostlyclean-compile mostlyclean-generic \ ++ mostlyclean-libtool ++ ++pdf: pdf-am ++ ++pdf-am: ++ ++ps: ps-am ++ ++ps-am: ++ ++uninstall-am: uninstall-pkglibLTLIBRARIES ++ ++.MAKE: install-am install-strip ++ ++.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ ++ clean-libtool clean-pkglibLTLIBRARIES cscopelist-am ctags \ ++ ctags-am distclean distclean-compile distclean-generic \ ++ distclean-libtool distclean-tags distdir dvi dvi-am html \ ++ html-am info info-am install install-am install-data \ ++ install-data-am install-dvi install-dvi-am install-exec \ ++ install-exec-am install-html install-html-am install-info \ ++ install-info-am install-man install-pdf install-pdf-am \ ++ install-pkglibLTLIBRARIES install-ps install-ps-am \ ++ install-strip installcheck installcheck-am installdirs \ ++ maintainer-clean maintainer-clean-generic mostlyclean \ ++ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ ++ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ ++ uninstall-pkglibLTLIBRARIES ++ ++ ++# Tell versions [3.59,3.63) of GNU make to not export all variables. ++# Otherwise a system limit (for SysV at least) may be exceeded. ++.NOEXPORT: +diff --git a/plugins/mmutf8fix/mmutf8fix.c b/plugins/mmutf8fix/mmutf8fix.c +new file mode 100644 +index 0000000..8e647f2 +--- /dev/null ++++ b/plugins/mmutf8fix/mmutf8fix.c +@@ -0,0 +1,328 @@ ++/* mmutf8fix.c ++ * fix invalid UTF8 sequences. This is begun as a very simple replacer ++ * of non-control characters, and actually breaks some UTF-8 encoding ++ * right now. If the module turns out to be useful, it should be enhanced ++ * to support modes that really detect invalid UTF8. In the longer term ++ * it could also be evolved into an any-charset-to-UTF8 converter. But ++ * first let's see if it really gets into widespread enough use. ++ * ++ * Copyright 2013 Adiscon GmbH. ++ * ++ * This file is part of rsyslog. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * -or- ++ * see COPYING.ASL20 in the source distribution ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++#include "config.h" ++#include "rsyslog.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "conf.h" ++#include "syslogd-types.h" ++#include "srUtils.h" ++#include "template.h" ++#include "module-template.h" ++#include "errmsg.h" ++ ++MODULE_TYPE_OUTPUT ++MODULE_TYPE_NOKEEP ++MODULE_CNFNAME("mmutf8fix") ++ ++ ++DEFobjCurrIf(errmsg); ++DEF_OMOD_STATIC_DATA ++ ++/* define operation modes we have */ ++#define MODE_CC 0 /* just fix control characters */ ++#define MODE_UTF8 1 /* do real UTF-8 fixing */ ++ ++/* config variables */ ++typedef struct _instanceData { ++ uchar replChar; ++ uint8_t mode; /* operations mode */ ++} instanceData; ++ ++struct modConfData_s { ++ rsconf_t *pConf; /* our overall config object */ ++}; ++static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ ++static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */ ++ ++ ++/* tables for interfacing with the v6 config system */ ++/* action (instance) parameters */ ++static struct cnfparamdescr actpdescr[] = { ++ { "mode", eCmdHdlrGetWord, 0 }, ++ { "replacementchar", eCmdHdlrGetChar, 0 } ++}; ++static struct cnfparamblk actpblk = ++ { CNFPARAMBLK_VERSION, ++ sizeof(actpdescr)/sizeof(struct cnfparamdescr), ++ actpdescr ++ }; ++ ++BEGINbeginCnfLoad ++CODESTARTbeginCnfLoad ++ loadModConf = pModConf; ++ pModConf->pConf = pConf; ++ENDbeginCnfLoad ++ ++BEGINendCnfLoad ++CODESTARTendCnfLoad ++ENDendCnfLoad ++ ++BEGINcheckCnf ++CODESTARTcheckCnf ++ENDcheckCnf ++ ++BEGINactivateCnf ++CODESTARTactivateCnf ++ runModConf = pModConf; ++ENDactivateCnf ++ ++BEGINfreeCnf ++CODESTARTfreeCnf ++ENDfreeCnf ++ ++ ++BEGINcreateInstance ++CODESTARTcreateInstance ++ENDcreateInstance ++ ++ ++BEGINisCompatibleWithFeature ++CODESTARTisCompatibleWithFeature ++ENDisCompatibleWithFeature ++ ++ ++BEGINfreeInstance ++CODESTARTfreeInstance ++ENDfreeInstance ++ ++ ++static inline void ++setInstParamDefaults(instanceData *pData) ++{ ++ pData->mode = MODE_UTF8; ++ pData->replChar = ' '; ++} ++ ++BEGINnewActInst ++ struct cnfparamvals *pvals; ++ int i; ++CODESTARTnewActInst ++ DBGPRINTF("newActInst (mmutf8fix)\n"); ++ if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { ++ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); ++ } ++ ++ CODE_STD_STRING_REQUESTnewActInst(1) ++ CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG)); ++ CHKiRet(createInstance(&pData)); ++ setInstParamDefaults(pData); ++ ++ for(i = 0 ; i < actpblk.nParams ; ++i) { ++ if(!pvals[i].bUsed) ++ continue; ++ if(!strcmp(actpblk.descr[i].name, "mode")) { ++ if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"utf-8", ++ sizeof("utf-8")-1)) { ++ pData->mode = MODE_UTF8; ++ } else if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"controlcharacters", ++ sizeof("controlcharacters")-1)) { ++ pData->mode = MODE_CC; ++ } else { ++ char *cstr = es_str2cstr(pvals[i].val.d.estr, NULL); ++ errmsg.LogError(0, RS_RET_INVLD_MODE, ++ "mmutf8fix: invalid mode '%s' - ignored", ++ cstr); ++ free(cstr); ++ } ++ } else if(!strcmp(actpblk.descr[i].name, "replacementchar")) { ++ pData->replChar = es_getBufAddr(pvals[i].val.d.estr)[0]; ++ } else { ++ dbgprintf("mmutf8fix: program error, non-handled " ++ "param '%s'\n", actpblk.descr[i].name); ++ } ++ } ++ ++CODE_STD_FINALIZERnewActInst ++ cnfparamvalsDestruct(pvals, &actpblk); ++ENDnewActInst ++ ++ ++BEGINdbgPrintInstInfo ++CODESTARTdbgPrintInstInfo ++ENDdbgPrintInstInfo ++ ++ ++BEGINtryResume ++CODESTARTtryResume ++ENDtryResume ++ ++ ++static inline void ++doCC(instanceData *pData, uchar *msg, int lenMsg) ++{ ++ int i; ++ ++ for(i = 0 ; i < lenMsg ; ++i) { ++ if(msg[i] < 32 || msg[i] > 126) { ++ msg[i] = pData->replChar; ++ } ++ } ++} ++ ++/* fix an invalid multibyte sequence */ ++static inline void ++fixInvldMBSeq(instanceData *pData, uchar *msg, int lenMsg, int strtIdx, int *endIdx, int8_t seqLen) ++{ ++ int i; ++ ++ /* startIdx and seqLen always set if bytesLeft is set, ++ which is required before this function is called */ ++ *endIdx = strtIdx + seqLen; ++ if(*endIdx > lenMsg) ++ *endIdx = lenMsg; ++ for(i = strtIdx ; i < *endIdx ; ++i) ++ msg[i] = pData->replChar; ++} ++ ++static inline void ++doUTF8(instanceData *pData, uchar *msg, int lenMsg) ++{ ++ uchar c; ++ int8_t seqLen = 0, bytesLeft = 0; ++ uint32_t codepoint; ++ int strtIdx = 0, endIdx = 0; ++ int i; ++ ++ for(i = 0 ; i < lenMsg ; ++i) { ++ c = msg[i]; ++ if(bytesLeft) { ++ if((c & 0xc0) != 0x80) { ++ /* sequence invalid, invalidate all bytes ++ startIdx is always set if bytesLeft is set */ ++ fixInvldMBSeq(pData, msg, lenMsg, strtIdx, &endIdx, ++ seqLen); ++ i = endIdx - 1; ++ bytesLeft = 0; ++ } else { ++ codepoint = (codepoint << 6) | (c & 0x3f); ++ --bytesLeft; ++ if(bytesLeft == 0) { ++ /* too-large codepoint? */ ++ if(codepoint > 0x10FFFF) { ++ /* sequence invalid, invalidate all bytes ++ startIdx is always set if bytesLeft is set */ ++ fixInvldMBSeq(pData, msg, lenMsg, ++ strtIdx, &endIdx, ++ seqLen); ++ } ++ } ++ } ++ } else { ++ if((c & 0x80) == 0) { ++ /* 1-byte sequence, US-ASCII */ ++ ; /* nothing to do, all well */ ++ } else if((c & 0xe0) == 0xc0) { ++ /* 2-byte sequence */ ++ /* 0xc0 and 0xc1 are illegal */ ++ if(c == 0xc0 || c == 0xc1) { ++ msg[i] = pData->replChar; ++ } else { ++ strtIdx = i; ++ seqLen = bytesLeft = 1; ++ codepoint = c & 0x1f; ++ } ++ } else if((c & 0xf0) == 0xe0) { ++ /* 3-byte sequence */ ++ strtIdx = i; ++ seqLen = bytesLeft = 2; ++ codepoint = c & 0x0f; ++ } else if((c & 0xf8) == 0xf0) { ++ /* 4-byte sequence */ ++ strtIdx = i; ++ seqLen = bytesLeft = 3; ++ codepoint = c & 0x07; ++ } else { /* invalid (5&6 byte forbidden by RFC3629) */ ++ msg[i] = pData->replChar; ++ } ++ if(i+bytesLeft >= lenMsg) { ++ int dummy = lenMsg; ++ /* invalid, as rest of message cannot contain full char */ ++ fixInvldMBSeq(pData, msg, lenMsg, strtIdx, &dummy, seqLen); ++ i = lenMsg - 1; ++ } ++ } ++ } ++} ++ ++BEGINdoAction ++ msg_t *pMsg; ++ uchar *msg; ++ int lenMsg; ++CODESTARTdoAction ++ pMsg = (msg_t*) ppString[0]; ++ lenMsg = getMSGLen(pMsg); ++ msg = getMSG(pMsg); ++ if(pData->mode == MODE_CC) { ++ doCC(pData, msg, lenMsg); ++ } else { ++ doUTF8(pData, msg, lenMsg); ++ } ++ENDdoAction ++ ++ ++BEGINparseSelectorAct ++CODESTARTparseSelectorAct ++CODE_STD_STRING_REQUESTparseSelectorAct(1) ++ if(strncmp((char*) p, ":mmutf8fix:", sizeof(":mmutf8fix:") - 1)) { ++ errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED, ++ "mmutf8fix supports only v6+ config format, use: " ++ "action(type=\"mmutf8fix\" ...)"); ++ } ++ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); ++CODE_STD_FINALIZERparseSelectorAct ++ENDparseSelectorAct ++ ++ ++BEGINmodExit ++CODESTARTmodExit ++ objRelease(errmsg, CORE_COMPONENT); ++ENDmodExit ++ ++ ++BEGINqueryEtryPt ++CODESTARTqueryEtryPt ++CODEqueryEtryPt_STD_OMOD_QUERIES ++CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ++CODEqueryEtryPt_STD_CONF2_QUERIES ++ENDqueryEtryPt ++ ++ ++BEGINmodInit() ++CODESTARTmodInit ++ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ ++CODEmodInit_QueryRegCFSLineHdlr ++ DBGPRINTF("mmutf8fix: module compiled with rsyslog version %s.\n", VERSION); ++ CHKiRet(objUse(errmsg, CORE_COMPONENT)); ++ENDmodInit diff --git a/SOURCES/rsyslog-7.4.7-rhbz1214257-maxMessageSize.patch b/SOURCES/rsyslog-7.4.7-rhbz1214257-maxMessageSize.patch new file mode 100644 index 0000000..88fa1c6 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1214257-maxMessageSize.patch @@ -0,0 +1,166 @@ +From bbd8c8c7176148702a737b8df62132cd1e078627 Mon Sep 17 00:00:00 2001 +From: Rainer Gerhards +Date: Tue, 13 Jan 2015 09:54:41 +0100 +Subject: [PATCH] bugfixes for maxMessageSize global parameter + +closely related, thus done in a single commit: + +- bugfix: invalid data size for iMaxLine global property + It was defined as int, but inside the config system it was declared as + size type, which uses int64_t. With legacy config statements, this could + lead to misadressing, which usually meant the another config variable was + overwritten (depending on memory layout). + closes https://github.com/rsyslog/rsyslog/issues/205 + +- bugfix: negative values for maxMessageSize global parameter were permitted +--- + runtime/glbl.c | 43 ++++++++++++++++++++++++++++++++++++------- + runtime/glbl.h | 7 ++++--- + 2 files changed, 40 insertions(+), 10 deletions(-) + +diff --git a/runtime/glbl.c b/runtime/glbl.c +index c57cedf..bfe0b0e 100644 +--- a/runtime/glbl.c ++++ b/runtime/glbl.c +@@ -7,7 +7,7 @@ + * + * Module begun 2008-04-16 by Rainer Gerhards + * +- * Copyright 2008-2013 Rainer Gerhards and Adiscon GmbH. ++ * Copyright 2008-2015 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * +@@ -116,6 +116,12 @@ static struct cnfparamvals *cnfparamvals = NULL; + * each time a new config load begins (TODO: create interface?) + */ + ++static int ++GetMaxLine(void) ++{ ++ return(iMaxLine); ++} ++ + /* define a macro for the simple properties' set and get functions + * (which are always the same). This is only suitable for pretty + * simple cases which require neither checks nor memory allocation. +@@ -138,7 +144,6 @@ static dataType Get##nameFunc(void) \ + SIMP_PROP(ParseHOSTNAMEandTAG, bParseHOSTNAMEandTAG, int) + SIMP_PROP(OptimizeUniProc, bOptimizeUniProc, int) + SIMP_PROP(PreserveFQDN, bPreserveFQDN, int) +-SIMP_PROP(MaxLine, iMaxLine, int) + SIMP_PROP(DefPFFamily, iDefPFFamily, int) /* note that in the future we may check the family argument */ + SIMP_PROP(DropMalPTRMsgs, bDropMalPTRMsgs, int) + SIMP_PROP(Option_DisallowWarning, option_DisallowWarning, int) +@@ -282,6 +287,32 @@ finalize_it: + } + + ++/* This function is used both by legacy and RainerScript conf. It is a real setter. */ ++static rsRetVal ++setMaxLine(const int64_t iNew) ++{ ++ if(iNew < 128) { ++ errmsg.LogError(0, RS_RET_INVALID_VALUE, "maxMessageSize tried to set " ++ "to %lld, but cannot be less than 128 - set to 128 " ++ "instead", (long long) iNew); ++ iMaxLine = 128; ++ } else if(iNew > (int64_t) INT_MAX) { ++ errmsg.LogError(0, RS_RET_INVALID_VALUE, "maxMessageSize larger than " ++ "INT_MAX (%d) - reduced to INT_MAX", INT_MAX); ++ iMaxLine = INT_MAX; ++ } else { ++ iMaxLine = (int) iNew; ++ } ++} ++ ++static rsRetVal ++legacySetMaxMessageSize(void __attribute__((unused)) *pVal, int64_t iNew) ++{ ++ DEFiRet; ++ iRet = setMaxLine(iNew); ++ RETiRet; ++} ++ + static rsRetVal + setDebugFile(void __attribute__((unused)) *pVal, uchar *pNewVal) + { +@@ -534,10 +565,10 @@ CODESTARTobjQueryInterface(glbl) + pIf->GetLocalHostIP = GetLocalHostIP; + pIf->SetGlobalInputTermination = SetGlobalInputTermination; + pIf->GetGlobalInputTermState = GetGlobalInputTermState; ++ pIf->GetMaxLine = GetMaxLine; + #define SIMP_PROP(name) \ + pIf->Get##name = Get##name; \ + pIf->Set##name = Set##name; +- SIMP_PROP(MaxLine); + SIMP_PROP(OptimizeUniProc); + SIMP_PROP(ParseHOSTNAMEandTAG); + SIMP_PROP(PreserveFQDN); +@@ -561,7 +592,6 @@ CODESTARTobjQueryInterface(glbl) + finalize_it: + ENDobjQueryInterface(glbl) + +- + /* Reset config variables to default values. + * rgerhards, 2008-04-17 + */ +@@ -649,7 +679,7 @@ glblDoneLoadCnf(void) + "dropmsgswithmaliciousdnsptrrecords")) { + bDropMalPTRMsgs = (int) cnfparamvals[i].val.d.n; + } else if(!strcmp(paramblk.descr[i].name, "maxmessagesize")) { +- iMaxLine = (int) cnfparamvals[i].val.d.n; ++ setMaxLine(cnfparamvals[i].val.d.n); + } else { + dbgprintf("glblDoneLoadCnf: program error, non-handled " + "param '%s'\n", paramblk.descr[i].name); +@@ -681,8 +711,7 @@ BEGINAbstractObjClassInit(glbl, 1, OBJ_IS_CORE_MODULE) /* class, version */ + CHKiRet(regCfSysLineHdlr((uchar *)"localhostipif", 0, eCmdHdlrGetWord, setLocalHostIPIF, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"optimizeforuniprocessor", 0, eCmdHdlrBinary, NULL, &bOptimizeUniProc, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"preservefqdn", 0, eCmdHdlrBinary, NULL, &bPreserveFQDN, NULL)); +- CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, +- NULL, &iMaxLine, NULL)); ++ CHKiRet(regCfSysLineHdlr((uchar *)"maxmessagesize", 0, eCmdHdlrSize, legacySetMaxMessageSize, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, NULL)); + + INIT_ATOMIC_HELPER_MUT(mutTerminateInputs); +diff --git a/runtime/glbl.h b/runtime/glbl.h +index 44171f2..b89e202 100644 +--- a/runtime/glbl.h ++++ b/runtime/glbl.h +@@ -8,7 +8,7 @@ + * Please note that there currently is no glbl.c file as we do not yet + * have any implementations. + * +- * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. ++ * Copyright 2008-2015 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * +@@ -41,10 +41,10 @@ extern pid_t glbl_ourpid; + /* interfaces */ + BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */ + uchar* (*GetWorkDir)(void); ++ int (*GetMaxLine)(void); + #define SIMP_PROP(name, dataType) \ + dataType (*Get##name)(void); \ + rsRetVal (*Set##name)(dataType); +- SIMP_PROP(MaxLine, int) + SIMP_PROP(OptimizeUniProc, int) + SIMP_PROP(PreserveFQDN, int) + SIMP_PROP(DefPFFamily, int) +@@ -81,9 +81,10 @@ BEGINinterface(glbl) /* name must also be changed in ENDinterface macro! */ + /* next change is v9! */ + /* v8 - 2012-03-21 */ + prop_t* (*GetLocalHostIP)(void); ++ /* v9 - 2015-01-12 SetMaxLine method removed */ + #undef SIMP_PROP + ENDinterface(glbl) +-#define glblCURR_IF_VERSION 7 /* increment whenever you change the interface structure! */ ++#define glblCURR_IF_VERSION 9 /* increment whenever you change the interface structure! */ + /* version 2 had PreserveFQDN added - rgerhards, 2008-12-08 */ + + /* the remaining prototypes */ +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1216957-imjournal-ste-file.patch b/SOURCES/rsyslog-7.4.7-rhbz1216957-imjournal-ste-file.patch new file mode 100644 index 0000000..6c28db5 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1216957-imjournal-ste-file.patch @@ -0,0 +1,92 @@ +From 882f404873ab8410455b17c3e7940e5c645bd143 Mon Sep 17 00:00:00 2001 +From: Max Prokhorov +Date: Sun, 22 Nov 2015 23:15:27 +0300 +Subject: [PATCH] Fix imjournal state file issues + +Derived from upstream: + +* commit 64af99a: + imjournal: Initialize counter outside of while loop + +* commit 8a10940: + bugfix: afterRun entry point not correctly called + + The entry point was called at the wrong spot, only when the thread + had not already terminated by itself. This could cause various + cleanup to not be done. + + closes https://github.com/rsyslog/rsyslog/issues/882 + +* fix a segfault in imuxsock triggered by 8a10940 +--- + plugins/imjournal/imjournal.c | 3 ++- + plugins/imuxsock/imuxsock.c | 4 ++++ + threads.c | 9 +++++---- + 3 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/plugins/imjournal/imjournal.c b/plugins/imjournal/imjournal.c +index 36c7e04..65017c6 100755 +--- a/plugins/imjournal/imjournal.c ++++ b/plugins/imjournal/imjournal.c +@@ -588,6 +588,7 @@ finalize_it: + } + + BEGINrunInput ++ int count = 0; + CODESTARTrunInput + CHKiRet(ratelimitNew(&ratelimiter, "imjournal", NULL)); + dbgprintf("imjournal: ratelimiting burst %d, interval %d\n", cs.ratelimitBurst, +@@ -603,7 +604,7 @@ CODESTARTrunInput + * signalled to do so. This, however, is handled by the framework. + */ + while (glbl.GetGlobalInputTermState() == 0) { +- int count = 0, r; ++ int r; + + r = sd_journal_next(j); + if (r < 0) { +diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c +index df504dd..f50b912 100644 +--- a/plugins/imuxsock/imuxsock.c ++++ b/plugins/imuxsock/imuxsock.c +@@ -1419,6 +1419,10 @@ BEGINafterRun + int i; + CODESTARTafterRun + /* do cleanup here */ ++ if(startIndexUxLocalSockets == 1 && nfd == 1) { ++ /* No sockets were configured, no cleanup needed. */ ++ return RS_RET_OK; ++ } + /* Close the UNIX sockets. */ + for (i = 0; i < nfd; i++) + if (listeners[i].fd != -1) +diff --git a/threads.c b/threads.c +index 990733a..a38b18f 100644 +--- a/threads.c ++++ b/threads.c +@@ -79,6 +79,11 @@ static rsRetVal thrdDestruct(thrdInfo_t *pThis) + if(pThis->bIsActive == 1) { + thrdTerminate(pThis); + } ++ ++ /* call cleanup function, if any */ ++ if(pThis->pAfterRun != NULL) ++ pThis->pAfterRun(pThis); ++ + pthread_mutex_destroy(&pThis->mutThrd); + pthread_cond_destroy(&pThis->condThrdTerm); + free(pThis->name); +@@ -148,10 +153,6 @@ rsRetVal thrdTerminate(thrdInfo_t *pThis) + } + pthread_join(pThis->thrdID, NULL); /* wait for input thread to complete */ + +- /* call cleanup function, if any */ +- if(pThis->pAfterRun != NULL) +- pThis->pAfterRun(pThis); +- + RETiRet; + } + +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1222746-json-race.patch b/SOURCES/rsyslog-7.4.7-rhbz1222746-json-race.patch new file mode 100644 index 0000000..3482839 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1222746-json-race.patch @@ -0,0 +1,85 @@ +From f15dc13cab2814bdf40e909b8325e436ee1f3940 Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Thu, 14 Jul 2016 03:41:27 +0200 +Subject: [PATCH] Prevent races in calls to json_object_get_string() + +--- + runtime/msg.c | 10 ++++++++++ + runtime/msg.h | 1 + + 2 files changed, 11 insertions(+) + +diff --git a/runtime/msg.c b/runtime/msg.c +index 10ecf48..4c82182 100644 +--- a/runtime/msg.c ++++ b/runtime/msg.c +@@ -716,6 +716,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) + pM->rcvFrom.pRcvFrom = NULL; + pM->pRuleset = NULL; + pM->json = NULL; ++ pthread_mutex_init(&pM->json_mut, NULL); + memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt)); + memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP)); + pM->TAG.pszTAG = NULL; +@@ -861,6 +862,7 @@ CODESTARTobjDestruct(msg) + rsCStrDestruct(&pThis->pCSMSGID); + if(pThis->json != NULL) + json_object_put(pThis->json); ++ pthread_mutex_destroy(&pThis->json_mut); + if(pThis->pszUUID != NULL) + free(pThis->pszUUID); + # ifndef HAVE_ATOMIC_BUILTINS +@@ -1065,7 +1067,9 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) + psz = getRcvFromIP(pThis); + CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFromIP"), PROPTYPE_PSZ, (void*) psz)); + if(pThis->json != NULL) { ++ pthread_mutex_lock(&pThis->json_mut); + psz = (uchar*) json_object_get_string(pThis->json); ++ pthread_mutex_unlock(&pThis->json_mut); + CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("json"), PROPTYPE_PSZ, (void*) psz)); + } + +@@ -2546,7 +2550,9 @@ getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, un + field = json_object_object_get(parent, (char*)leaf); + } + if(field != NULL) { ++ pthread_mutex_lock(&pM->json_mut); + *pRes = (uchar*) strdup(json_object_get_string(field)); ++ pthread_mutex_unlock(&pM->json_mut); + *buflen = (int) ustrlen(*pRes); + *pbMustBeFreed = 1; + } +@@ -2985,7 +2991,9 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, + bufLen = 2; + *pbMustBeFreed = 0; + } else { ++ pthread_mutex_lock(&pMsg->json_mut); + pRes = (uchar*)strdup(json_object_get_string(pMsg->json)); ++ pthread_mutex_unlock(&pMsg->json_mut); + *pbMustBeFreed = 1; + } + break; +@@ -3686,7 +3694,9 @@ msgGetCEEVarNew(msg_t *pMsg, char *name) + goto done; + } + json = json_object_object_get(parent, (char*)leaf); ++ pthread_mutex_lock(&pMsg->json_mut); + val = (char*)json_object_get_string(json); ++ pthread_mutex_unlock(&pMsg->json_mut); + estr = es_newStrFromCStr(val, strlen(val)); + done: + return estr; +diff --git a/runtime/msg.h b/runtime/msg.h +index e7babdb..814468d 100644 +--- a/runtime/msg.h ++++ b/runtime/msg.h +@@ -107,6 +107,7 @@ struct msg { + struct syslogTime tRcvdAt;/* time the message entered this program */ + struct syslogTime tTIMESTAMP;/* (parsed) value of the timestamp */ + struct json_object *json; ++ pthread_mutex_t json_mut; /* prevent concurent mutability of the json_object in the msg */ + /* some fixed-size buffers to save malloc()/free() for frequently used fields (from the default templates) */ + uchar szRawMsg[CONF_RAWMSG_BUFSIZE]; /* most messages are small, and these are stored here (without malloc/free!) */ + uchar szHOSTNAME[CONF_HOSTNAME_BUFSIZE]; +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1223566-imrelp-multi-ruleset.patch b/SOURCES/rsyslog-7.4.7-rhbz1223566-imrelp-multi-ruleset.patch new file mode 100644 index 0000000..c4ab077 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1223566-imrelp-multi-ruleset.patch @@ -0,0 +1,299 @@ +From c661a2917d065339e00d7588320a653ba29792ea Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Mon, 27 Jun 2016 20:10:31 +0200 +Subject: [PATCH] Allow multiple rulesets in imrelp + +Disable legacy module config directives if new style was used +--- + plugins/imrelp/imrelp.c | 140 ++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 113 insertions(+), 27 deletions(-) + +diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c +index 5e0ae55..203e7cb 100644 +--- a/plugins/imrelp/imrelp.c ++++ b/plugins/imrelp/imrelp.c +@@ -75,6 +75,8 @@ static struct configSettings_s { + struct instanceConf_s { + uchar *pszBindPort; /* port to bind to */ + struct instanceConf_s *next; ++ uchar *pszBindRuleset; /* name of ruleset to bind to */ ++ ruleset_t *pBindRuleset; /* ruleset to bind listener to */ + }; + + +@@ -88,9 +90,20 @@ struct modConfData_s { + static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ + static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + ++/* module-global parameters */ ++static struct cnfparamdescr modpdescr[] = { ++ { "ruleset", eCmdHdlrGetWord, 0 }, ++}; ++static struct cnfparamblk modpblk = ++ { CNFPARAMBLK_VERSION, ++ sizeof(modpdescr)/sizeof(struct cnfparamdescr), ++ modpdescr ++ }; ++ + /* input instance parameters */ + static struct cnfparamdescr inppdescr[] = { +- { "port", eCmdHdlrString, CNFPARAM_REQUIRED } ++ { "port", eCmdHdlrString, CNFPARAM_REQUIRED }, ++ { "ruleset", eCmdHdlrGetWord, 0 } + }; + static struct cnfparamblk inppblk = + { CNFPARAMBLK_VERSION, +@@ -99,6 +112,9 @@ static struct cnfparamblk inppblk = + }; + + ++#include "im-helper.h" /* must be included AFTER the type definitions! */ ++static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */ ++ + + /* ------------------------------ callbacks ------------------------------ */ + +@@ -113,17 +129,18 @@ static struct cnfparamblk inppblk = + * we will only see the hostname (twice). -- rgerhards, 2009-10-14 + */ + static relpRetVal +-onSyslogRcv(uchar *pHostname, uchar *pIP, uchar *msg, size_t lenMsg) ++onSyslogRcv(void *pUsr, uchar *pHostname, uchar *pIP, uchar *msg, size_t lenMsg) + { + prop_t *pProp = NULL; + msg_t *pMsg; ++ instanceConf_t *inst = (instanceConf_t*) pUsr; + DEFiRet; + + CHKiRet(msgConstruct(&pMsg)); + MsgSetInputName(pMsg, pInputName); + MsgSetRawMsg(pMsg, (char*)msg, lenMsg); + MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY); +- MsgSetRuleset(pMsg, runModConf->pBindRuleset); ++ MsgSetRuleset(pMsg, inst->pBindRuleset); + pMsg->msgFlags = PARSE_HOSTNAME | NEEDS_PARSING; + + /* TODO: optimize this, we can store it inside the session, requires +@@ -155,6 +172,8 @@ createInstance(instanceConf_t **pinst) + inst->next = NULL; + + inst->pszBindPort = NULL; ++ inst->pszBindRuleset = NULL; ++ inst->pBindRuleset = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { +@@ -174,8 +193,10 @@ finalize_it: + static inline void + std_checkRuleset_genErrMsg(modConfData_t *modConf, __attribute__((unused)) instanceConf_t *inst) + { +- errmsg.LogError(0, NO_ERRCODE, "imrelp: ruleset '%s' not found - " +- "using default ruleset instead", modConf->pszBindRuleset); ++ errmsg.LogError(0, NO_ERRCODE, "imrelp[%s]: ruleset '%s' not found - " ++ "using default ruleset instead", ++ inst->pszBindPort, inst->pszBindRuleset); ++ + } + + +@@ -196,6 +217,14 @@ static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) + } + inst->pszBindPort = pNewVal; + ++ if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { ++ inst->pszBindRuleset = NULL; ++ } else { ++ CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset)); ++ } ++ inst->pBindRuleset = NULL; ++ ++ + finalize_it: + RETiRet; + } +@@ -205,18 +234,33 @@ static rsRetVal + addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst) + { + DEFiRet; ++ relpSrv_t *pSrv; ++ int relpRet; ++ + if(pRelpEngine == NULL) { + CHKiRet(relpEngineConstruct(&pRelpEngine)); + CHKiRet(relpEngineSetDbgprint(pRelpEngine, dbgprintf)); + CHKiRet(relpEngineSetFamily(pRelpEngine, glbl.GetDefPFFamily())); + CHKiRet(relpEngineSetEnableCmd(pRelpEngine, (uchar*) "syslog", eRelpCmdState_Required)); +- CHKiRet(relpEngineSetSyslogRcv(pRelpEngine, onSyslogRcv)); ++ CHKiRet(relpEngineSetSyslogRcv2(pRelpEngine, onSyslogRcv)); + if (!glbl.GetDisableDNS()) { + CHKiRet(relpEngineSetDnsLookupMode(pRelpEngine, 1)); + } + } + +- CHKiRet(relpEngineAddListner(pRelpEngine, inst->pszBindPort)); ++ CHKiRet(relpEngineListnerConstruct(pRelpEngine, &pSrv)); ++ CHKiRet(relpSrvSetLstnPort(pSrv, inst->pszBindPort)); ++ ++ relpSrvSetUsrPtr(pSrv, inst); ++ ++ relpRet = relpEngineListnerConstructFinalize(pRelpEngine, pSrv); ++ if(relpRet != RELP_RET_OK) { ++ errmsg.LogError(0, RS_RET_RELP_ERR, ++ "imrelp: could not activate relp listner, code %d", relpRet); ++ ABORT_FINALIZE(RS_RET_RELP_ERR); ++ } ++ ++ resetConfigVariables(NULL,NULL); + + finalize_it: + RETiRet; +@@ -249,6 +293,8 @@ CODESTARTnewInpInst + continue; + if(!strcmp(inppblk.descr[i].name, "port")) { + inst->pszBindPort = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ } else if(!strcmp(inppblk.descr[i].name, "ruleset")) { ++ inst->pszBindRuleset = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("imrelp: program error, non-handled " + "param '%s'\n", inppblk.descr[i].name); +@@ -264,41 +310,76 @@ BEGINbeginCnfLoad + CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; ++ pModConf->pszBindRuleset = NULL; + /* init legacy config variables */ + cs.pszBindRuleset = NULL; ++ bLegacyCnfModGlobalsPermitted = 1; + ENDbeginCnfLoad + ++BEGINsetModCnf ++ struct cnfparamvals *pvals = NULL; ++ int i; ++CODESTARTsetModCnf ++ pvals = nvlstGetParams(lst, &modpblk, NULL); ++ if(pvals == NULL) { ++ errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module " ++ "config parameters [module(...)]"); ++ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); ++ } ++ ++ if(Debug) { ++ dbgprintf("module (global) param blk for imrelp:\n"); ++ cnfparamsPrint(&modpblk, pvals); ++ } ++ ++ for(i = 0 ; i < modpblk.nParams ; ++i) { ++ if(!pvals[i].bUsed) ++ continue; ++ if(!strcmp(modpblk.descr[i].name, "ruleset")) { ++ loadModConf->pszBindRuleset = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ } else { ++ dbgprintf("imrelp: program error, non-handled " ++ "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); ++ } ++ } ++ /* remove all of our legacy module handlers, as they can not used in addition ++ * the the new-style config method. ++ */ ++ bLegacyCnfModGlobalsPermitted = 0; ++finalize_it: ++ if(pvals != NULL) ++ cnfparamvalsDestruct(pvals, &modpblk); ++ENDsetModCnf + + BEGINendCnfLoad + CODESTARTendCnfLoad +- if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { +- loadModConf->pszBindRuleset = NULL; ++ if(loadModConf->pszBindRuleset == NULL) { ++ if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { ++ loadModConf->pszBindRuleset = NULL; ++ } else { ++ CHKmalloc(loadModConf->pszBindRuleset = ustrdup(cs.pszBindRuleset)); ++ } + } else { +- CHKmalloc(loadModConf->pszBindRuleset = ustrdup(cs.pszBindRuleset)); ++ if((cs.pszBindRuleset != NULL) && (cs.pszBindRuleset[0] != '\0')) { ++ errmsg.LogError(0, RS_RET_DUP_PARAM, "imrelp: warning: ruleset " ++ "set via legacy directive ignored"); ++ } + } +- loadModConf->pBindRuleset = NULL; + finalize_it: + free(cs.pszBindRuleset); ++ cs.pszBindRuleset = NULL; + loadModConf = NULL; /* done loading */ + ENDendCnfLoad + + + BEGINcheckCnf +- rsRetVal localRet; +- ruleset_t *pRuleset; ++ instanceConf_t *inst; + CODESTARTcheckCnf +- /* we emulate the standard "ruleset query" code provided by the framework +- * for *instances* (which we can currently not support due to librelp). +- */ +- if(pModConf->pszBindRuleset == NULL) { +- pModConf->pBindRuleset = NULL; +- } else { +- localRet = ruleset.GetRuleset(pModConf->pConf, &pRuleset, pModConf->pszBindRuleset); +- if(localRet == RS_RET_NOT_FOUND) { +- std_checkRuleset_genErrMsg(pModConf, NULL); ++ for(inst = pModConf->root ; inst != NULL ; inst = inst->next) { ++ if(inst->pszBindRuleset == NULL && pModConf->pszBindRuleset != NULL) { ++ CHKmalloc(inst->pszBindRuleset = ustrdup(pModConf->pszBindRuleset)); + } +- CHKiRet(localRet); +- pModConf->pBindRuleset = pRuleset; ++ std_checkRuleset(pModConf, inst); + } + finalize_it: + ENDcheckCnf +@@ -311,8 +392,10 @@ CODESTARTactivateCnfPrePrivDrop + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(pModConf, inst); + } +- if(pRelpEngine == NULL) ++ if(pRelpEngine == NULL) { ++ errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "imrelp: no RELP listener defined, module ca n not run."); + ABORT_FINALIZE(RS_RET_NO_RUN); ++ } + finalize_it: + ENDactivateCnfPrePrivDrop + +@@ -326,10 +409,12 @@ BEGINfreeCnf + CODESTARTfreeCnf + for(inst = pModConf->root ; inst != NULL ; ) { + free(inst->pszBindPort); ++ free(inst->pszBindRuleset); + del = inst; + inst = inst->next; + free(del); + } ++ free(pModConf->pszBindRuleset); + ENDfreeCnf + + /* This is used to terminate the plugin. Note that the signal handler blocks +@@ -420,6 +505,7 @@ CODEqueryEtryPt_STD_IMOD_QUERIES + CODEqueryEtryPt_STD_CONF2_QUERIES + CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES + CODEqueryEtryPt_STD_CONF2_IMOD_QUERIES ++CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES + CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES + ENDqueryEtryPt + +@@ -437,8 +523,8 @@ CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(ruleset, CORE_COMPONENT)); + + /* register config file handlers */ +- CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverbindruleset", 0, eCmdHdlrGetWord, +- NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); ++ CHKiRet(regCfSysLineHdlr2((uchar*)"inputrelpserverbindruleset", 0, eCmdHdlrGetWord, ++ NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverrun", 0, eCmdHdlrGetWord, + addInstance, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1224336-ruleset-crash.patch b/SOURCES/rsyslog-7.4.7-rhbz1224336-ruleset-crash.patch new file mode 100644 index 0000000..000d51c --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1224336-ruleset-crash.patch @@ -0,0 +1,61 @@ +From 72e7eac21c492fb8a63f486315936dfbd66f9b05 Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Fri, 3 Apr 2015 16:41:03 +0200 +Subject: [PATCH] Make llDestroy safe + +Keep the destructed list in a consistent state as the provided +destructor may access the very same list again before llDestroy +finishes. + +The previous approach could actually trigger a segmentation violation +error for specific scenarios and configurations. +In one particular case, destructing the list of rulesets lead to +shutdown of an action DA queue and spooling its messages to disk which +in turn triggered a search for the ruleset name of the message trought +the destructed list. + +The change probably slightly degrades performace, but that shouldn't +have an observable effect in the current code base. +--- + runtime/linkedlist.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/runtime/linkedlist.c b/runtime/linkedlist.c +index 53aace4..df8b19f 100644 +--- a/runtime/linkedlist.c ++++ b/runtime/linkedlist.c +@@ -90,22 +90,24 @@ 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; ++ /* keep the list structure in a consistent state as ++ * the destructor bellow may reference it again ++ */ ++ pThis->pRoot = pElt->pNext; ++ if(pElt->pNext == NULL) ++ pThis->pLast = NULL; ++ + /* we ignore errors during destruction, as we need to try + * finish the linked list in any case. + */ +- llDestroyElt(pThis, pEltPrev); ++ llDestroyElt(pThis, pElt); ++ pElt = pThis->pRoot; + } +- /* now clean up the pointers */ +- pThis->pRoot = NULL; +- pThis->pLast = NULL; + + RETiRet; + } +-- +1.9.3 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1245194-imjournal-ste-file.patch b/SOURCES/rsyslog-7.4.7-rhbz1245194-imjournal-ste-file.patch new file mode 100644 index 0000000..b8271cb --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1245194-imjournal-ste-file.patch @@ -0,0 +1,203 @@ +From fed69cc7ac1bccb7012a68d954b58c6de9ad75e7 Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Sun, 10 Jul 2016 23:46:42 +0200 +Subject: [PATCH] Make state file handling in imjournal more robust + +--- + plugins/imjournal/imjournal.c | 98 ++++++++++++++++++++++++++++++------------- + 1 file changed, 70 insertions(+), 28 deletions(-) + +diff --git a/plugins/imjournal/imjournal.c b/plugins/imjournal/imjournal.c +index f97b745..1ebbd1c 100755 +--- a/plugins/imjournal/imjournal.c ++++ b/plugins/imjournal/imjournal.c +@@ -70,6 +70,7 @@ static struct configSettings_s { + int ratelimitInterval; + int ratelimitBurst; + int bIgnorePrevious; ++ int bIgnoreNonValidStatefile; + int iDfltSeverity; + int iDfltFacility; + char *dfltTag; +@@ -84,6 +85,7 @@ static struct cnfparamdescr modpdescr[] = { + { "ratelimit.burst", eCmdHdlrInt, 0 }, + { "persiststateinterval", eCmdHdlrInt, 0 }, + { "ignorepreviousmessages", eCmdHdlrBinary, 0 }, ++ { "ignorenonvalidstatefile", eCmdHdlrBinary, 0 }, + { "defaultseverity", eCmdHdlrSeverity, 0 }, + { "defaultfacility", eCmdHdlrString, 0 }, + { "defaulttag", eCmdHdlrGetWord, 0 }, +@@ -441,23 +443,36 @@ static rsRetVal + persistJournalState () { + DEFiRet; + FILE *sf; /* state file */ ++ char tmp_sf[MAXFNAME]; + char *cursor; + int ret = 0; + + /* On success, sd_journal_get_cursor() returns 1 in systemd + 197 or older and 0 in systemd 198 or newer */ + if ((ret = sd_journal_get_cursor(j, &cursor)) >= 0) { +- if ((sf = fopen(cs.stateFile, "wb")) != NULL) { ++ /* we create a temporary name by adding a ".tmp" ++ * suffix to the end of our state file's name ++ */ ++ snprintf(tmp_sf, sizeof(tmp_sf), "%s.tmp", cs.stateFile); ++ if ((sf = fopen(tmp_sf, "wb")) != NULL) { + if (fprintf(sf, "%s", cursor) < 0) { + iRet = RS_RET_IO_ERROR; + } + fclose(sf); + free(cursor); ++ /* change the name of the file to the configured one */ ++ if (iRet == RS_RET_OK && rename(tmp_sf, cs.stateFile) == -1) { ++ char errStr[256]; ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ iRet = RS_RET_IO_ERROR; ++ errmsg.LogError(0, iRet, "rename() failed: " ++ "'%s', new path: '%s'\n", errStr, cs.stateFile); ++ } + } else { + char errStr[256]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + errmsg.LogError(0, RS_RET_FOPEN_FAILURE, "fopen() failed: " +- "'%s', path: '%s'\n", errStr, cs.stateFile); ++ "'%s', path: '%s'\n", errStr, tmp_sf); + iRet = RS_RET_FOPEN_FAILURE; + } + } else { +@@ -515,6 +530,35 @@ finalize_it: + RETiRet; + } + ++/* Seek to the very end of the journal and ignore all older ++ * messages. ++ */ ++static rsRetVal ++skipOldMessages(void) ++{ ++ DEFiRet; ++ ++ if (sd_journal_seek_tail(j) < 0) { ++ char errStr[256]; ++ ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ errmsg.LogError(0, RS_RET_ERR, ++ "sd_journal_seek_tail() failed: '%s'", errStr); ++ ABORT_FINALIZE(RS_RET_ERR); ++ } ++ if (sd_journal_previous(j) < 0) { ++ char errStr[256]; ++ ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ errmsg.LogError(0, RS_RET_ERR, ++ "sd_journal_previous() failed: '%s'", errStr); ++ ABORT_FINALIZE(RS_RET_ERR); ++ } ++ ++finalize_it: ++ RETiRet; ++ ++} + + /* This function loads a journal cursor from the state file. + */ +@@ -546,41 +590,32 @@ loadJournalState() + errmsg.LogError(0, RS_RET_ERR, "imjournal: " + "couldn't seek to cursor `%s'\n", readCursor); + iRet = RS_RET_ERR; +- goto finalize_it; ++ } else { ++ sd_journal_next(j); + } +- sd_journal_next(j); + } else { + errmsg.LogError(0, RS_RET_IO_ERROR, "imjournal: " + "fscanf on state file `%s' failed\n", cs.stateFile); + iRet = RS_RET_IO_ERROR; +- goto finalize_it; + } ++ + fclose(r_sf); ++ ++ if (iRet != RS_RET_OK && cs.bIgnoreNonValidStatefile) { ++ /* ignore state file errors */ ++ iRet = RS_RET_OK; ++ errmsg.LogError(0, NO_ERRCODE, ++ "imjournal: ignoring invalid state file"); ++ if (cs.bIgnorePrevious) { ++ skipOldMessages(); ++ } ++ } + } else { + errmsg.LogError(0, RS_RET_FOPEN_FAILURE, "imjournal: " + "open on state file `%s' failed\n", cs.stateFile); + } +- } else { +- /* when IgnorePrevious, seek to the end of journal */ +- if (cs.bIgnorePrevious) { +- if (sd_journal_seek_tail(j) < 0) { +- char errStr[256]; +- +- rs_strerror_r(errno, errStr, sizeof(errStr)); +- errmsg.LogError(0, RS_RET_ERR, +- "sd_journal_seek_tail() failed: '%s'", errStr); +- ABORT_FINALIZE(RS_RET_ERR); +- } +- +- if (sd_journal_previous(j) < 0) { +- char errStr[256]; +- +- rs_strerror_r(errno, errStr, sizeof(errStr)); +- errmsg.LogError(0, RS_RET_ERR, +- "sd_journal_previous() failed: '%s'", errStr); +- ABORT_FINALIZE(RS_RET_ERR); +- } +- } ++ } else if (cs.bIgnorePrevious) { ++ skipOldMessages(); + } + + finalize_it: +@@ -598,6 +633,8 @@ CODESTARTrunInput + + if (cs.stateFile) { + CHKiRet(loadJournalState()); ++ } else if (cs.bIgnorePrevious) { ++ skipOldMessages(); + } + + /* this is an endless loop - it is terminated when the thread is +@@ -641,6 +678,7 @@ BEGINbeginCnfLoad + CODESTARTbeginCnfLoad + bLegacyCnfModGlobalsPermitted = 1; + ++ cs.bIgnoreNonValidStatefile = 1; + cs.iPersistStateInterval = DFLT_persiststateinterval; + cs.stateFile = NULL; + cs.ratelimitBurst = 20000; +@@ -739,7 +777,9 @@ CODESTARTsetModCnf + } else if(!strcmp(modpblk.descr[i].name, "ratelimit.interval")) { + cs.ratelimitInterval = (int) pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "ignorepreviousmessages")) { +- cs.bIgnorePrevious = (int) pvals[i].val.d.n; ++ cs.bIgnorePrevious = (int) pvals[i].val.d.n; ++ } else if (!strcmp(modpblk.descr[i].name, "ignorenonvalidstatefile")) { ++ cs.bIgnoreNonValidStatefile = (int) pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "defaultseverity")) { + cs.iDfltSeverity = (int) pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "defaultfacility")) { +@@ -806,7 +846,9 @@ CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournalstatefile", 0, eCmdHdlrGetWord, + NULL, &cs.stateFile, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournalignorepreviousmessages", 0, eCmdHdlrBinary, +- NULL, &cs.bIgnorePrevious, STD_LOADABLE_MODULE_ID)); ++ NULL, &cs.bIgnorePrevious, STD_LOADABLE_MODULE_ID)); ++ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournalignorenonvalidstatefile", 0, eCmdHdlrBinary, ++ NULL, &cs.bIgnoreNonValidStatefile, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournaldefaultseverity", 0, eCmdHdlrSeverity, + NULL, &cs.iDfltSeverity, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournaldefaultfacility", 0, eCmdHdlrCustomHandler, +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1263853-startup-order.patch b/SOURCES/rsyslog-7.4.7-rhbz1263853-startup-order.patch new file mode 100644 index 0000000..65e2551 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1263853-startup-order.patch @@ -0,0 +1,24 @@ +From d0e0108bde62119a0b759fe5b88262fa375fc108 Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Thu, 7 Jul 2016 01:33:45 +0200 +Subject: [PATCH] Order service start after the network + +--- + rsyslog.service.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/rsyslog.service.in b/rsyslog.service.in +index 8e2d64c..2c44f3b 100644 +--- a/rsyslog.service.in ++++ b/rsyslog.service.in +@@ -1,6 +1,7 @@ + [Unit] + Description=System Logging Service + Requires=syslog.socket ++After=network.target + + [Service] + Type=notify +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1282687-ruleset-parser-crash.patch b/SOURCES/rsyslog-7.4.7-rhbz1282687-ruleset-parser-crash.patch new file mode 100644 index 0000000..ba5ca5b --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1282687-ruleset-parser-crash.patch @@ -0,0 +1,28 @@ +From c7f33b1edeffc8b7a2da2f7665e45c42f4b4e5db Mon Sep 17 00:00:00 2001 +From: Rainer Gerhards +Date: Wed, 9 Jul 2014 11:56:18 +0200 +Subject: [PATCH] bugfix: double-free when ruleset() parser parameters were + used + +While unlikely, this could cause stability issues even after the +config phase. +--- + runtime/ruleset.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/runtime/ruleset.c b/runtime/ruleset.c +index 1afb403..8e8f63b 100644 +--- a/runtime/ruleset.c ++++ b/runtime/ruleset.c +@@ -993,7 +993,7 @@ rulesetProcessCnf(struct cnfobj *o) + for(i = 0 ; i < ar->nmemb ; ++i) { + parserName = (uchar*)es_str2cstr(ar->arr[i], NULL); + doRulesetAddParser(pRuleset, parserName); +- free(parserName); ++ /* note parserName is freed in doRulesetAddParser()! */ + } + } + +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1295798-shutdown-delay.patch b/SOURCES/rsyslog-7.4.7-rhbz1295798-shutdown-delay.patch new file mode 100644 index 0000000..42ebc13 --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1295798-shutdown-delay.patch @@ -0,0 +1,77 @@ +From fde188d9bdd5845f3b1928c2cd61ddf5a541e657 Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Mon, 4 Jul 2016 16:04:51 +0200 +Subject: [PATCH] Fix a race condition in wtp + +The shutdown sequence in wtp relies for its operation on signals and +cancelation cleanup handlers. Previously, trying to shutdown a thread +that has not yet been properly initialized could lead to a deadlock. +This change makes wtpStartWrkr() synchronous in regard to the +initialization of the newly created thread. + +Thanks to Rado Sroka for the analysis and an initial patch. +--- + runtime/wtp.c | 13 +++++++++++++ + runtime/wtp.h | 1 + + 2 files changed, 14 insertions(+) + +diff --git a/runtime/wtp.c b/runtime/wtp.c +index 19151e7..d48a2d7 100644 +--- a/runtime/wtp.c ++++ b/runtime/wtp.c +@@ -88,6 +88,7 @@ static rsRetVal NotImplementedDummy() { return RS_RET_NOT_IMPLEMENTED; } + */ + BEGINobjConstruct(wtp) /* be sure to specify the object type also in END macro! */ + pthread_mutex_init(&pThis->mutWtp, NULL); ++ pthread_cond_init(&pThis->condThrdInitDone, NULL); + pthread_cond_init(&pThis->condThrdTrm, NULL); + pthread_attr_init(&pThis->attrThrd); + /* Set thread scheduling policy to default */ +@@ -155,6 +156,7 @@ CODESTARTobjDestruct(wtp) + + /* actual destruction */ + pthread_cond_destroy(&pThis->condThrdTrm); ++ pthread_cond_destroy(&pThis->condThrdInitDone); + pthread_mutex_destroy(&pThis->mutWtp); + pthread_attr_destroy(&pThis->attrThrd); + DESTROY_ATOMIC_HELPER_MUT(pThis->mutCurNumWrkThrd); +@@ -385,6 +387,12 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in + # endif + + pthread_cleanup_push(wtpWrkrExecCancelCleanup, pWti); ++ ++ /* let the parent know we're done with initialization */ ++ d_pthread_mutex_lock(&pThis->mutWtp); ++ pthread_cond_broadcast(&pThis->condThrdInitDone); ++ d_pthread_mutex_unlock(&pThis->mutWtp); ++ + wtiWorker(pWti); + pthread_cleanup_pop(0); + wtpWrkrExecCleanup(pWti); +@@ -436,6 +444,11 @@ wtpStartWrkr(wtp_t *pThis) + wtpGetDbgHdr(pThis), iState, + ATOMIC_FETCH_32BIT(&pThis->iCurNumWrkThrd, &pThis->mutCurNumWrkThrd)); + ++ /* wait for the new thread to initialize its signal mask and ++ * cancelation cleanup handler before proceeding ++ */ ++ d_pthread_cond_wait(&pThis->condThrdInitDone, &pThis->mutWtp); ++ + finalize_it: + d_pthread_mutex_unlock(&pThis->mutWtp); + RETiRet; +diff --git a/runtime/wtp.h b/runtime/wtp.h +index 25992f7..63912e6 100644 +--- a/runtime/wtp.h ++++ b/runtime/wtp.h +@@ -50,6 +50,7 @@ struct wtp_s { + rsRetVal (*pConsumer)(void *); /* user-supplied consumer function for dewtpd messages */ + /* synchronization variables */ + pthread_mutex_t mutWtp; /* mutex for the wtp's thread management */ ++ pthread_cond_t condThrdInitDone; /* signalled when a new thread is ready for work */ + pthread_cond_t condThrdTrm;/* signalled when threads terminate */ + /* end sync variables */ + /* user objects */ +-- +2.5.5 + diff --git a/SOURCES/rsyslog-7.4.7-rhbz1303617-imfile-wildcards.patch b/SOURCES/rsyslog-7.4.7-rhbz1303617-imfile-wildcards.patch new file mode 100644 index 0000000..7efabbe --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1303617-imfile-wildcards.patch @@ -0,0 +1,2211 @@ +--- ./config.h.in.fix ++++ ./config.h.in +@@ -97,6 +97,9 @@ + /* set define */ + #undef HAVE_GLOB_NOMAGIC + ++/* Define to 1 if you have the `inotify_init` function. */ ++#undef HAVE_INOTIFY_INIT ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_INTTYPES_H + +@@ -265,6 +268,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_IOCTL_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_INOTIFY_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_SYS_PARAM_H + +--- ./configure.fix ++++ ./configure +@@ -13958,7 +13958,7 @@ $as_echo "#define HAVE_SYS_WAIT_H 1" >>c + + fi + +-for ac_header in arpa/inet.h libgen.h malloc.h fcntl.h locale.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h sys/stat.h syslog.h unistd.h utmp.h utmpx.h sys/epoll.h sys/prctl.h ++for ac_header in arpa/inet.h libgen.h malloc.h fcntl.h locale.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/file.h sys/inotify.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h sys/stat.h syslog.h unistd.h utmp.h utmpx.h sys/epoll.h sys/prctl.h + do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` + ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +@@ -15153,7 +15153,7 @@ fi + done + + +-for ac_func in flock basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setsid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64 ++for ac_func in flock inotify_init basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setsid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64 + do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` + ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" + +#diff --git a/configure.ac b/configure.ac +#index 123ed7e..be5d7f1 100644 +#--- a/configure.ac +#+++ b/configure.ac +#@@ -85,7 +85,7 @@ AC_SUBST(DL_LIBS) +# AC_HEADER_RESOLV +# AC_HEADER_STDC +# AC_HEADER_SYS_WAIT +#-AC_CHECK_HEADERS([arpa/inet.h libgen.h malloc.h fcntl.h locale.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h sys/stat.h syslog.h unistd.h utmp.h utmpx.h sys/epoll.h sys/prctl.h]) +#+AC_CHECK_HEADERS([arpa/inet.h libgen.h malloc.h fcntl.h locale.h netdb.h netinet/in.h paths.h stddef.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h sys/stat.h sys/inotify.h syslog.h unistd.h utmp.h utmpx.h sys/epoll.h sys/prctl.h]) +# +# # Checks for typedefs, structures, and compiler characteristics. +# AC_C_CONST +#@@ -121,7 +121,7 @@ AC_TYPE_SIGNAL +# AC_FUNC_STAT +# AC_FUNC_STRERROR_R +# AC_FUNC_VPRINTF +#-AC_CHECK_FUNCS([flock basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setsid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64]) +#+AC_CHECK_FUNCS([flock inotify_init basename alarm clock_gettime gethostbyname gethostname gettimeofday localtime_r memset mkdir regcomp select setsid socket strcasecmp strchr strdup strerror strndup strnlen strrchr strstr strtol strtoul uname ttyname_r getline malloc_trim prctl epoll_create epoll_create1 fdatasync syscall lseek64]) +# +# # getifaddrs is in libc (mostly) or in libsocket (eg Solaris 11) or not defined (eg Solaris 10) +# AC_SEARCH_LIBS([getifaddrs], [socket], [AC_DEFINE(HAVE_GETIFADDRS, [1], [set define])]) + +diff --git a/doc/imfile.html b/doc/imfile.html +index a69f62e..4592cde 100644 +--- a/doc/imfile.html ++++ b/doc/imfile.html +@@ -30,26 +30,74 @@ lines from the previous file can no longer be obtained.

    + records the last processed location and continues to work from there + upon restart. So no data is lost during a restart (except, as noted + above, if the file is rotated just in this very moment).

    +-

    Currently, the file must have a fixed name and location +-(directory). It is planned to add support for dynamically generating +-file names in the future.

    +-

    Multiple files may be monitored by specifying +-$InputRunFileMonitor multiple times. +-

    ++ ++State Files
    ++

    Rsyslog must keep track of which parts of the monitored file ++are already processed. This is done in so-called “state files”. ++These files are always created in the rsyslog working directory ++(configurable via $WorkDirectory).

    ++

    To avoid problems with duplicate state files, rsyslog automatically ++generates state file names according to the following scheme:

    ++
      ++
    • the string “imfile-state:” is added before the actual file name, ++which includes the full path
    • ++
    • the full name is prepended after that string, but all occurrences ++of “/” are replaced by “-” to facilitate handling of these files
    • ++
    ++

    As a concrete example, consider file /var/log/applog is ++being monitored. The corresponding state file will be named ++imfile-state:-var-log-applog.

    ++

    Note that it is possible to set a fixed state file name via the ++deprecated “stateFile” parameter. It is suggested to avoid this, as ++the user must take care of name clashes. Most importantly, if ++“stateFile” is set for file monitors with wildcards, the same ++state file is used for all occurrences of these files. In short, ++this will usually not work and cause confusion. Upon startup, ++rsyslog tries to detect these cases and emit warning messages. ++However, the detection simply checks for the presence of “*” ++and as such it will not cover more complex cases.

    ++

    Note that when $WorkDirectory is not set or ++set to a non-writable location, the state file will not be generated. ++In those cases, the file content will always be completely re-sent by ++imfile, because the module does not know that it already processed ++parts of that file.

    ++ ++Metadata
    ++

    The imfile module supports message metadata. It supports the following ++data items

    ++
      ++
    • filename

      ++

      Name of the file where the message originated from. This is most ++useful when using wildcards inside file monitors, because it then ++is the only way to know which file the message originated from. ++The value can be accessed using the %$!metadata!filename% property.

      ++
    • ++
    ++

    Metadata is only present if enabled. By default it is enabled for ++input() statements that contain wildcards. For all others, it is ++disabled by default. It can explicitly be turned on or off via the ++addMetadata input() parameter, which always overrides the default.

    ++ ++ + +

    Configuration Directives:

    +

    Module Directives

    +
      +-
    • PollingInterval +-seconds
      +-This is a global setting. It specifies how often files are to be polled +-for new data. The time specified is in seconds. The default value is 10 +-seconds. Please note that future +-releases of imfile may support per-file polling intervals, but +-currently this is not the case. If multiple PollingInterval +-statements are present in rsyslog.conf, only the last one is used.
      ++
    • mode (requires v8.1.5+)
      ++This specifies if imfile is shall run in inotify ("inotify", default) ++or polling ("polling") mode. Traditionally, imfile used polling mode, which is much more ++resource-intense (and slower) than inotify mode. It is suggested that users ++turn on "polling" mode only if they experience strange problems in inotify mode. ++In theory, there never should be a reason to enable "polling" mode and later versions ++will most probably remove that mode. ++ ++
    • PollingInterval
      ++This setting specifies how often files are to be polled for new data. ++For obvious reasons, it has effect only if imfile is runnin in polling mode. ++The time specified is in seconds. The default value ++is 10 seconds.
      + A short poll interval provides more rapid message forwarding, but +-requires more system resources. While it is possible, we stongly ++requires more system resources. While it is possible, we strongly + recommend not to set the polling interval to 0 seconds. That will make + rsyslogd become a CPU hog, taking up considerable resources. It is + supported, however, for the few very unusual situations where this +@@ -61,37 +109,31 @@ nothing is left to be processed.
    • + +

      Action Directives

      +
        +-
      • (required) File /path/to/file
        +-The file being monitored. So far, this must be an absolute name (no +-macros or templates)
      • +-
      • (required) Tag +-tag:
        ++
      • File (mandatory)
        ++The file to be monitored. This must be an absolute name.
      • ++ ++
      • StateFile
        ++This is the name of this file’s state file. This parameter should ++usually not be used. Check the section on “State Files” above ++for more details.
      • ++ ++
      • Tag (mandatory)
        + The tag to be used for messages that originate from this file. If you + would like to see the colon after the tag, you need to specify it here +-(as shown above).
      • +-
      • (required) StateFile +-<name-of-state-file>
        +-Rsyslog must keep track of which parts of the to be monitored file it +-already processed. This is done in the state file. This file always is +-created in the rsyslog working directory (configurable via +-$WorkDirectory). Be careful to use unique names for different files +-being monitored. If there are duplicates, all sorts of "interesting" +-things may happen. Rsyslog currently does not check if a name is +-specified multiple times. +-Note that when $WorkDirectory is not set or set to a non-writable +-location, the state file will not be generated.
      • +-
      • Facility +-facility
        ++(like 'tag="tag:"').
      • ++ ++
      • Facility
        + The syslog facility to be assigned to lines read. Can be specified in + textual form (e.g. "local0", "local1", ...) or as numbers (e.g. 128 for +-"local0"). Textual form is suggested. Default  is +-"local0".
      • +-
      • Severity
        +-The +-syslog severity to be assigned to lines read. Can be specified in ++"local0"). Textual form is suggested. Default is ++"local0".
      • ++ ++
      • Severity
        ++The syslog severity to be assigned to lines read. Can be specified in + textual form (e.g. "info", "warning", ...) or as numbers (e.g. 4 for +-"info"). Textual form is suggested. Default ++"info"). Textual form is suggested. Default + is "notice".
      • ++ +
      • PersistStateInterval [lines]
        + Specifies how often the state file shall be written when processing the input + file. The default value is 0, which means a new state file is only written when +@@ -101,20 +143,29 @@ been processed. This setting can be used to guard against message duplication du + to fatal errors (like power fail). Note that this setting affects imfile + performance, especially when set to a low value. Frequently writing the state + file is very time consuming. ++ +
      • ReadMode [mode]
        + This mode should defined when having multiline messages. The value can range from 0-2 and determines the multiline detection method. +
        0 (default) - line based (Each line is a new message) +
        1 - paragraph (There is a blank line between log messages) +
        2 - indented (New log messages start at the beginning of a line. If a line starts with a space it is part of the log message before it) ++ +
      • MaxLinesAtOnce [number] +-
        +-This is useful if multiple files need to be monitored. If set to 0, each file +-will be fully processed and then processing switches to the next file +-(this was the default in previous versions). If it is set, a maximum of +-[number] lines is processed in sequence for each file, and then the file is +-switched. This provides a kind of mutiplexing the load of multiple files and +-probably leads to a more natural distribution of events when multiple busy files +-are monitored. The default is 1024. ++

        This is a legacy setting that only is supported in polling mode. ++In inotify mode, it is fixed at 0 and all attempts to configure ++a different value will be ignored, but will generate an error ++message.

        ++

        Please note that future versions of imfile may not support this ++parameter at all. So it is suggested to not use it.

        ++

        In polling mode, if set to 0, each file will be fully processed and ++then processing switches to the next file. If it is set to any other ++value, a maximum of [number] lines is processed in sequence for each file, ++and then the file is switched. This provides a kind of mutiplexing ++the load of multiple files and probably leads to a more natural ++distribution of events when multiple busy files are monitored. For ++polling mode, the default is 10240.

        ++
        ++ +
      • MaxSubmitAtOnce [number] +
        + This is an expert option. It can be used to set the maximum input batch size that +@@ -124,17 +175,38 @@ modify this option. If you do not know what this doc here talks about, this is a + good indication that you should NOT modify the default. +
      • Ruleset <ruleset> + Binds the listener to a specific ruleset.
      • ++ ++
      • deleteStateOnFileDelete [on/off] ++

        Default: on

        ++

        This parameter controls if state files are deleted if their associated ++main file is deleted. Usually, this is a good idea, because otherwise ++problems would occur if a new file with the same name is created. In ++that case, imfile would pick up reading from the last position in ++the deleted file, which usually is not what you want.

        ++

        However, there is one situation where not deleting associated state ++file makes sense: this is the case if a monitored file is modified ++with an editor (like vi or gedit). Most editors write out modifications ++by deleting the old file and creating a new now. If the state file ++would be deleted in that case, all of the file would be reprocessed, ++something that’s probably not intended in most case. As a side-note, ++it is strongly suggested not to modify monitored files with ++editors. In any case, in such a situation, it makes sense to ++disable state file deletion. That also applies to similar use ++cases.

        ++

        In general, this parameter should only by set if the users ++knows exactly why this is required.

        ++
      • ++ ++
      • addMetadata [on/off] ++

        Default: see intro section on Metadata

        ++

        This is used to turn on or off the addition of metadata to the ++message object.

        ++
      • ++ +
      ++ + Caveats/Known Bugs: +-

      So far, only 100 files can be monitored. If more are needed, +-the source needs to be patched. See define MAX_INPUT_FILES in imfile.c

      Powertop +-users may want to notice that imfile utilizes polling. Thus, it is no +-good citizen when it comes to conserving system power consumption. We +-are currently evaluating to move to inotify(). However, there are a +-number of subtle issues, which needs to be worked out first. We will +-make the change as soon as we can. If you can afford it, we recommend +-using a long polling interval in the mean time. +-

      ++

      Currently none. +

      Sample:

      +

      The following sample monitors two files. If you need just one, + remove the second one. If you need more, add them according to the +@@ -160,6 +232,11 @@ input(type="imfile" File="/path/to/file2" + + +

      Legacy Configuration Directives:

      ++

      Note: in order to preserve compatibility with previous versions, the ++LF escaping in multi-line messages is turned off for legacy-configured ++file monitors (the "escapeLF" input parameter). This can cause serious problems. ++So it is highly suggested that new deployments use the new input() statement ++and keep LF escaping turned on. +

        +
      • $InputFileName /path/to/file
        + equivalent to: File
      • +@@ -194,16 +271,6 @@ equivalent to: MaxLinesAtOnce + Available in 5.7.5+, 6.1.5+
        + equivalent to: Ruleset +
      +-Caveats/Known Bugs: +-

      So far, only 100 files can be monitored. If more are needed, +-the source needs to be patched. See define MAX_INPUT_FILES in imfile.c

      Powertop +-users may want to notice that imfile utilizes polling. Thus, it is no +-good citizen when it comes to conserving system power consumption. We +-are currently evaluating to move to inotify(). However, there are a +-number of subtle issues, which needs to be worked out first. We will +-make the change as soon as we can. If you can afford it, we recommend +-using a long polling interval in the mean time. +-

      +

      Sample:

      +

      The following sample monitors two files. If you need just one, + remove the second one. If you need more, add them according to the +@@ -234,7 +301,7 @@ $InputFilePollInterval 10 + [manual index] [rsyslog site]

      +

      This documentation is part of the + rsyslog project.
      +-Copyright © 2008 by Rainer ++Copyright © 2008-2014 by Rainer + Gerhards and Adiscon. + Released under the GNU GPL version 3 or higher.

      + +diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c +index 9c824c1..a094f77 100644 +--- a/plugins/imfile/imfile.c ++++ b/plugins/imfile/imfile.c +@@ -31,6 +31,13 @@ + #include + #include + #include /* do NOT remove: will soon be done by the module generation macros */ ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_INOTIFY_H ++#include ++#endif + #ifdef HAVE_SYS_STAT_H + # include + #endif +@@ -70,22 +77,40 @@ static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config para + #define NUM_MULTISUB 1024 /* default max number of submits */ + #define DFLT_PollInterval 10 + +-typedef struct fileInfo_s { ++#define INIT_FILE_TAB_SIZE 4 /* default file table size - is extended as needed, use 2^x value */ ++#define INIT_FILE_IN_DIR_TAB_SIZE 1 /* initial size for "associated files tab" in directory table */ ++#define INIT_WDMAP_TAB_SIZE 1 /* default wdMap table size - is extended as needed, use 2^x value */ ++ ++#define ADD_METADATA_UNSPECIFIED -1 ++ ++/* this structure is used in pure polling mode as well one of the support ++ * structures for inotify. ++ */ ++typedef struct lstn_s { ++ struct lstn_s *next, *prev; ++ struct lstn_s *masterLstn;/* if dynamic file (via wildcard), this points to the configured ++ * master entry. For master entries, it is always NULL. Only ++ * dynamic files can be deleted from the "files" list. */ + uchar *pszFileName; ++ uchar *pszDirName; ++ uchar *pszBaseName; + uchar *pszTag; + size_t lenTag; +- uchar *pszStateFile; /* file in which state between runs is to be stored */ ++ uchar *pszStateFile; /* file in which state between runs is to be stored (dynamic if NULL) */ + int iFacility; + int iSeverity; + int maxLinesAtOnce; + int nRecords; /**< How many records did we process before persisting the stream? */ + int iPersistStateInterval; /**< how often should state be persisted? (0=on close only) */ + strm_t *pStrm; /* its stream (NULL if not assigned) */ ++ sbool bRMStateOnDel; ++ sbool hasWildcard; + int readMode; /* which mode to use in ReadMulteLine call? */ ++ sbool addMetadata; + ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + ratelimit_t *ratelimiter; + multi_submit_t multiSub; +-} fileInfo_t; ++} lstn_t; + + static struct configSettings_s { + uchar *pszFileName; +@@ -103,6 +128,8 @@ static struct configSettings_s { + + struct instanceConf_s { + uchar *pszFileName; ++ uchar *pszDirName; ++ uchar *pszFileBaseName; + uchar *pszTag; + uchar *pszStateFile; + uchar *pszBindRuleset; +@@ -110,7 +137,9 @@ struct instanceConf_s { + int iPersistStateInterval; + int iFacility; + int iSeverity; ++ sbool bRMStateOnDel; + int readMode; ++ sbool addMetadata; + int maxLinesAtOnce; + ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + struct instanceConf_s *next; +@@ -118,28 +147,94 @@ struct instanceConf_s { + + + /* forward definitions */ +-static rsRetVal persistStrmState(fileInfo_t *pInfo); ++static rsRetVal persistStrmState(lstn_t *pInfo); + static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + ++ ++#define OPMODE_POLLING 0 ++#define OPMODE_INOTIFY 1 ++ + /* config variables */ + struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + int iPollInterval; /* number of seconds to sleep when there was no file activity */ + instanceConf_t *root, *tail; ++ lstn_t *pRootLstn; ++ lstn_t *pTailLstn; ++ uint8_t opMode; + sbool configSetViaV2Method; + }; + static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ + static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + +-static int iFilPtr = 0; /* number of files to be monitored; pointer to next free spot during config */ +-#define MAX_INPUT_FILES 100 +-static fileInfo_t files[MAX_INPUT_FILES]; ++#if HAVE_INOTIFY_INIT ++/* support for inotify mode */ ++ ++/* we need to track directories */ ++struct dirInfoFiles_s { /* associated files */ ++ lstn_t *pLstn; ++ int refcnt; /* due to inotify's async nature, we may have multiple ++ * references to a single file inside our cache - e.g. when ++ * inodes are removed, and the file name is re-created BUT another ++ * process (like rsyslogd ;)) holds open the old inode. ++ */ ++}; ++typedef struct dirInfoFiles_s dirInfoFiles_t; ++ ++/* This structure is a dynamic table to track file entries */ ++struct fileTable_s { ++ dirInfoFiles_t *listeners; ++ int currMax; ++ int allocMax; ++}; ++typedef struct fileTable_s fileTable_t; ++ ++/* The dirs table (defined below) contains one entry for each directory that ++ * is to be monitored. For each directory, it contains array which point to ++ * the associated *active* files as well as *configured* files. Note that ++ * the configured files may currently not exist, but will be processed ++ * when they are created. ++ */ ++struct dirInfo_s { ++ uchar *dirName; ++ fileTable_t active; /* associated active files */ ++ fileTable_t configured; /* associated configured files */ ++}; ++typedef struct dirInfo_s dirInfo_t; ++static dirInfo_t *dirs = NULL; ++static int allocMaxDirs; ++static int currMaxDirs; ++/* the following two macros are used to select the correct file table */ ++#define ACTIVE_FILE 1 ++#define CONFIGURED_FILE 0 ++ ++ ++/* We need to map watch descriptors to our actual objects. Unfortunately, the ++ * inotify API does not provide us with any cookie, so a simple O(1) algorithm ++ * cannot be done (what a shame...). We assume that maintaining the array is much ++ * less often done than looking it up, so we keep the array sorted by watch descripor ++ * and do a binary search on the wd we get back. This is at least O(log n), which ++ * is not too bad for the anticipated use case. ++ */ ++struct wd_map_s { ++ int wd; /* ascending sort key */ ++ lstn_t *pLstn; /* NULL, if this is a dir entry, otherwise pointer into listener(file) table */ ++ int dirIdx; /* index into dirs table, undefined if pLstn == NULL */ ++}; ++typedef struct wd_map_s wd_map_t; ++static wd_map_t *wdmap = NULL; ++static int nWdmap; ++static int allocMaxWdmap; ++static int ino_fd; /* fd for inotify calls */ ++ ++#endif /* #if HAVE_INOTIFY_INIT 40 -------------------------------------------------- */ + + static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this input */ + + /* module-global parameters */ + static struct cnfparamdescr modpdescr[] = { +- { "pollinginterval", eCmdHdlrPositiveInt, 0 } ++ { "pollinginterval", eCmdHdlrPositiveInt, 0 }, ++ { "mode", eCmdHdlrGetWord, 0 } + }; + static struct cnfparamblk modpblk = + { CNFPARAMBLK_VERSION, +@@ -150,7 +245,7 @@ static struct cnfparamblk modpblk = + /* input instance parameters */ + static struct cnfparamdescr inppdescr[] = { + { "file", eCmdHdlrString, CNFPARAM_REQUIRED }, +- { "statefile", eCmdHdlrString, CNFPARAM_REQUIRED }, ++ { "statefile", eCmdHdlrString, 0 }, + { "tag", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "severity", eCmdHdlrSeverity, 0 }, + { "facility", eCmdHdlrFacility, 0 }, +@@ -158,7 +253,10 @@ static struct cnfparamdescr inppdescr[] = { + { "readmode", eCmdHdlrInt, 0 }, + { "maxlinesatonce", eCmdHdlrInt, 0 }, + { "maxsubmitatonce", eCmdHdlrInt, 0 }, +- { "persiststateinterval", eCmdHdlrInt, 0 } ++ { "removestateondelete", eCmdHdlrBinary, 0 }, ++ { "persiststateinterval", eCmdHdlrInt, 0 }, ++ { "deletestateonfiledelete", eCmdHdlrBinary, 0 }, ++ { "addmetadata", eCmdHdlrBinary, 0 }, + }; + static struct cnfparamblk inppblk = + { CNFPARAMBLK_VERSION, +@@ -168,10 +266,171 @@ static struct cnfparamblk inppblk = + + #include "im-helper.h" /* must be included AFTER the type definitions! */ + ++ ++#if HAVE_INOTIFY_INIT ++/* support for inotify mode */ ++ ++#if 0 /* enable if you need this for debugging */ ++static void ++dbg_wdmapPrint(char *msg) ++{ ++ int i; ++ DBGPRINTF("%s\n", msg); ++ for(i = 0 ; i < nWdmap ; ++i) ++ DBGPRINTF("wdmap[%d]: wd: %d, file %d, dir %d\n", i, ++ wdmap[i].wd, wdmap[i].fIdx, wdmap[i].dirIdx); ++} ++#endif ++ ++static inline rsRetVal ++wdmapInit(void) ++{ ++ DEFiRet; ++ free(wdmap); ++ CHKmalloc(wdmap = malloc(sizeof(wd_map_t) * INIT_WDMAP_TAB_SIZE)); ++ allocMaxWdmap = INIT_WDMAP_TAB_SIZE; ++ nWdmap = 0; ++finalize_it: ++ RETiRet; ++} ++ ++/* looks up a wdmap entry by dirIdx and returns it's index if found ++ * or -1 if not found. ++ */ ++static int ++wdmapLookupListner(lstn_t* pLstn) ++{ ++ int i = 0; ++ int wd = -1; ++ /* Loop through */ ++ for(i = 0 ; i < nWdmap; ++i) { ++ if (wdmap[i].pLstn == pLstn) ++ wd = wdmap[i].wd; ++ } ++ ++ return wd; ++} ++ ++/* compare function for bsearch() */ ++static int ++wdmap_cmp(const void *k, const void *a) ++{ ++ int key = *((int*) k); ++ wd_map_t *etry = (wd_map_t*) a; ++ if(key < etry->wd) ++ return -1; ++ else if(key > etry->wd) ++ return 1; ++ else ++ return 0; ++} ++/* looks up a wdmap entry and returns it's index if found ++ * or -1 if not found. ++ */ ++static wd_map_t * ++wdmapLookup(int wd) ++{ ++ return bsearch(&wd, wdmap, nWdmap, sizeof(wd_map_t), wdmap_cmp); ++} ++ ++/* note: we search backwards, as inotify tends to return increasing wd's */ ++static rsRetVal ++wdmapAdd(int wd, const int dirIdx, lstn_t *const pLstn) ++{ ++ wd_map_t *newmap; ++ int newmapsize; ++ int i; ++ DEFiRet; ++ ++ for(i = nWdmap-1 ; i >= 0 && wdmap[i].wd > wd ; --i) ++ ; /* just scan */ ++ if(i >= 0 && wdmap[i].wd == wd) { ++ DBGPRINTF("imfile: wd %d already in wdmap!\n", wd); ++ ABORT_FINALIZE(RS_RET_FILE_ALREADY_IN_TABLE); ++ } ++ ++i; ++ /* i now points to the entry that is to be moved upwards (or end of map) */ ++ if(nWdmap == allocMaxWdmap) { ++ newmapsize = 2 * allocMaxWdmap; ++ CHKmalloc(newmap = realloc(wdmap, sizeof(wd_map_t) * newmapsize)); ++ // TODO: handle the error more intelligently? At all possible? -- 2013-10-15 ++ wdmap = newmap; ++ allocMaxWdmap = newmapsize; ++ } ++ if(i < nWdmap) { ++ /* we need to shift to make room for new entry */ ++ memmove(wdmap + i + 1, wdmap + i, sizeof(wd_map_t) * (nWdmap - i)); ++ } ++ wdmap[i].wd = wd; ++ wdmap[i].dirIdx = dirIdx; ++ wdmap[i].pLstn = pLstn; ++ ++nWdmap; ++ DBGPRINTF("imfile: enter into wdmap[%d]: wd %d, dir %d, lstn %s:%s\n",i,wd,dirIdx, ++ (pLstn == NULL) ? "DIRECTORY" : "FILE", ++ (pLstn == NULL) ? dirs[dirIdx].dirName : pLstn->pszFileName); ++ ++finalize_it: ++ RETiRet; ++} ++ ++static rsRetVal ++wdmapDel(const int wd) ++{ ++ int i; ++ DEFiRet; ++ ++ for(i = 0 ; i < nWdmap && wdmap[i].wd < wd ; ++i) ++ ; /* just scan */ ++ if(i == nWdmap || wdmap[i].wd != wd) { ++ DBGPRINTF("imfile: wd %d shall be deleted but not in wdmap!\n", wd); ++ FINALIZE; ++ } ++ ++ if(i < nWdmap-1) { ++ /* we need to shift to delete it (see comment at wdmap definition) */ ++ memmove(wdmap + i, wdmap + i + 1, sizeof(wd_map_t) * (nWdmap - i - 1)); ++ } ++ --nWdmap; ++ DBGPRINTF("imfile: wd %d deleted, was idx %d\n", wd, i); ++ ++finalize_it: ++ RETiRet; ++} ++ ++#endif /* #if HAVE_INOTIFY_INIT */ ++ ++ ++/* this generates a state file name suitable for the current file. To avoid ++ * malloc calls, it must be passed a buffer which should be MAXFNAME large. ++ * Note: the buffer is not necessarily populated ... always ONLY use the ++ * RETURN VALUE! ++ */ ++static uchar * ++getStateFileName(lstn_t *const __restrict__ pLstn, ++ uchar *const __restrict__ buf, ++ const size_t lenbuf) ++{ ++ uchar *ret; ++ if(pLstn->pszStateFile == NULL) { ++ snprintf((char*)buf, lenbuf - 1, "imfile-state:%s", pLstn->pszFileName); ++ buf[lenbuf-1] = '\0'; /* be on the safe side... */ ++ uchar *p = buf; ++ for( ; *p ; ++p) { ++ if(*p == '/') ++ *p = '-'; ++ } ++ ret = buf; ++ } else { ++ ret = pLstn->pszStateFile; ++ } ++ return ret; ++} ++ ++ + /* enqueue the read file line as a message. The provided string is + * not freed - thuis must be done by the caller. + */ +-static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine) ++static rsRetVal enqLine(lstn_t *pLstn, cstr_t *cstrLine) + { + DEFiRet; + msg_t *pMsg; +@@ -184,14 +443,16 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine) + CHKiRet(msgConstruct(&pMsg)); + MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY); + MsgSetInputName(pMsg, pInputName); +- MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine), cstrLen(cstrLine)); ++ MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStrNoNULL(cstrLine), cstrLen(cstrLine)); + MsgSetMSGoffs(pMsg, 0); /* we do not have a header... */ + MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())); +- MsgSetTAG(pMsg, pInfo->pszTag, pInfo->lenTag); +- pMsg->iFacility = LOG_FAC(pInfo->iFacility); +- pMsg->iSeverity = LOG_PRI(pInfo->iSeverity); +- MsgSetRuleset(pMsg, pInfo->pRuleset); +- ratelimitAddMsg(pInfo->ratelimiter, &pInfo->multiSub, pMsg); ++ MsgSetTAG(pMsg, pLstn->pszTag, pLstn->lenTag); ++ pMsg->iFacility = LOG_FAC(pLstn->iFacility); ++ pMsg->iSeverity = LOG_PRI(pLstn->iSeverity); ++ MsgSetRuleset(pMsg, pLstn->pRuleset); ++ if(pLstn->addMetadata) ++ msgAddMetadata(pMsg, (uchar*)"filename", pLstn->pszFileName); ++ ratelimitAddMsg(pLstn->ratelimiter, &pLstn->multiSub, pMsg); + finalize_it: + RETiRet; + } +@@ -201,30 +462,37 @@ finalize_it: + * if so, reading it in. Processing continues from the last know location. + */ + static rsRetVal +-openFile(fileInfo_t *pThis) ++openFile(lstn_t *pLstn) + { + DEFiRet; + strm_t *psSF = NULL; + uchar pszSFNam[MAXFNAME]; + size_t lenSFNam; + struct stat stat_buf; ++ uchar statefile[MAXFNAME]; + ++ uchar *const statefn = getStateFileName(pLstn, statefile, sizeof(statefile)); ++ DBGPRINTF("imfile: trying to open state for '%s', state file '%s'\n", ++ pLstn->pszFileName, statefn); + /* Construct file name */ +- lenSFNam = snprintf((char*)pszSFNam, sizeof(pszSFNam) / sizeof(uchar), "%s/%s", +- (char*) glbl.GetWorkDir(), (char*)pThis->pszStateFile); ++ lenSFNam = snprintf((char*)pszSFNam, sizeof(pszSFNam), "%s/%s", ++ (char*) glbl.GetWorkDir(), (char*)statefn); + + /* check if the file exists */ + if(stat((char*) pszSFNam, &stat_buf) == -1) { + if(errno == ENOENT) { +- dbgprintf("filemon %p: clean startup, no .si file found\n", pThis); ++ DBGPRINTF("imfile: clean startup, state file for '%s'\n", pLstn->pszFileName); + ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); + } else { +- dbgprintf("filemon %p: error %d trying to access .si file\n", pThis, errno); ++ char errStr[1024]; ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ DBGPRINTF("imfile: error trying to access state file for '%s':%s\n", ++ pLstn->pszFileName, errStr); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + } + +- /* If we reach this point, we have a .si file */ ++ /* If we reach this point, we have a state file */ + + CHKiRet(strm.Construct(&psSF)); + CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_READ)); +@@ -233,10 +501,22 @@ openFile(fileInfo_t *pThis) + CHKiRet(strm.ConstructFinalize(psSF)); + + /* read back in the object */ +- CHKiRet(obj.Deserialize(&pThis->pStrm, (uchar*) "strm", psSF, NULL, pThis)); ++ CHKiRet(obj.Deserialize(&pLstn->pStrm, (uchar*) "strm", psSF, NULL, pLstn)); ++ DBGPRINTF("imfile: deserialized state file, state file base name '%s', " ++ "configured base name '%s'\n", pLstn->pStrm->pszFName, ++ pLstn->pszFileName); ++ if(ustrcmp(pLstn->pStrm->pszFName, pLstn->pszFileName)) { ++ errmsg.LogError(0, RS_RET_STATEFILE_WRONG_FNAME, "imfile: state file '%s' " ++ "contains file name '%s', but is used for file '%s'. State " ++ "file deleted, starting from begin of file.", ++ pszSFNam, pLstn->pStrm->pszFName, pLstn->pszFileName); ++ ++ unlink((char*)pszSFNam); ++ ABORT_FINALIZE(RS_RET_STATEFILE_WRONG_FNAME); ++ } + +- strm.CheckFileChange(pThis->pStrm); +- CHKiRet(strm.SeekCurrOffs(pThis->pStrm)); ++ strm.CheckFileChange(pLstn->pStrm); ++ CHKiRet(strm.SeekCurrOffs(pLstn->pStrm)); + + /* note: we do not delete the state file, so that the last position remains + * known even in the case that rsyslogd aborts for some reason (like powerfail) +@@ -247,13 +527,13 @@ finalize_it: + strm.Destruct(&psSF); + + if(iRet != RS_RET_OK) { +- if(pThis->pStrm != NULL) +- strm.Destruct(&pThis->pStrm); +- CHKiRet(strm.Construct(&pThis->pStrm)); +- CHKiRet(strm.SettOperationsMode(pThis->pStrm, STREAMMODE_READ)); +- CHKiRet(strm.SetsType(pThis->pStrm, STREAMTYPE_FILE_MONITOR)); +- CHKiRet(strm.SetFName(pThis->pStrm, pThis->pszFileName, strlen((char*) pThis->pszFileName))); +- CHKiRet(strm.ConstructFinalize(pThis->pStrm)); ++ if(pLstn->pStrm != NULL) ++ strm.Destruct(&pLstn->pStrm); ++ CHKiRet(strm.Construct(&pLstn->pStrm)); ++ CHKiRet(strm.SettOperationsMode(pLstn->pStrm, STREAMMODE_READ)); ++ CHKiRet(strm.SetsType(pLstn->pStrm, STREAMTYPE_FILE_MONITOR)); ++ CHKiRet(strm.SetFName(pLstn->pStrm, pLstn->pszFileName, strlen((char*) pLstn->pszFileName))); ++ CHKiRet(strm.ConstructFinalize(pLstn->pStrm)); + } + + RETiRet; +@@ -275,39 +555,38 @@ static void pollFileCancelCleanup(void *pArg) + + /* poll a file, need to check file rollover etc. open file if not open */ + #pragma GCC diagnostic ignored "-Wempty-body" +-static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData) ++static rsRetVal pollFile(lstn_t *pLstn, int *pbHadFileData) + { + cstr_t *pCStr = NULL; +- int nProcessed = 0; + DEFiRet; + +- ASSERT(pbHadFileData != NULL); +- + /* Note: we must do pthread_cleanup_push() immediately, because the POXIS macros + * otherwise do not work if I include the _cleanup_pop() inside an if... -- rgerhards, 2008-08-14 + */ + pthread_cleanup_push(pollFileCancelCleanup, &pCStr); +- if(pThis->pStrm == NULL) { +- CHKiRet(openFile(pThis)); /* open file */ ++ int nProcessed = 0; ++ if(pLstn->pStrm == NULL) { ++ CHKiRet(openFile(pLstn)); /* open file */ + } + + /* loop below will be exited when strmReadLine() returns EOF */ + while(glbl.GetGlobalInputTermState() == 0) { +- if(pThis->maxLinesAtOnce != 0 && nProcessed >= pThis->maxLinesAtOnce) ++ if(pLstn->maxLinesAtOnce != 0 && nProcessed >= pLstn->maxLinesAtOnce) + break; +- CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode)); ++ CHKiRet(strm.ReadLine(pLstn->pStrm, &pCStr, pLstn->readMode)); + ++nProcessed; +- *pbHadFileData = 1; /* this is just a flag, so set it and forget it */ +- CHKiRet(enqLine(pThis, pCStr)); /* process line */ ++ if(pbHadFileData != NULL) ++ *pbHadFileData = 1; /* this is just a flag, so set it and forget it */ ++ CHKiRet(enqLine(pLstn, pCStr)); /* process line */ + rsCStrDestruct(&pCStr); /* discard string (must be done by us!) */ +- if(pThis->iPersistStateInterval > 0 && pThis->nRecords++ >= pThis->iPersistStateInterval) { +- persistStrmState(pThis); +- pThis->nRecords = 0; ++ if(pLstn->iPersistStateInterval > 0 && pLstn->nRecords++ >= pLstn->iPersistStateInterval) { ++ persistStrmState(pLstn); ++ pLstn->nRecords = 0; + } + } + + finalize_it: +- multiSubmitFlush(&pThis->multiSub); ++ multiSubmitFlush(&pLstn->multiSub); + pthread_cleanup_pop(0); + + if(pCStr != NULL) { +@@ -338,9 +617,11 @@ createInstance(instanceConf_t **pinst) + inst->nMultiSub = NUM_MULTISUB; + inst->iSeverity = 5; + inst->iFacility = 128; +- inst->maxLinesAtOnce = 10240; ++ inst->maxLinesAtOnce = 0; + inst->iPersistStateInterval = 0; + inst->readMode = 0; ++ inst->bRMStateOnDel = 1; ++ inst->addMetadata = ADD_METADATA_UNSPECIFIED; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { +@@ -356,8 +637,94 @@ finalize_it: + } + + ++/* the basen(ame) buffer must be of size MAXFNAME ++ * returns the index of the slash in front of basename ++ */ ++static int ++getBasename(uchar *const __restrict__ basen, uchar *const __restrict__ path) ++{ ++ int i; ++ int found = 0; ++ const int lenName = ustrlen(path); ++ for(i = lenName ; i >= 0 ; --i) { ++ if(path[i] == '/') { ++ /* found basename component */ ++ found = 1; ++ if(i == lenName) ++ basen[0] = '\0'; ++ else { ++ memcpy(basen, path+i+1, lenName-i); ++ } ++ break; ++ } ++ } ++ if (found == 1) ++ return i; ++ else { ++ return -1; ++ } ++} ++ ++/* this function checks instance parameters and does some required pre-processing ++ * (e.g. split filename in path and actual name) ++ * Note: we do NOT use dirname()/basename() as they have portability problems. ++ */ ++static rsRetVal ++checkInstance(instanceConf_t *inst) ++{ ++ char dirn[MAXFNAME]; ++ uchar basen[MAXFNAME]; ++ int i; ++ struct stat sb; ++ int r; ++ int eno; ++ char errStr[512]; ++ DEFiRet; ++ ++ /* this is primarily for the clang static analyzer, but also ++ * guards against logic errors in the config handler. ++ */ ++ if(inst->pszFileName == NULL) ++ ABORT_FINALIZE(RS_RET_INTERNAL_ERROR); ++ ++ i = getBasename(basen, inst->pszFileName); ++ if (i == -1) { ++ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile: file path '%s' does not include a basename component", ++ inst->pszFileName); ++ ABORT_FINALIZE(RS_RET_CONFIG_ERROR); ++ } ++ ++ memcpy(dirn, inst->pszFileName, i); /* do not copy slash */ ++ dirn[i] = '\0'; ++ CHKmalloc(inst->pszFileBaseName = (uchar*) strdup((char*)basen)); ++ CHKmalloc(inst->pszDirName = (uchar*) strdup(dirn)); ++ ++ if(dirn[0] == '\0') { ++ dirn[0] = '/'; ++ dirn[1] = '\0'; ++ } ++ r = stat(dirn, &sb); ++ if(r != 0) { ++ eno = errno; ++ rs_strerror_r(eno, errStr, sizeof(errStr)); ++ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile warning: directory '%s': %s", ++ dirn, errStr); ++ ABORT_FINALIZE(RS_RET_CONFIG_ERROR); ++ } ++ if(!S_ISDIR(sb.st_mode)) { ++ errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile warning: configured directory " ++ "'%s' is NOT a directory", dirn); ++ ABORT_FINALIZE(RS_RET_CONFIG_ERROR); ++ } ++ ++finalize_it: ++ RETiRet; ++} ++ ++ + /* add a new monitor */ +-static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) ++static rsRetVal ++addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) + { + instanceConf_t *inst; + DEFiRet; +@@ -370,10 +737,6 @@ static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) + errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile error: no tag value given , file monitor can not be created"); + ABORT_FINALIZE(RS_RET_CONFIG_ERROR); + } +- if(cs.pszStateFile == NULL) { +- errmsg.LogError(0, RS_RET_CONFIG_ERROR, "imfile error: not state file name given, file monitor can not be created"); +- ABORT_FINALIZE(RS_RET_CONFIG_ERROR); +- } + + CHKiRet(createInstance(&inst)); + if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { +@@ -383,12 +746,24 @@ static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) + } + inst->pszFileName = (uchar*) strdup((char*) cs.pszFileName); + inst->pszTag = (uchar*) strdup((char*) cs.pszFileTag); +- inst->pszStateFile = (uchar*) strdup((char*) cs.pszStateFile); ++ inst->pszStateFile = cs.pszStateFile == NULL ? NULL : (uchar*) strdup((char*) cs.pszStateFile); + inst->iSeverity = cs.iSeverity; + inst->iFacility = cs.iFacility; +- inst->maxLinesAtOnce = cs.maxLinesAtOnce; ++ if(cs.maxLinesAtOnce) { ++ if(loadModConf->opMode == OPMODE_INOTIFY) { ++ errmsg.LogError(0, RS_RET_PARAM_NOT_PERMITTED, ++ "parameter \"maxLinesAtOnce\" not " ++ "permited in inotify mode - ignored"); ++ } else { ++ inst->maxLinesAtOnce = cs.maxLinesAtOnce; ++ } ++ } + inst->iPersistStateInterval = cs.iPersistStateInterval; + inst->readMode = cs.readMode; ++ inst->addMetadata = 0; ++ inst->bRMStateOnDel = 0; ++ ++ CHKiRet(checkInstance(inst)); + + /* reset legacy system */ + cs.iPersistStateInterval = 0; +@@ -400,41 +775,155 @@ finalize_it: + } + + +-/* This function is called when a new listener (monitor) shall be added. */ ++/* This adds a new listener object to the bottom of the list, but ++ * it does NOT initialize any data members except for the list ++ * pointers themselves. ++ */ ++static rsRetVal ++lstnAdd(lstn_t **newLstn) ++{ ++ lstn_t *pLstn; ++ DEFiRet; ++ ++ CHKmalloc(pLstn = (lstn_t*) MALLOC(sizeof(lstn_t))); ++ if(runModConf->pRootLstn == NULL) { ++ runModConf->pRootLstn = pLstn; ++ pLstn->prev = NULL; ++ } else { ++ runModConf->pTailLstn->next = pLstn; ++ pLstn->prev = runModConf->pTailLstn; ++ } ++ runModConf->pTailLstn = pLstn; ++ pLstn->next = NULL; ++ *newLstn = pLstn; ++ ++finalize_it: ++ RETiRet; ++} ++ ++/* delete a listener object */ ++static void ++lstnDel(lstn_t *pLstn) ++{ ++ DBGPRINTF("imfile: lstnDel called for %s\n", pLstn->pszFileName); ++ if(pLstn->pStrm != NULL) { /* stream open? */ ++ persistStrmState(pLstn); ++ strm.Destruct(&(pLstn->pStrm)); ++ } ++ ratelimitDestruct(pLstn->ratelimiter); ++ free(pLstn->multiSub.ppMsgs); ++ free(pLstn->pszFileName); ++ free(pLstn->pszTag); ++ free(pLstn->pszStateFile); ++ free(pLstn->pszBaseName); ++ ++ if(pLstn == runModConf->pRootLstn) ++ runModConf->pRootLstn = pLstn->next; ++ if(pLstn == runModConf->pTailLstn) ++ runModConf->pTailLstn = pLstn->prev; ++ if(pLstn->next != NULL) ++ pLstn->next->prev = pLstn->prev; ++ if(pLstn->prev != NULL) ++ pLstn->prev->next = pLstn->next; ++ free(pLstn); ++} ++ ++/* Duplicate an existing listener. This is called when a new file is to ++ * be monitored due to wildcard detection. Returns the new pLstn in ++ * the ppExisting parameter. ++ */ ++static rsRetVal ++lstnDup(lstn_t **ppExisting, uchar *const __restrict__ newname) ++{ ++ DEFiRet; ++ lstn_t *const existing = *ppExisting; ++ lstn_t *pThis; ++ ++ CHKiRet(lstnAdd(&pThis)); ++ pThis->pszDirName = existing->pszDirName; /* read-only */ ++ pThis->pszBaseName = (uchar*)strdup((char*)newname); ++ if(asprintf((char**)&pThis->pszFileName, "%s/%s", (char*)pThis->pszDirName, (char*)newname) == -1) { ++ DBGPRINTF("imfile/lstnDup: asprintf failed, malfunction can happen\n"); ++ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); ++ } ++ pThis->pszTag = (uchar*) strdup((char*) existing->pszTag); ++ pThis->lenTag = ustrlen(pThis->pszTag); ++ pThis->pszStateFile = existing->pszStateFile == NULL ? NULL : (uchar*) strdup((char*) existing->pszStateFile); ++ ++ CHKiRet(ratelimitNew(&pThis->ratelimiter, "imfile", (char*)pThis->pszFileName)); ++ pThis->multiSub.maxElem = existing->multiSub.maxElem; ++ pThis->multiSub.nElem = 0; ++ CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(pThis->multiSub.maxElem * sizeof(msg_t*))); ++ pThis->iSeverity = existing->iSeverity; ++ pThis->iFacility = existing->iFacility; ++ pThis->maxLinesAtOnce = existing->maxLinesAtOnce; ++ pThis->iPersistStateInterval = existing->iPersistStateInterval; ++ pThis->readMode = existing->readMode; ++ pThis->bRMStateOnDel = existing->bRMStateOnDel; ++ pThis->hasWildcard = existing->hasWildcard; ++ pThis->addMetadata = existing->addMetadata; ++ pThis->pRuleset = existing->pRuleset; ++ pThis->nRecords = 0; ++ pThis->pStrm = NULL; ++ pThis->masterLstn = existing; ++ *ppExisting = pThis; ++finalize_it: ++ RETiRet; ++} ++ ++/* This function is called when a new listener shall be added. ++ * It also does some late stage error checking on the config ++ * and reports issues it finds. ++ */ + static inline rsRetVal + addListner(instanceConf_t *inst) + { + DEFiRet; +- fileInfo_t *pThis; +- +- if(iFilPtr < MAX_INPUT_FILES) { +- pThis = &files[iFilPtr]; +- //TODO: optimize, save strdup? +- pThis->pszFileName = (uchar*) strdup((char*) inst->pszFileName); +- pThis->pszTag = (uchar*) strdup((char*) inst->pszTag); +- pThis->lenTag = ustrlen(pThis->pszTag); +- pThis->pszStateFile = (uchar*) strdup((char*) inst->pszStateFile); +- +- CHKiRet(ratelimitNew(&pThis->ratelimiter, "imfile", (char*)inst->pszFileName)); +- CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(inst->nMultiSub * sizeof(msg_t*))); +- pThis->multiSub.maxElem = inst->nMultiSub; +- pThis->multiSub.nElem = 0; +- pThis->iSeverity = inst->iSeverity; +- pThis->iFacility = inst->iFacility; +- pThis->maxLinesAtOnce = inst->maxLinesAtOnce; +- pThis->iPersistStateInterval = inst->iPersistStateInterval; +- pThis->readMode = inst->readMode; +- pThis->pRuleset = inst->pBindRuleset; +- pThis->nRecords = 0; +- } else { +- errmsg.LogError(0, RS_RET_OUT_OF_DESRIPTORS, +- "Too many file monitors configured - ignoring %s", ++ lstn_t *pThis; ++ sbool hasWildcard; ++ ++ hasWildcard = containsGlobWildcard((char*)inst->pszFileBaseName); ++ if(hasWildcard) { ++ if(runModConf->opMode == OPMODE_POLLING) { ++ errmsg.LogError(0, RS_RET_IMFILE_WILDCARD, ++ "imfile: The to-be-monitored file \"%s\" contains " ++ "wildcards. This is not supported in " ++ "polling mode.", inst->pszFileName); ++ ABORT_FINALIZE(RS_RET_IMFILE_WILDCARD); ++ } else if(inst->pszStateFile != NULL) { ++ errmsg.LogError(0, RS_RET_IMFILE_WILDCARD, ++ "imfile: warning: it looks like to-be-monitored " ++ "file \"%s\" contains wildcards. This usually " ++ "does not work well with specifying a state file.", + inst->pszFileName); +- ABORT_FINALIZE(RS_RET_OUT_OF_DESRIPTORS); ++ } + } +- ++iFilPtr; /* we got a new file to monitor */ + +- resetConfigVariables(NULL, NULL); /* values are both dummies */ ++ CHKiRet(lstnAdd(&pThis)); ++ pThis->hasWildcard = hasWildcard; ++ pThis->pszFileName = (uchar*) strdup((char*) inst->pszFileName); ++ pThis->pszDirName = inst->pszDirName; /* use memory from inst! */ ++ pThis->pszBaseName = (uchar*)strdup((char*)inst->pszFileBaseName); /* be consistent with expanded wildcards! */ ++ pThis->pszTag = (uchar*) strdup((char*) inst->pszTag); ++ pThis->lenTag = ustrlen(pThis->pszTag); ++ pThis->pszStateFile = inst->pszStateFile == NULL ? NULL : (uchar*) strdup((char*) inst->pszStateFile); ++ ++ CHKiRet(ratelimitNew(&pThis->ratelimiter, "imfile", (char*)inst->pszFileName)); ++ CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(inst->nMultiSub * sizeof(msg_t*))); ++ pThis->multiSub.maxElem = inst->nMultiSub; ++ pThis->multiSub.nElem = 0; ++ pThis->iSeverity = inst->iSeverity; ++ pThis->iFacility = inst->iFacility; ++ pThis->maxLinesAtOnce = inst->maxLinesAtOnce; ++ pThis->iPersistStateInterval = inst->iPersistStateInterval; ++ pThis->readMode = inst->readMode; ++ pThis->bRMStateOnDel = inst->bRMStateOnDel; ++ pThis->addMetadata = (inst->addMetadata == ADD_METADATA_UNSPECIFIED) ? ++ hasWildcard : inst->addMetadata; ++ pThis->pRuleset = inst->pBindRuleset; ++ pThis->nRecords = 0; ++ pThis->pStrm = NULL; ++ pThis->masterLstn = NULL; /* we *are* a master! */ + finalize_it: + RETiRet; + } +@@ -466,6 +955,8 @@ CODESTARTnewInpInst + inst->pszFileName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(inppblk.descr[i].name, "statefile")) { + inst->pszStateFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ } else if(!strcmp(inppblk.descr[i].name, "removestateondelete")) { ++ inst->bRMStateOnDel = (uint8_t) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "tag")) { + inst->pszTag = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(inppblk.descr[i].name, "ruleset")) { +@@ -476,8 +967,19 @@ CODESTARTnewInpInst + inst->iFacility = pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "readmode")) { + inst->readMode = pvals[i].val.d.n; ++ } else if(!strcmp(inppblk.descr[i].name, "deletestateonfiledelete")) { ++ inst->bRMStateOnDel = (sbool) pvals[i].val.d.n; ++ } else if(!strcmp(inppblk.descr[i].name, "addmetadata")) { ++ inst->addMetadata = (sbool) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "maxlinesatonce")) { +- inst->maxLinesAtOnce = pvals[i].val.d.n; ++ if( loadModConf->opMode == OPMODE_INOTIFY ++ && pvals[i].val.d.n > 0) { ++ errmsg.LogError(0, RS_RET_PARAM_NOT_PERMITTED, ++ "parameter \"maxLinesAtOnce\" not " ++ "permited in inotify mode - ignored"); ++ } else { ++ inst->maxLinesAtOnce = pvals[i].val.d.n; ++ } + } else if(!strcmp(inppblk.descr[i].name, "persiststateinterval")) { + inst->iPersistStateInterval = pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "maxsubmitatonce")) { +@@ -487,6 +989,7 @@ CODESTARTnewInpInst + "param '%s'\n", inppblk.descr[i].name); + } + } ++ CHKiRet(checkInstance(inst)); + finalize_it: + CODE_STD_FINALIZERnewInpInst + cnfparamvalsDestruct(pvals, &inppblk); +@@ -497,6 +1000,7 @@ CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init our settings */ ++ loadModConf->opMode = OPMODE_POLLING; + loadModConf->iPollInterval = DFLT_PollInterval; + loadModConf->configSetViaV2Method = 0; + bLegacyCnfModGlobalsPermitted = 1; +@@ -518,6 +1022,7 @@ BEGINsetModCnf + struct cnfparamvals *pvals = NULL; + int i; + CODESTARTsetModCnf ++ loadModConf->opMode = OPMODE_POLLING; + pvals = nvlstGetParams(lst, &modpblk, NULL); + if(pvals == NULL) { + errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "imfile: error processing module " +@@ -535,6 +1040,17 @@ CODESTARTsetModCnf + continue; + if(!strcmp(modpblk.descr[i].name, "pollinginterval")) { + loadModConf->iPollInterval = (int) pvals[i].val.d.n; ++ } else if(!strcmp(modpblk.descr[i].name, "mode")) { ++ if(!es_strconstcmp(pvals[i].val.d.estr, "polling")) ++ loadModConf->opMode = OPMODE_POLLING; ++ else if(!es_strconstcmp(pvals[i].val.d.estr, "inotify")) ++ loadModConf->opMode = OPMODE_INOTIFY; ++ else { ++ char *cstr = es_str2cstr(pvals[i].val.d.estr, NULL); ++ errmsg.LogError(0, RS_RET_PARAM_ERROR, "imfile: unknown " ++ "mode '%s'", cstr); ++ free(cstr); ++ } + } else { + dbgprintf("imfile: program error, non-handled " + "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); +@@ -559,7 +1075,9 @@ CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system */ + loadModConf->iPollInterval = cs.iPollInterval; + } +- dbgprintf("imfile: polling interval is %d\n", loadModConf->iPollInterval); ++ dbgprintf("imfile: opmode is %d, polling interval is %d\n", ++ loadModConf->opMode, ++ loadModConf->iPollInterval); + + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ +@@ -591,11 +1109,15 @@ BEGINactivateCnf + instanceConf_t *inst; + CODESTARTactivateCnf + runModConf = pModConf; ++ runModConf->pRootLstn = NULL, ++ runModConf->pTailLstn = NULL; ++ + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(inst); + } ++ + /* if we could not set up any listeners, there is no point in running... */ +- if(iFilPtr == 0) { ++ if(runModConf->pRootLstn == 0) { + errmsg.LogError(0, NO_ERRCODE, "imfile: no file monitors could be started, " + "input not activated.\n"); + ABORT_FINALIZE(RS_RET_NO_RUN); +@@ -610,6 +1132,8 @@ CODESTARTfreeCnf + for(inst = pModConf->root ; inst != NULL ; ) { + free(inst->pszBindRuleset); + free(inst->pszFileName); ++ free(inst->pszDirName); ++ free(inst->pszFileBaseName); + free(inst->pszTag); + free(inst->pszStateFile); + del = inst; +@@ -619,30 +1143,7 @@ CODESTARTfreeCnf + ENDfreeCnf + + +- +-/* This function is the cancel cleanup handler. It is called when rsyslog decides the +- * module must be stopped, what most probably happens during shutdown of rsyslogd. When +- * this function is called, the runInput() function (below) is already terminated - somewhere +- * in the middle of what it was doing. The cancel cleanup handler below should take +- * care of any locked mutexes and such, things that really need to be cleaned up +- * before processing continues. In general, many plugins do not need to provide +- * any code at all here. +- * +- * IMPORTANT: the calling interface of this function can NOT be modified. It actually is +- * called by pthreads. The provided argument is currently not being used. +- */ +-static void +-inputModuleCleanup(void __attribute__((unused)) *arg) +-{ +- BEGINfunc +- ENDfunc +-} +- +- +-/* This function is called by the framework to gather the input. The module stays +- * most of its lifetime inside this function. It MUST NEVER exit this function. Doing +- * so would end module processing and rsyslog would NOT reschedule the module. If +- * you exit from this function, you violate the interface specification! ++/* Monitor files in traditional polling mode. + * + * We go through all files and remember if at least one had data. If so, we do + * another run (until no data was present in any file). Then we sleep for +@@ -659,33 +1160,655 @@ inputModuleCleanup(void __attribute__((unused)) *arg) + * On spamming the main queue: keep in mind that it will automatically rate-limit + * ourselfes if we begin to overrun it. So we really do not need to care here. + */ +-#pragma GCC diagnostic ignored "-Wempty-body" +-BEGINrunInput +- int i; ++static rsRetVal ++doPolling(void) ++{ + int bHadFileData; /* were there at least one file with data during this run? */ +-CODESTARTrunInput +- pthread_cleanup_push(inputModuleCleanup, NULL); ++ DEFiRet; + while(glbl.GetGlobalInputTermState() == 0) { + do { ++ lstn_t *pLstn; + bHadFileData = 0; +- for(i = 0 ; i < iFilPtr ; ++i) { ++ for(pLstn = runModConf->pRootLstn ; pLstn != NULL ; pLstn = pLstn->next) { + if(glbl.GetGlobalInputTermState() == 1) + break; /* terminate input! */ +- pollFile(&files[i], &bHadFileData); ++ pollFile(pLstn, &bHadFileData); + } +- } while(iFilPtr > 1 && bHadFileData == 1 && glbl.GetGlobalInputTermState() == 0); /* warning: do...while()! */ ++ } while(bHadFileData == 1 && glbl.GetGlobalInputTermState() == 0); ++ /* warning: do...while()! */ + +- /* Note: the additional 10ns wait is vitally important. It guards rsyslog against totally +- * hogging the CPU if the users selects a polling interval of 0 seconds. It doesn't hurt any +- * other valid scenario. So do not remove. -- rgerhards, 2008-02-14 ++ /* Note: the additional 10ns wait is vitally important. It guards rsyslog ++ * against totally hogging the CPU if the users selects a polling interval ++ * of 0 seconds. It doesn't hurt any other valid scenario. So do not remove. ++ * rgerhards, 2008-02-14 + */ + if(glbl.GetGlobalInputTermState() == 0) + srSleep(runModConf->iPollInterval, 10); + } +- DBGPRINTF("imfile: terminating upon request of rsyslog core\n"); + +- pthread_cleanup_pop(0); /* just for completeness, but never called... */ +- RETiRet; /* use it to make sure the housekeeping is done! */ ++ RETiRet; ++} ++ ++ ++#if HAVE_INOTIFY_INIT ++static rsRetVal ++fileTableInit(fileTable_t *const __restrict__ tab, const int nelem) ++{ ++ DEFiRet; ++ CHKmalloc(tab->listeners = malloc(sizeof(dirInfoFiles_t) * nelem)); ++ tab->allocMax = nelem; ++ tab->currMax = 0; ++finalize_it: ++ RETiRet; ++} ++/* uncomment if needed ++static void ++fileTableDisplay(fileTable_t *tab) ++{ ++ int f; ++ uchar *baseName; ++ DBGPRINTF("imfile: dirs.currMaxfiles %d\n", tab->currMax); ++ for(f = 0 ; f < tab->currMax ; ++f) { ++ baseName = tab->listeners[f].pLstn->pszBaseName; ++ DBGPRINTF("imfile: TABLE %p CONTENTS, %d->%p:'%s'\n", tab, f, tab->listeners[f].pLstn, (char*)baseName); ++ } ++} ++*/ ++ ++static int ++fileTableSearch(fileTable_t *const __restrict__ tab, uchar *const __restrict__ fn) ++{ ++ int f; ++ uchar *baseName = NULL; ++ /* UNCOMMENT FOR DEBUG fileTableDisplay(tab); */ ++ for(f = 0 ; f < tab->currMax ; ++f) { ++ baseName = tab->listeners[f].pLstn->pszBaseName; ++ if(!fnmatch((char*)baseName, (char*)fn, FNM_PATHNAME | FNM_PERIOD)) ++ break; /* found */ ++ } ++ if(f == tab->currMax) ++ f = -1; ++ DBGPRINTF("imfile: fileTableSearch file '%s' - '%s', found:%d\n", fn, baseName, f); ++ return f; ++} ++ ++static int ++fileTableSearchNoWildcard(fileTable_t *const __restrict__ tab, uchar *const __restrict__ fn) ++{ ++ int f; ++ uchar *baseName = NULL; ++ /* UNCOMMENT FOR DEBUG fileTableDisplay(tab); */ ++ for(f = 0 ; f < tab->currMax ; ++f) { ++ baseName = tab->listeners[f].pLstn->pszBaseName; ++ if (strcmp((const char*)baseName, (const char*)fn) == 0) ++ break; /* found */ ++ } ++ if(f == tab->currMax) ++ f = -1; ++ DBGPRINTF("imfile: fileTableSearchNoWildcard file '%s' - '%s', found:%d\n", fn, baseName, f); ++ return f; ++} ++ ++/* add file to file table */ ++static rsRetVal ++fileTableAddFile(fileTable_t *const __restrict__ tab, lstn_t *const __restrict__ pLstn) ++{ ++ int j; ++ DEFiRet; ++ /* UNCOMMENT FOR DEBUG fileTableDisplay(tab); */ ++ for(j = 0 ; j < tab->currMax && tab->listeners[j].pLstn != pLstn ; ++j) ++ ; /* just scan */ ++ if(j < tab->currMax) { ++ ++tab->listeners[j].refcnt; ++ DBGPRINTF("imfile: file '%s' already registered, refcnt now %d\n", ++ pLstn->pszFileName, tab->listeners[j].refcnt); ++ FINALIZE; ++ } ++ ++ if(tab->currMax == tab->allocMax) { ++ const int newMax = 2 * tab->allocMax; ++ dirInfoFiles_t *newListenerTab = realloc(tab->listeners, newMax * sizeof(dirInfoFiles_t)); ++ if(newListenerTab == NULL) { ++ errmsg.LogError(0, RS_RET_OUT_OF_MEMORY, ++ "cannot alloc memory to map directory/file relationship " ++ "for '%s' - ignoring", pLstn->pszFileName); ++ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); ++ } ++ tab->listeners = newListenerTab; ++ tab->allocMax = newMax; ++ DBGPRINTF("imfile: increased dir table to %d entries\n", allocMaxDirs); ++ } ++ ++ tab->listeners[tab->currMax].pLstn = pLstn; ++ tab->listeners[tab->currMax].refcnt = 1; ++ tab->currMax++; ++finalize_it: ++ RETiRet; ++} ++ ++/* delete a file from file table */ ++static rsRetVal ++fileTableDelFile(fileTable_t *const __restrict__ tab, lstn_t *const __restrict__ pLstn) ++{ ++ int j; ++ DEFiRet; ++ ++ for(j = 0 ; j < tab->currMax && tab->listeners[j].pLstn != pLstn ; ++j) ++ ; /* just scan */ ++ if(j == tab->currMax) { ++ DBGPRINTF("imfile: no association for file '%s'\n", pLstn->pszFileName); ++ FINALIZE; ++ } ++ tab->listeners[j].refcnt--; ++ if(tab->listeners[j].refcnt == 0) { ++ /* we remove that entry (but we never shrink the table) */ ++ if(j < tab->currMax - 1) { ++ /* entry in middle - need to move others */ ++ memmove(tab->listeners+j, tab->listeners+j+1, ++ (tab->currMax -j-1) * sizeof(dirInfoFiles_t)); ++ } ++ --tab->currMax; ++ } ++finalize_it: ++ RETiRet; ++} ++/* add entry to dirs array */ ++static rsRetVal ++dirsAdd(uchar *dirName) ++{ ++ int newMax; ++ dirInfo_t *newDirTab; ++ DEFiRet; ++ ++ if(currMaxDirs == allocMaxDirs) { ++ newMax = 2 * allocMaxDirs; ++ newDirTab = realloc(dirs, newMax * sizeof(dirInfo_t)); ++ if(newDirTab == NULL) { ++ errmsg.LogError(0, RS_RET_OUT_OF_MEMORY, ++ "cannot alloc memory to monitor directory '%s' - ignoring", ++ dirName); ++ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); ++ } ++ dirs = newDirTab; ++ allocMaxDirs = newMax; ++ DBGPRINTF("imfile: increased dir table to %d entries\n", allocMaxDirs); ++ } ++ ++ /* if we reach this point, there is space in the file table for the new entry */ ++ dirs[currMaxDirs].dirName = dirName; ++ CHKiRet(fileTableInit(&dirs[currMaxDirs].active, INIT_FILE_IN_DIR_TAB_SIZE)); ++ CHKiRet(fileTableInit(&dirs[currMaxDirs].configured, INIT_FILE_IN_DIR_TAB_SIZE)); ++ ++ ++currMaxDirs; ++ DBGPRINTF("imfile: added to dirs table: '%s'\n", dirName); ++finalize_it: ++ RETiRet; ++} ++ ++ ++/* checks if a dir name is already inside the dirs array. If so, returns ++ * its index. If not present, -1 is returned. ++ */ ++static int ++dirsFindDir(uchar *dir) ++{ ++ int i; ++ ++ for(i = 0 ; i < currMaxDirs && ustrcmp(dir, dirs[i].dirName) ; ++i) ++ ; /* just scan, all done in for() */ ++ if(i == currMaxDirs) ++ i = -1; ++ return i; ++} ++ ++static rsRetVal ++dirsInit(void) ++{ ++ instanceConf_t *inst; ++ DEFiRet; ++ ++ free(dirs); ++ CHKmalloc(dirs = malloc(sizeof(dirInfo_t) * INIT_FILE_TAB_SIZE)); ++ allocMaxDirs = INIT_FILE_TAB_SIZE; ++ currMaxDirs = 0; ++ ++ for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { ++ if(dirsFindDir(inst->pszDirName) == -1) ++ dirsAdd(inst->pszDirName); ++ } ++ ++finalize_it: ++ RETiRet; ++} ++ ++/* add file to directory (create association) ++ * fIdx is index into file table, all other information is pulled from that table. ++ * bActive is 1 if the file is to be added to active set, else zero ++ */ ++static rsRetVal ++dirsAddFile(lstn_t *__restrict__ pLstn, const int bActive) ++{ ++ int dirIdx; ++ dirInfo_t *dir; ++ DEFiRet; ++ ++ dirIdx = dirsFindDir(pLstn->pszDirName); ++ if(dirIdx == -1) { ++ errmsg.LogError(0, RS_RET_INTERNAL_ERROR, "imfile: could not find " ++ "directory '%s' in dirs array - ignoring", ++ pLstn->pszDirName); ++ FINALIZE; ++ } ++ ++ dir = dirs + dirIdx; ++ CHKiRet(fileTableAddFile((bActive ? &dir->active : &dir->configured), pLstn)); ++ DBGPRINTF("imfile: associated file [%s] to directory %d[%s], Active = %d\n", ++ pLstn->pszFileName, dirIdx, dir->dirName, bActive); ++ /* UNCOMMENT FOR DEBUG fileTableDisplay(bActive ? &dir->active : &dir->configured); */ ++finalize_it: ++ RETiRet; ++} ++ ++ ++static void ++in_setupDirWatch(const int dirIdx) ++{ ++ int wd; ++ wd = inotify_add_watch(ino_fd, (char*)dirs[dirIdx].dirName, IN_CREATE|IN_DELETE|IN_MOVED_FROM); ++ if(wd < 0) { ++ DBGPRINTF("imfile: could not create dir watch for '%s'\n", ++ dirs[dirIdx].dirName); ++ goto done; ++ } ++ wdmapAdd(wd, dirIdx, NULL); ++ DBGPRINTF("imfile: watch %d added for dir %s\n", wd, dirs[dirIdx].dirName); ++done: return; ++} ++ ++/* Setup a new file watch for a known active file. It must already have ++ * been entered into the correct tables. ++ * Note: we need to try to read this file, as it may already contain data this ++ * needs to be processed, and we won't get an event for that as notifications ++ * happen only for things after the watch has been activated. ++ * Note: newFileName is NULL for configured files, and non-NULL for dynamically ++ * detected files (e.g. wildcards!) ++ */ ++static void ++startLstnFile(lstn_t *const __restrict__ pLstn) ++{ ++ rsRetVal localRet; ++ const int wd = inotify_add_watch(ino_fd, (char*)pLstn->pszFileName, IN_MODIFY); ++ if(wd < 0) { ++ char errStr[512]; ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ DBGPRINTF("imfile: could not create file table entry for '%s' - " ++ "not processing it now: %s\n", ++ pLstn->pszFileName, errStr); ++ goto done; ++ } ++ if((localRet = wdmapAdd(wd, -1, pLstn)) != RS_RET_OK) { ++ DBGPRINTF("imfile: error %d adding file to wdmap, ignoring\n", localRet); ++ goto done; ++ } ++ DBGPRINTF("imfile: watch %d added for file %s\n", wd, pLstn->pszFileName); ++ dirsAddFile(pLstn, ACTIVE_FILE); ++ pollFile(pLstn, NULL); ++done: return; ++} ++ ++/* Setup a new file watch for dynamically discovered files (via wildcards). ++ * Note: we need to try to read this file, as it may already contain data this ++ * needs to be processed, and we won't get an event for that as notifications ++ * happen only for things after the watch has been activated. ++ */ ++static void ++in_setupFileWatchDynamic(lstn_t *pLstn, uchar *const __restrict__ newBaseName) ++{ ++ char fullfn[MAXFNAME]; ++ struct stat fileInfo; ++ snprintf(fullfn, MAXFNAME, "%s/%s", pLstn->pszDirName, newBaseName); ++ if(stat(fullfn, &fileInfo) != 0) { ++ char errStr[1024]; ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ dbgprintf("imfile: ignoring file '%s' cannot stat(): %s\n", ++ fullfn, errStr); ++ goto done; ++ } ++ ++ if(S_ISDIR(fileInfo.st_mode)) { ++ DBGPRINTF("imfile: ignoring directory '%s'\n", fullfn); ++ goto done; ++ } ++ ++ if(lstnDup(&pLstn, newBaseName) != RS_RET_OK) ++ goto done; ++ ++ startLstnFile(pLstn); ++done: return; ++} ++ ++/* Setup a new file watch for static (configured) files. ++ * Note: we need to try to read this file, as it may already contain data this ++ * needs to be processed, and we won't get an event for that as notifications ++ * happen only for things after the watch has been activated. ++ */ ++static void ++in_setupFileWatchStatic(lstn_t *const __restrict__ pLstn) ++{ ++ DBGPRINTF("imfile: adding file '%s' to configured table\n", ++ pLstn->pszFileName); ++ dirsAddFile(pLstn, CONFIGURED_FILE); ++ ++ if(pLstn->hasWildcard) { ++ DBGPRINTF("imfile: file '%s' has wildcard, doing initial " ++ "expansion\n", pLstn->pszFileName); ++ glob_t files; ++ const int ret = glob((char*)pLstn->pszFileName, ++ GLOB_MARK|GLOB_NOSORT|GLOB_BRACE, NULL, &files); ++ if(ret == 0) { ++ unsigned i; ++ for(i = 0 ; i < files.gl_pathc ; i++) { ++ uchar basen[MAXFNAME]; ++ uchar *const file = (uchar*)files.gl_pathv[i]; ++ if(file[strlen((char*)file)-1] == '/') ++ continue;/* we cannot process subdirs! */ ++ getBasename(basen, file); ++ in_setupFileWatchDynamic(pLstn, basen); ++ } ++ globfree(&files); ++ } ++ } else { ++ /* Duplicate static object as well, otherwise the configobject could be deleted later! */ ++ if(lstnDup(&pLstn, pLstn->pszBaseName) != RS_RET_OK) { ++ DBGPRINTF("imfile: in_setupFileWatchStatic failed to duplicate listener for '%s'\n", pLstn->pszFileName); ++ goto done; ++ } ++ startLstnFile(pLstn); ++ } ++done: return; ++} ++ ++/* setup our initial set of watches, based on user config */ ++static void ++in_setupInitialWatches() ++{ ++ int i; ++ for(i = 0 ; i < currMaxDirs ; ++i) { ++ in_setupDirWatch(i); ++ } ++ lstn_t *pLstn; ++ for(pLstn = runModConf->pRootLstn ; pLstn != NULL ; pLstn = pLstn->next) { ++ if(pLstn->masterLstn == NULL) { ++ /* we process only static (master) entries */ ++ in_setupFileWatchStatic(pLstn); ++ } ++ } ++} ++ ++static void ++in_dbg_showEv(struct inotify_event *ev) ++{ ++ if(ev->mask & IN_IGNORED) { ++ DBGPRINTF("INOTIFY event: watch was REMOVED\n"); ++ } else if(ev->mask & IN_MODIFY) { ++ DBGPRINTF("INOTIFY event: watch was MODIFID\n"); ++ } else if(ev->mask & IN_ACCESS) { ++ DBGPRINTF("INOTIFY event: watch IN_ACCESS\n"); ++ } else if(ev->mask & IN_ATTRIB) { ++ DBGPRINTF("INOTIFY event: watch IN_ATTRIB\n"); ++ } else if(ev->mask & IN_CLOSE_WRITE) { ++ DBGPRINTF("INOTIFY event: watch IN_CLOSE_WRITE\n"); ++ } else if(ev->mask & IN_CLOSE_NOWRITE) { ++ DBGPRINTF("INOTIFY event: watch IN_CLOSE_NOWRITE\n"); ++ } else if(ev->mask & IN_CREATE) { ++ DBGPRINTF("INOTIFY event: file was CREATED: %s\n", ev->name); ++ } else if(ev->mask & IN_DELETE) { ++ DBGPRINTF("INOTIFY event: watch IN_DELETE\n"); ++ } else if(ev->mask & IN_DELETE_SELF) { ++ DBGPRINTF("INOTIFY event: watch IN_DELETE_SELF\n"); ++ } else if(ev->mask & IN_MOVE_SELF) { ++ DBGPRINTF("INOTIFY event: watch IN_MOVE_SELF\n"); ++ } else if(ev->mask & IN_MOVED_FROM) { ++ DBGPRINTF("INOTIFY event: watch IN_MOVED_FROM\n"); ++ } else if(ev->mask & IN_MOVED_TO) { ++ DBGPRINTF("INOTIFY event: watch IN_MOVED_TO\n"); ++ } else if(ev->mask & IN_OPEN) { ++ DBGPRINTF("INOTIFY event: watch IN_OPEN\n"); ++ } else if(ev->mask & IN_ISDIR) { ++ DBGPRINTF("INOTIFY event: watch IN_ISDIR\n"); ++ } else { ++ DBGPRINTF("INOTIFY event: unknown mask code %8.8x\n", ev->mask); ++ } ++} ++ ++ ++/* inotify told us that a file's wd was closed. We now need to remove ++ * the file from our internal structures. Remember that a different inode ++ * with the same name may already be in processing. ++ */ ++static void ++in_removeFile(const int dirIdx, ++ lstn_t *const __restrict__ pLstn) ++{ ++ uchar statefile[MAXFNAME]; ++ uchar toDel[MAXFNAME]; ++ int bDoRMState; ++ int wd; ++ uchar *statefn; ++ DBGPRINTF("imfile: remove listener '%s', dirIdx %d\n", ++ pLstn->pszFileName, dirIdx); ++ if(pLstn->bRMStateOnDel) { ++ statefn = getStateFileName(pLstn, statefile, sizeof(statefile)); ++ snprintf((char*)toDel, sizeof(toDel), "%s/%s", ++ glbl.GetWorkDir(), (char*)statefn); ++ bDoRMState = 1; ++ } else { ++ bDoRMState = 0; ++ } ++ pollFile(pLstn, NULL); /* one final try to gather data */ ++ /* delete listener data */ ++ DBGPRINTF("imfile: DELETING listener data for '%s' - '%s'\n", pLstn->pszBaseName, pLstn->pszFileName); ++ lstnDel(pLstn); ++ fileTableDelFile(&dirs[dirIdx].active, pLstn); ++ if(bDoRMState) { ++ DBGPRINTF("imfile: unlinking '%s'\n", toDel); ++ if(unlink((char*)toDel) != 0) { ++ char errStr[1024]; ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ errmsg.LogError(0, RS_RET_ERR, "imfile: could not remove state " ++ "file \"%s\": %s", toDel, errStr); ++ } ++ } ++ wd = wdmapLookupListner(pLstn); ++ wdmapDel(wd); ++} ++ ++static void ++in_handleDirEventCREATE(struct inotify_event *ev, const int dirIdx) ++{ ++ lstn_t *pLstn; ++ int ftIdx; ++ ftIdx = fileTableSearch(&dirs[dirIdx].active, (uchar*)ev->name); ++ if(ftIdx >= 0) { ++ pLstn = dirs[dirIdx].active.listeners[ftIdx].pLstn; ++ } else { ++ dbgprintf("imfile: file '%s' not active in dir '%s'\n", ++ ev->name, dirs[dirIdx].dirName); ++ ftIdx = fileTableSearch(&dirs[dirIdx].configured, (uchar*)ev->name); ++ if(ftIdx == -1) { ++ dbgprintf("imfile: file '%s' not associated with dir '%s'\n", ++ ev->name, dirs[dirIdx].dirName); ++ goto done; ++ } ++ pLstn = dirs[dirIdx].configured.listeners[ftIdx].pLstn; ++ } ++ DBGPRINTF("imfile: file '%s' associated with dir '%s'\n", ev->name, dirs[dirIdx].dirName); ++ in_setupFileWatchDynamic(pLstn, (uchar*)ev->name); ++done: return; ++} ++ ++/* note: we need to care only for active files in the DELETE case. ++ * Two reasons: a) if this is a configured file, it should be active ++ * b) if not for some reason, there still is nothing we can do against ++ * it, and trying to process a *deleted* file really makes no sense ++ * (remeber we don't have it open, so it actually *is gone*). ++ */ ++static void ++in_handleDirEventDELETE(struct inotify_event *const ev, const int dirIdx) ++{ ++ const int ftIdx = fileTableSearch(&dirs[dirIdx].active, (uchar*)ev->name); ++ if(ftIdx == -1) { ++ dbgprintf("imfile: deleted file '%s' not active in dir '%s'\n", ++ ev->name, dirs[dirIdx].dirName); ++ goto done; ++ } ++ DBGPRINTF("imfile: imfile delete processing for '%s'\n", ++ dirs[dirIdx].active.listeners[ftIdx].pLstn->pszFileName); ++ in_removeFile(dirIdx, dirs[dirIdx].active.listeners[ftIdx].pLstn); ++done: return; ++} ++ ++static void ++in_handleDirEvent(struct inotify_event *const ev, const int dirIdx) ++{ ++ DBGPRINTF("imfile: handle dir event for %s\n", dirs[dirIdx].dirName); ++ if((ev->mask & IN_CREATE)) { ++ in_handleDirEventCREATE(ev, dirIdx); ++ } else if((ev->mask & IN_DELETE)) { ++ in_handleDirEventDELETE(ev, dirIdx); ++ } else { ++ DBGPRINTF("imfile: got non-expected inotify event:\n"); ++ in_dbg_showEv(ev); ++ } ++} ++ ++ ++static void ++in_handleFileEvent(struct inotify_event *ev, const wd_map_t *const etry) ++{ ++ if(ev->mask & IN_MODIFY) { ++ pollFile(etry->pLstn, NULL); ++ } else { ++ DBGPRINTF("imfile: got non-expected inotify event:\n"); ++ in_dbg_showEv(ev); ++ } ++} ++ ++static void ++in_processEvent(struct inotify_event *ev) ++{ ++ wd_map_t *etry; ++ lstn_t *pLstn; ++ int iRet; ++ int ftIdx; ++ int wd; ++ ++ DBGPRINTF("imfile: in_processEvent (wd=%d) event Mask='0x%.8X'\n", ev->wd, ev->mask); ++ if(ev->mask & IN_IGNORED) { ++ goto done; ++ } else if(ev->mask & IN_MOVED_FROM) { ++ /* Find wd entry and remove it */ ++ etry = wdmapLookup(ev->wd); ++ if(etry != NULL) { ++ ftIdx = fileTableSearchNoWildcard(&dirs[etry->dirIdx].active, (uchar*)ev->name); ++ DBGPRINTF("imfile: IN_MOVED_FROM Event (ftIdx=%d, name=%s)\n", ftIdx, ev->name); ++ if(ftIdx >= 0) { ++ /* Find listener and wd table index*/ ++ pLstn = dirs[etry->dirIdx].active.listeners[ftIdx].pLstn; ++ wd = wdmapLookupListner(pLstn); ++ ++ /* Remove file from inotify watch */ ++ iRet = inotify_rm_watch(ino_fd, wd); /* Note this will TRIGGER IN_IGNORED Event! */ ++ if (iRet != 0) { ++ DBGPRINTF("imfile: inotify_rm_watch error %d (ftIdx=%d, wd=%d, name=%s)\n", errno, ftIdx, wd, ev->name); ++ } else { ++ DBGPRINTF("imfile: inotify_rm_watch successfully removed file from watch (ftIdx=%d, wd=%d, name=%s)\n", ftIdx, wd, ev->name); ++ } ++ in_removeFile(etry->dirIdx, pLstn); ++ DBGPRINTF("imfile: IN_MOVED_FROM Event file removed file (wd=%d, name=%s)\n", wd, ev->name); ++ } ++ } ++ goto done; ++ } ++ etry = wdmapLookup(ev->wd); ++ if(etry == NULL) { ++ DBGPRINTF("imfile: could not lookup wd %d\n", ev->wd); ++ goto done; ++ } ++ if(etry->pLstn == NULL) { /* directory? */ ++ in_handleDirEvent(ev, etry->dirIdx); ++ } else { ++ in_handleFileEvent(ev, etry); ++ } ++done: return; ++} ++ ++/* Monitor files in inotify mode */ ++static rsRetVal ++do_inotify() ++{ ++ char iobuf[8192]; ++ struct inotify_event *ev; ++ int rd; ++ int currev; ++ DEFiRet; ++ ++ CHKiRet(wdmapInit()); ++ CHKiRet(dirsInit()); ++ ino_fd = inotify_init(); ++ if(ino_fd < 0) { ++ errmsg.LogError(1, RS_RET_INOTIFY_INIT_FAILED, "imfile: Init inotify instance failed "); ++ return RS_RET_INOTIFY_INIT_FAILED; ++ } ++ DBGPRINTF("imfile: inotify fd %d\n", ino_fd); ++ in_setupInitialWatches(); ++ ++ while(glbl.GetGlobalInputTermState() == 0) { ++ rd = read(ino_fd, iobuf, sizeof(iobuf)); ++ if(rd < 0 && Debug) { ++ char errStr[1024]; ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ DBGPRINTF("imfile: error during inotify: %s\n", errStr); ++ } ++ currev = 0; ++ while(currev < rd) { ++ ev = (struct inotify_event*) (iobuf+currev); ++ in_dbg_showEv(ev); ++ in_processEvent(ev); ++ currev += sizeof(struct inotify_event) + ev->len; ++ } ++ } ++ ++finalize_it: ++ close(ino_fd); ++ RETiRet; ++} ++ ++#else /* #if HAVE_INOTIFY_INIT */ ++static rsRetVal ++do_inotify() ++{ ++ errmsg.LogError(0, RS_RET_NOT_IMPLEMENTED, "imfile: mode set to inotify, but the " ++ "platform does not support inotify"); ++ return RS_RET_NOT_IMPLEMENTED; ++} ++#endif /* #if HAVE_INOTIFY_INIT */ ++ ++/* This function is called by the framework to gather the input. The module stays ++ * most of its lifetime inside this function. It MUST NEVER exit this function. Doing ++ * so would end module processing and rsyslog would NOT reschedule the module. If ++ * you exit from this function, you violate the interface specification! ++ */ ++BEGINrunInput ++CODESTARTrunInput ++ DBGPRINTF("imfile: working in %s mode\n", ++ (runModConf->opMode == OPMODE_POLLING) ? "polling" : "inotify"); ++ if(runModConf->opMode == OPMODE_POLLING) ++ iRet = doPolling(); ++ else ++ iRet = do_inotify(); ++ ++ DBGPRINTF("imfile: terminating upon request of rsyslog core\n"); + ENDrunInput + #pragma GCC diagnostic warning "-Wempty-body" + /* END no-touch zone * +@@ -718,25 +1841,26 @@ ENDwillRun + * rgerhards, 2008-02-13 + */ + static rsRetVal +-persistStrmState(fileInfo_t *pInfo) ++persistStrmState(lstn_t *pLstn) + { + DEFiRet; + strm_t *psSF = NULL; /* state file (stream) */ + size_t lenDir; ++ uchar statefile[MAXFNAME]; + +- ASSERT(pInfo != NULL); +- +- /* TODO: create a function persistObj in obj.c? */ ++ uchar *const statefn = getStateFileName(pLstn, statefile, sizeof(statefile)); ++ DBGPRINTF("imfile: persisting state for '%s' to file '%s'\n", ++ pLstn->pszFileName, statefn); + CHKiRet(strm.Construct(&psSF)); + lenDir = ustrlen(glbl.GetWorkDir()); + if(lenDir > 0) + CHKiRet(strm.SetDir(psSF, glbl.GetWorkDir(), lenDir)); + CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_WRITE_TRUNC)); + CHKiRet(strm.SetsType(psSF, STREAMTYPE_FILE_SINGLE)); +- CHKiRet(strm.SetFName(psSF, pInfo->pszStateFile, strlen((char*) pInfo->pszStateFile))); ++ CHKiRet(strm.SetFName(psSF, statefn, strlen((char*) statefn))); + CHKiRet(strm.ConstructFinalize(psSF)); + +- CHKiRet(strm.Serialize(pInfo->pStrm, psSF)); ++ CHKiRet(strm.Serialize(pLstn->pStrm, psSF)); + CHKiRet(strm.Flush(psSF)); + + CHKiRet(strm.Destruct(&psSF)); +@@ -749,7 +1873,7 @@ finalize_it: + errmsg.LogError(0, iRet, "imfile: could not persist state " + "file %s - data may be repeated on next " + "startup. Is WorkDirectory set?", +- pInfo->pszStateFile); ++ statefn); + } + + RETiRet; +@@ -760,23 +1884,10 @@ finalize_it: + * shall free any resources and prepare the module for unload. + */ + BEGINafterRun +- int i; + CODESTARTafterRun +- /* Close files and persist file state information. We do NOT abort on error iRet as that makes +- * matters worse (at least we can try persisting the others...). Please note that, under stress +- * conditions, it may happen that we are terminated before we actuall could open all streams. So +- * before we change anything, we need to make sure the stream was open. +- */ +- for(i = 0 ; i < iFilPtr ; ++i) { +- if(files[i].pStrm != NULL) { /* stream open? */ +- persistStrmState(&files[i]); +- strm.Destruct(&(files[i].pStrm)); +- } +- ratelimitDestruct(files[i].ratelimiter); +- free(files[i].multiSub.ppMsgs); +- free(files[i].pszFileName); +- free(files[i].pszTag); +- free(files[i].pszStateFile); ++ while(runModConf->pRootLstn != NULL) { ++ /* Note: lstnDel() reasociates root! */ ++ lstnDel(runModConf->pRootLstn); + } + + if(pInputName != NULL) +@@ -804,6 +1915,15 @@ CODESTARTmodExit + objRelease(errmsg, CORE_COMPONENT); + objRelease(prop, CORE_COMPONENT); + objRelease(ruleset, CORE_COMPONENT); ++#if HAVE_INOTIFY_INIT ++ /* we use these vars only in inotify mode */ ++ if(dirs != NULL) { ++ free(dirs->active.listeners); ++ free(dirs->configured.listeners); ++ free(dirs); ++ } ++ free(wdmap); ++#endif + ENDmodExit + + +@@ -840,7 +1960,6 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus + cs.iFacility = 128; /* local0 */ + cs.iSeverity = 5; /* notice, as of rfc 3164 */ + cs.readMode = 0; +- cs.pBindRuleset = NULL; + cs.maxLinesAtOnce = 10240; + + RETiRet; +diff --git a/runtime/msg.c b/runtime/msg.c +index 10ecf48..ae36907 100644 +--- a/runtime/msg.c ++++ b/runtime/msg.c +@@ -4043,6 +4043,31 @@ finalize_it: + RETiRet; + } + ++/* add Metadata to the message. This is stored in a special JSON ++ * container. Note that only string types are currently supported, ++ * what should pose absolutely no problem with the string-ish nature ++ * of rsyslog metadata. ++ * added 2015-01-09 rgerhards ++ */ ++rsRetVal ++msgAddMetadata(msg_t *const __restrict__ pMsg, ++ uchar *const __restrict__ metaname, ++ uchar *const __restrict__ metaval) ++{ ++ DEFiRet; ++ struct json_object *const json = json_object_new_object(); ++ CHKmalloc(json); ++ struct json_object *const jval = json_object_new_string((char*)metaval); ++ if(jval == NULL) { ++ json_object_put(json); ++ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); ++ } ++ json_object_object_add(json, metaname, jval); ++ iRet = msgAddJSON(pMsg, (uchar*)"!metadata", json); ++ finalize_it: ++ RETiRet; ++} ++ + static struct json_object * + jsonDeepCopy(struct json_object *src) + { +diff --git a/runtime/msg.h b/runtime/msg.h +index e7babdb..4692158 100644 +--- a/runtime/msg.h ++++ b/runtime/msg.h +@@ -187,6 +187,7 @@ rsRetVal msgGetCEEVar(msg_t *pThis, cstr_t *propName, var_t **ppVar); + es_str_t* msgGetCEEVarNew(msg_t *pMsg, char *name); + rsRetVal msgAddJSON(msg_t *pM, uchar *name, struct json_object *json); + rsRetVal getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed); ++rsRetVal msgAddMetadata(msg_t *msg, uchar *metaname, uchar *metaval); + rsRetVal MsgGetSeverity(msg_t *pThis, int *piSeverity); + rsRetVal MsgDeserialize(msg_t *pMsg, strm_t *pStrm); + +diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h +index e62ba86..2efc340 100644 +--- a/runtime/rsyslog.h ++++ b/runtime/rsyslog.h +@@ -219,6 +219,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth + 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_INOTIFY_INIT_FAILED = - 3009, /**< the initialization of an inotify instance 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 */ +@@ -460,6 +461,11 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth + RS_RET_CA_CERT_MISSING = -2329,/**< a CA cert is missing where one is required (e.g. TLS) */ + RS_RET_CERT_MISSING = -2330,/**< a cert is missing where one is required (e.g. TLS) */ + RS_RET_CERTKEY_MISSING = -2331,/**< a cert (private) key is missing where one is required (e.g. TLS) */ ++ RS_RET_STATEFILE_WRONG_FNAME = -2361,/**< state file is for wrong file */ ++ ++ /* up to 2419 reserved for 8.4.x */ ++ RS_RET_IMFILE_WILDCARD = -2420, /**< imfile file name contains wildcard, which may be problematic */ ++ RS_RET_FILE_ALREADY_IN_TABLE = -2431,/**< in imfile: table already contains to be added file */ + + /* RainerScript error messages (range 1000.. 1999) */ + RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ +diff --git a/runtime/srutils.c b/runtime/srutils.c +index 6a509b4..e6b45be 100644 +--- a/runtime/srutils.c ++++ b/runtime/srutils.c +@@ -642,11 +642,11 @@ containsGlobWildcard(char *str) + return 0; + } + /* From Linux Programmer's Guide: +- * "A string is a wildcard pattern if it contains one of the characters '?', '*' or '['" +- * "One can remove the special meaning of '?', '*' and '[' by preceding them by a backslash" ++ * "A string is a wildcard pattern if it contains one of the characters '?', '*', '{' or '['" ++ * "One can remove the special meaning of '?', '*', '{' and '[' by preceding them by a backslash" + */ + for(p = str; *p != '\0'; p++) { +- if((*p == '?' || *p == '*' || *p == '[') && ++ if((*p == '?' || *p == '*' || *p == '[' || *p == '{') && + (p == str || *(p-1) != '\\')) { + return 1; + } +-- +2.5.5 diff --git a/SOURCES/rsyslog-7.4.7-rhbz1312459-partial-msg-loss.patch b/SOURCES/rsyslog-7.4.7-rhbz1312459-partial-msg-loss.patch new file mode 100644 index 0000000..796197e --- /dev/null +++ b/SOURCES/rsyslog-7.4.7-rhbz1312459-partial-msg-loss.patch @@ -0,0 +1,60 @@ +From c7dc590f9c6d9cd92a83ad25ca4738ee6bda6c3b Mon Sep 17 00:00:00 2001 +From: Tomas Heinrich +Date: Sat, 5 Mar 2016 20:59:16 +0100 +Subject: [PATCH] Fix processing of partial messages over TCP + +Commit b0e8ce6c added a new variable to track processing of a single +message split into multiple packets. Several bits of code weren't +ported to the new variable and could cause mishandling. +Messages not terminated by a NL were discarded upon connection +termination. +--- + tcps_sess.c | 5 ++--- + tcps_sess.h | 1 - + 2 files changed, 2 insertions(+), 4 deletions(-) + +diff --git a/tcps_sess.c b/tcps_sess.c +index 5821e44..199bece 100644 +--- a/tcps_sess.c ++++ b/tcps_sess.c +@@ -69,7 +69,7 @@ static rsRetVal Close(tcps_sess_t *pThis); + /* Standard-Constructor */ + BEGINobjConstruct(tcps_sess) /* be sure to specify the object type also in END macro! */ + pThis->iMsg = 0; /* just make sure... */ +- pThis->bAtStrtOfFram = 1; /* indicate frame header expected */ ++ pThis->inputState = eAtStrtFram; /* indicate frame header expected */ + pThis->eFraming = TCP_FRAMING_OCTET_STUFFING; /* just make sure... */ + /* now allocate the message reception buffer */ + CHKmalloc(pThis->pMsg = (uchar*) MALLOC(sizeof(uchar) * iMaxLine + 1)); +@@ -265,7 +265,6 @@ defaultDoSubmitMessage(tcps_sess_t *pThis, struct syslogTime *stTime, time_t ttG + + finalize_it: + /* reset status variables */ +- pThis->bAtStrtOfFram = 1; + pThis->iMsg = 0; + + RETiRet; +@@ -293,7 +292,7 @@ PrepareClose(tcps_sess_t *pThis) + + ISOBJ_TYPE_assert(pThis, tcps_sess); + +- if(pThis->bAtStrtOfFram == 1) { ++ if(pThis->inputState == eAtStrtFram) { + /* this is how it should be. There is no unprocessed + * data left and such we have nothing to do. For simplicity + * reasons, we immediately return in that case. +diff --git a/tcps_sess.h b/tcps_sess.h +index 4506cf0..3e10acf 100644 +--- a/tcps_sess.h ++++ b/tcps_sess.h +@@ -35,7 +35,6 @@ struct tcps_sess_s { + tcpLstnPortList_t *pLstnInfo; /* pointer back to listener info */ + netstrm_t *pStrm; + int iMsg; /* index of next char to store in msg */ +- sbool bAtStrtOfFram; /* are we at the very beginning of a new frame? */ + sbool bSuppOctetFram; /**< copy from listener, to speed up access */ + enum { + eAtStrtFram, +-- +2.5.5 + diff --git a/SPECS/rsyslog.spec b/SPECS/rsyslog.spec index a9c0ca8..9a9b80d 100644 --- a/SPECS/rsyslog.spec +++ b/SPECS/rsyslog.spec @@ -14,7 +14,7 @@ Summary: Enhanced system logging and kernel message trapping daemon Name: rsyslog Version: 7.4.7 -Release: 12%{?dist} +Release: 16%{?dist} License: (GPLv3+ and ASL 2.0) Group: System Environment/Daemons URL: http://www.rsyslog.com/ @@ -49,6 +49,18 @@ Patch20: rsyslog-7.4.7-rhbz1101602-imjournal-zero-bytes.patch Patch21: rsyslog-7.4.7-rhbz1188503-imjournal-default-tag.patch Patch22: rsyslog-7.4.7-rhbz1184402-imuxsock-hostname.patch Patch23: rsyslog-7.4.7-bz1254511-ppc64le_bug.patch +Patch24: rsyslog-7.4.7-rhbz1224336-ruleset-crash.patch +Patch25: rsyslog-7.4.7-rhbz1216957-imjournal-ste-file.patch +Patch26: rsyslog-7.4.7-rhbz1214257-maxMessageSize.patch +Patch27: rsyslog-7.4.7-rhbz1282687-ruleset-parser-crash.patch +Patch28: rsyslog-7.4.7-rhbz1312459-partial-msg-loss.patch +Patch29: rsyslog-7.4.7-rhbz1223566-imrelp-multi-ruleset.patch +Patch30: rsyslog-7.4.7-rhbz1295798-shutdown-delay.patch +Patch31: rsyslog-7.4.7-rhbz1146237-mmutf8fix.patch +Patch32: rsyslog-7.4.7-rhbz1263853-startup-order.patch +Patch33: rsyslog-7.4.7-rhbz1245194-imjournal-ste-file.patch +Patch34: rsyslog-7.4.7-rhbz1303617-imfile-wildcards.patch +Patch35: rsyslog-7.4.7-rhbz1222746-json-race.patch BuildRequires: bison BuildRequires: flex @@ -293,6 +305,18 @@ of source ports. %patch21 -p1 %patch22 -p1 %patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 %build %ifarch sparc64 @@ -329,6 +353,7 @@ export HIREDIS_LIBS=-L%{_libdir} --enable-mmjsonparse \ --enable-mmnormalize \ --enable-mmsnmptrapd \ + --enable-mmutf8fix \ --enable-mysql \ %if %{want_hiredis} --enable-omhiredis \ @@ -435,6 +460,7 @@ done %{_libdir}/rsyslog/lmzlibw.so %{_libdir}/rsyslog/mmanon.so %{_libdir}/rsyslog/mmcount.so +%{_libdir}/rsyslog/mmutf8fix.so %{_libdir}/rsyslog/omjournal.so %{_libdir}/rsyslog/ommail.so %{_libdir}/rsyslog/omprog.so @@ -535,6 +561,38 @@ done %{_libdir}/rsyslog/omudpspoof.so %changelog +* Thu Jul 14 2016 Tomas Heinrich 7.4.7-16 +- add a patch to prevent races in libjson-c calls + resolves: rhbz#1222746 + +* Sun Jul 10 2016 Tomas Heinrich 7.4.7-15 +- add a patch to make state file handling in imjournal more robust + resolves: rhbz#1245194 +- add a patch to support wildcards in imfile + resolves: rhbz#1303617 + +* Fri May 20 2016 Tomas Heinrich 7.4.7-14 +- add a patch to prevent loss of partial messages + resolves: rhbz#1312459 +- add a patch to allow multiple rulesets in imrelp + resolves: rhbz#1223566 +- add a patch to fix a race condition during shutdown + resolves: rhbz#1295798 +- add a patch to backport the mmutf8fix plugin + resolves: rhbz#1146237 +- add a patch to order service startup after the network + resolves: rhbz#1263853 + +* Mon May 16 2016 Tomas Heinrich 7.4.7-13 +- add a patch to prevent crashes when using multiple rulesets + resolves: rhbz#1224336 +- add a patch to keep the imjournal state file updated + resolves: rhbz#1216957 +- add a patch to fix an undefined behavior caused by the maxMessageSize directive + resolves: rhbz#1214257 +- add a patch to prevent crashes when using rulesets with a parser + resolves: rhbz#1282687 + * Fri Aug 28 2015 Tomas Heinrich 7.4.7-12 - amend the patch for rhbz#1151041 resolves: rhbz#1257150