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 @@
++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
++<html><head>
++<meta http-equiv="Content-Language" content="en">
++<title>Fix invalid UTF-8 Sequences (mmutf8fix)</title></head>
++
++<body>
++<a href="rsyslog_conf_modules.html">back</a>
++
++<h1>Fix invalid UTF-8 Sequences (mmutf8fix)</h1>
++<p><b>Module Name:&nbsp;&nbsp;&nbsp; mmutf8fix</b></p>
++<p><b>Author: </b>Rainer Gerhards &lt;rgerhards@adiscon.com&gt;</p>
++<p><b>Available since</b>: 7.5.4</p>
++<p><b>Description</b>:</p>
++<p>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.
++<p>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.
++
++<p><b>Proper Usage</b>:</p>
++<p>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 <b>not</b> change any
++properties set, used or extracted before the modification is done.
++<p>One potential use case is to normalize all messages. This is done by simply calling
++mmutf8fix right in front of all other actions.
++<p>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
++<a href="multi_ruleset.html">ruleset</a> and call mmutf8fix as first action in this
++ruleset.
++
++<p><b>Module Configuration Parameters</b>:</p>
++<p>Currently none.
++<p>&nbsp;</p>
++<p><b>Action Confguration Parameters</b>:</p>
++<ul>
++<li><b>mode</b> - <b>utf-8</b>/controlcharacters<br>
++This sets the basic detection mode.
++<br>In <b>utf-8</b> mode (the default), proper
++UTF-8 encoding is checked and bytes which are not proper UTF-8 sequences
++are acted on. If a proper multi-byte start sequence byte is detected but
++any of the following bytes is invalid, the whole sequence is replaced by
++the replacement method. This mode is most useful with non-US-ASCII character
++sets, which validly includes multibyte sequences. Note that in this mode
++control characters are NOT being replaced, because they are valid UTF-8.
++<br>In <b>controlcharacters</b> mode, all bytes which do not represent a
++printable US-ASCII character (codes 32 to 126) are replaced. Note that this
++also mangles valid UTF-8 multi-byte sequences, as these are (deliberately) outside
++of that character range.
++<li><b>replacementChar</b> - default " " (space), a single character<br>
++This is the character that invalid sequences are replaced by. Currently, it
++MUST be a <b>printable</b> US-ASCII character.
++</ul>
++
++<p><b>Caveats/Known Bugs:</b>
++<ul>
++<li>overlong UTF-8 encodings are currently not detected in utf-8 mode.
++</ul>
++
++<p><b>Samples:</b></p>
++<p>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.
++<p><textarea rows="5" cols="60">module(load="mmutf8fix")
++action(type="omfile" file="/path/to/non-fixed.log")
++action(type="mmutf8fix")
++action(type="omfile" file="/path/to/fixed.log")
++</textarea>
++
++<p>In this sample, we fix only message originating from host 10.0.0.1.
++<p><textarea rows="5" cols="60">module(load="mmutf8fix")
++if $fromhost-ip == "10.0.0.1" then
++    action(type="mmutf8fix")
++# all other actions here...
++</textarea>
++
++<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>] [<a href="manual.html">manual 
++index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
++<p><font size="2">This documentation is part of the
++<a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
++Copyright &copy; 2013 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
++<a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL 
++version 3 or higher.</font></p>
++
++</body></html>
+diff --git a/doc/rsyslog_conf_modules.html b/doc/rsyslog_conf_modules.html
+index 18d6b8a..aacd44b 100644
+--- a/doc/rsyslog_conf_modules.html
++++ b/doc/rsyslog_conf_modules.html
+@@ -118,6 +118,7 @@ enabled structured log messages.
+ <li><a href="mmsnmptrapd.html">mmsnmptrapd</a> - uses information provided by snmptrapd inside
+ the tag to correct the original sender system and priority of messages. Implemented via
+ the output module interface.
++<li><a href="mmutf8fix.html">mmutf8fix</a> - used to fix invalid UTF-8 character sequences
+ </ul>
+ 
+ <a name="lm"></a><h2>String Generator Modules</h2>
+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 <stdio.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++#include <signal.h>
++#include <errno.h>
++#include <unistd.h>
++#include <stdint.h>
++#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 <rgerhards@adiscon.com>
+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 <prokhorov.max@outlook.com>
+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 <theinric@redhat.com>
+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 <theinric@redhat.com>
+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 <theinric@redhat.com>
+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 <theinric@redhat.com>
+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 <theinric@redhat.com>
+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 <rgerhards@adiscon.com>
+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 <theinric@redhat.com>
+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 <inttypes.h> header file. */
+ #undef HAVE_INTTYPES_H
+ 
+@@ -265,6 +268,9 @@ 
+ /* Define to 1 if you have the <sys/ioctl.h> header file. */
+ #undef HAVE_SYS_IOCTL_H
+ 
++/* Define to 1 if you have the <sys/inotify.h> header file. */
++#undef HAVE_SYS_INOTIFY_H
++
+ /* Define to 1 if you have the <sys/param.h> 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.</p>
+ 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).</p>
+-<p>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.</p>
+-<p>Multiple files may be monitored by specifying
+-$InputRunFileMonitor multiple times.
+-</p>
++
++<b>State Files</b><br>
++<p>Rsyslog must keep track of which parts of the monitored file
++are already processed. This is done in so-called &#8220;state files&#8221;.
++These files are always created in the rsyslog working directory
++(configurable via $WorkDirectory).</p>
++<p>To avoid problems with duplicate state files, rsyslog automatically
++generates state file names according to the following scheme:</p>
++<ul class="simple">
++<li>the string &#8220;imfile-state:&#8221; is added before the actual file name,
++which includes the full path</li>
++<li>the full name is prepended after that string, but all occurrences
++of &#8220;/&#8221; are replaced by &#8220;-&#8221; to facilitate handling of these files</li>
++</ul>
++<p>As a concrete example, consider file <code class="docutils literal"><span class="pre">/var/log/applog</span></code> is
++being monitored. The corresponding state file will be named
++<code class="docutils literal"><span class="pre">imfile-state:-var-log-applog</span></code>.</p>
++<p>Note that it is possible to set a fixed state file name via the
++deprecated &#8220;stateFile&#8221; parameter. It is suggested to avoid this, as
++the user must take care of name clashes. Most importantly, if
++&#8220;stateFile&#8221; is set for file monitors with wildcards, the <strong>same</strong>
++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 &#8220;*&#8221;
++and as such it will not cover more complex cases.</p>
++<p>Note that when $WorkDirectory is not set or
++set to a non-writable location, the state file <strong>will not be generated</strong>.
++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.</p>
++
++<b>Metadata</b><br>
++<p>The imfile module supports message metadata. It supports the following
++data items</p>
++<ul>
++<li><p class="first">filename</p>
++<p>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.</p>
++</li>
++</ul>
++<p>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
++<em>addMetadata</em> input() parameter, which always overrides the default.</p>
++</div>
++
+ 
+ <p><b>Configuration Directives</b>:</p>
+ <p><b>Module Directives</b></p>
+ <ul>
+-<li><span style="font-weight: bold;">PollingInterval
+-seconds</span><br>
+-This is a global setting. It specifies how often files are to be polled
+-for new data. The time specified is in seconds. The <span style="font-weight: bold;">default value</span> 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.<br>
++<li><b>mode</b> (requires v8.1.5+)<br>
++This specifies if imfile is shall run in inotify ("inotify", <b>default</b>)
++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.
++
++<li><b>PollingInterval</b><br>
++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 <b>default value</b>
++is 10 seconds.<br>
+ 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.</li>
+ 
+ <p><b>Action Directives</b></p>
+ <ul>
+-<li><strong>(required) File&nbsp;/path/to/file</strong><br>
+-The file being monitored. So far, this must be an absolute name (no
+-macros or templates)</li>
+-<li><span style="font-weight: bold;">(required) Tag
+-tag:</span><br>
++<li><b>File</b> (mandatory)<br>
++The file to be monitored. This must be an absolute name.</li>
++
++<li><b>StateFile</b><br>
++This is the name of this file&#8217;s state file. This parameter should
++usually <strong>not</strong> be used. Check the section on &#8220;State Files&#8221; above
++for more details.</li>
++
++<li><b>Tag</b> (mandatory)<br>
+ 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).</li>
+-<li><span style="font-weight: bold;">(required) StateFile
+-&lt;name-of-state-file&gt;</span><br>
+-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.</li>
+-<li><span style="font-weight: bold;">Facility
+-facility</span><br>
++(like 'tag="tag:"').</li>
++
++<li><b>Facility</b><br>
+ 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. <span style="font-weight: bold;">Default</span> &nbsp;is
+-"local0".<span style="font-weight: bold;"></span></li>
+-<li><span style="font-weight: bold;">Severity</span><br>
+-The
+-syslog severity to be assigned to lines read. Can be specified in
++"local0"). Textual form is suggested. <b>Default</b> is
++"local0".</li>
++
++<li><b>Severity</b><br>
++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. <span style="font-weight: bold;">Default</span>
++"info"). Textual form is suggested. <b>Default</b>
+ is "notice".</li>
++
+ <li><b>PersistStateInterval</b> [lines]</b><br>
+ Specifies how often the state file shall be written when processing the input
+ file. The <strong>default</strong> 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.
++
+ <li><b>ReadMode</b> [mode]</b><br>
+ This mode should defined when having multiline messages. The value can range from 0-2 and determines the multiline detection method.
+ <br>0 (<strong>default</strong>) - line based (Each line is a new message)
+ <br>1 - paragraph (There is a blank line between log messages)
+ <br>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)
++
+ <li><b>MaxLinesAtOnce</b> [number]</b>
+-<br>
+-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 <strong>default</strong> is 1024.
++<dd><p>This is a legacy setting that only is supported in <em>polling</em> mode.
++In <em>inotify</em> mode, it is fixed at 0 and all attempts to configure
++a different value will be ignored, but will generate an error
++message.</p>
++<p>Please note that future versions of imfile may not support this
++parameter at all. So it is suggested to not use it.</p>
++<p>In <em>polling</em> 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
++<em>polling</em> mode, the <strong>default</strong> is 10240.</p>
++</dd></dl>
++
+ <li><b>MaxSubmitAtOnce</b> [number]</b>
+ <br>
+ 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.
+ <li><b>Ruleset</b> &lt;ruleset&gt;
+ Binds the listener to a specific <a href="multi_ruleset.html">ruleset</a>.</li>
++
++<li><b>deleteStateOnFileDelete [on/off]</b>
++<dd><p><strong>Default: on</strong></p>
++<p>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 <strong>deleted</strong> file, which usually is not what you want.</p>
++<p>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&#8217;s probably not intended in most case. As a side-note,
++it is strongly suggested <em>not</em> 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.</p>
++<p>In general, this parameter should only by set if the users
++knows exactly why this is required.</p>
++</dd></dl></li>
++
++<li><b>addMetadata [on/off]</b>
++<dd><p><strong>Default: see intro section on Metadata</strong></p>
++<p>This is used to turn on or off the addition of metadata to the
++message object.</p>
++</dd></dl></li>
++
+ </ul>
++
+ <b>Caveats/Known Bugs:</b>
+-<p>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</p><p>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.
+-</p>
++<p>Currently none.
+ <p><b>Sample:</b></p>
+ <p>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"
+ 
+ 
+ <p><b>Legacy Configuration Directives</b>:</p>
++<p>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.
+ <ul>
+ <li><strong>$InputFileName&nbsp;/path/to/file</strong><br>
+ equivalent to: File </li>
+@@ -194,16 +271,6 @@ equivalent to: MaxLinesAtOnce
+ Available in 5.7.5+, 6.1.5+<br>
+ equivalent to: Ruleset </li>
+ </ul>
+-<b>Caveats/Known Bugs:</b>
+-<p>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</p><p>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.
+-</p>
+ <p><b>Sample:</b></p>
+ <p>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
+ [<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+ <p><font size="2">This documentation is part of the
+ <a href="http://www.rsyslog.com/">rsyslog</a> project.<br>
+-Copyright &copy; 2008 by <a href="http://www.gerhards.net/rainer">Rainer
++Copyright &copy; 2008-2014 by <a href="http://www.gerhards.net/rainer">Rainer
+ Gerhards</a> and <a href="http://www.adiscon.com/">Adiscon</a>.
+ Released under the GNU GPL version 3 or higher.</font></p>
+ </body></html>
+diff --git a/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 <errno.h>
+ #include <fcntl.h>
+ #include <pthread.h>		/* do NOT remove: will soon be done by the module generation macros */
++#include <sys/types.h>
++#include <unistd.h>
++#include <glob.h>
++#include <fnmatch.h>
++#ifdef HAVE_SYS_INOTIFY_H
++#include <sys/inotify.h>
++#endif
+ #ifdef HAVE_SYS_STAT_H
+ #	include <sys/stat.h>
+ #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 <theinric@redhat.com>
+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 <theinric@redhat.com> 7.4.7-16
+- add a patch to prevent races in libjson-c calls
+  resolves: rhbz#1222746
+
+* Sun Jul 10 2016 Tomas Heinrich <theinric@redhat.com> 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 <theinric@redhat.com> 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 <theinric@redhat.com> 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 <theinric@redhat.com> 7.4.7-12
 - amend the patch for rhbz#1151041
   resolves: rhbz#1257150