summaryrefslogtreecommitdiff
path: root/plugins/imfile
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-04-03 03:08:50 +0200
committerMichael Biebl <biebl@debian.org>2014-04-03 03:08:50 +0200
commit9374a46543e9c43c009f80def8c3b2506b0b377e (patch)
tree8853fd40ee8d55ff24304ff8a4421640f3493c58 /plugins/imfile
parent209e193f14ec562df5aad945f04cd88b227cc602 (diff)
downloadrsyslog-9374a46543e9c43c009f80def8c3b2506b0b377e.tar.gz
Imported Upstream version 8.2.0upstream/8.2.0
Diffstat (limited to 'plugins/imfile')
-rw-r--r--plugins/imfile/Makefile.in204
-rw-r--r--plugins/imfile/imfile.c642
2 files changed, 759 insertions, 87 deletions
diff --git a/plugins/imfile/Makefile.in b/plugins/imfile/Makefile.in
index 6ec5169..d3d3ffe 100644
--- a/plugins/imfile/Makefile.in
+++ b/plugins/imfile/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.3 from Makefile.am.
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
-# Foundation, Inc.
+# 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.
@@ -16,6 +15,51 @@
@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@
@@ -35,7 +79,8 @@ POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = plugins/imfile
-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+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 \
@@ -83,9 +128,22 @@ imfile_la_OBJECTS = $(am_imfile_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 =
imfile_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(imfile_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
@@ -98,22 +156,40 @@ LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(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_at = $(am__v_at_@AM_V@)
-am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
-am__v_at_0 = @
+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_GEN = $(am__v_GEN_@AM_V@)
-am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
-am__v_GEN_0 = @echo " GEN " $@;
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(imfile_la_SOURCES)
DIST_SOURCES = $(imfile_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)
@@ -173,8 +249,6 @@ 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@
@@ -347,9 +421,9 @@ $(top_srcdir)/configure: $(am__configure_deps)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)"
@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -357,6 +431,8 @@ install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
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)"; \
}
@@ -372,12 +448,15 @@ uninstall-pkglibLTLIBRARIES:
clean-pkglibLTLIBRARIES:
-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
- @list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @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}; \
+ }
+
imfile.la: $(imfile_la_OBJECTS) $(imfile_la_DEPENDENCIES) $(EXTRA_imfile_la_DEPENDENCIES)
$(AM_V_CCLD)$(imfile_la_LINK) -rpath $(pkglibdir) $(imfile_la_OBJECTS) $(imfile_la_LIBADD) $(LIBS)
@@ -426,26 +505,15 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+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`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -457,15 +525,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+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
@@ -474,6 +538,21 @@ 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
@@ -618,19 +697,20 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-pkglibLTLIBRARIES ctags 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 uninstall uninstall-am uninstall-pkglibLTLIBRARIES
+.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.
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index 96487ba..414d237 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -5,7 +5,7 @@
*
* Work originally begun on 2008-02-01 by Rainer Gerhards
*
- * Copyright 2008-2013 Adiscon GmbH.
+ * Copyright 2008-2014 Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -23,7 +23,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "config.h" /* this is for autotools and always must be the first include */
+#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
@@ -33,6 +33,10 @@
#include <pthread.h> /* do NOT remove: will soon be done by the module generation macros */
#include <sys/types.h>
#include <unistd.h>
+#include <fnmatch.h>
+#ifdef HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
@@ -73,13 +77,16 @@ static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config para
#define DFLT_PollInterval 10
#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 */
/* this structure is used in pure polling mode as well one of the support
* structures for inotify.
*/
typedef struct fileInfo_s {
uchar *pszFileName;
- uchar *pszBasename; /* our files basename part */
+ uchar *pszDirName;
+ uchar *pszBaseName;
uchar *pszTag;
size_t lenTag;
uchar *pszStateFile; /* file in which state between runs is to be stored */
@@ -133,11 +140,16 @@ struct instanceConf_s {
static rsRetVal persistStrmState(fileInfo_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;
+ uint8_t opMode;
sbool configSetViaV2Method;
};
static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
@@ -145,13 +157,59 @@ static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current lo
static int iFilPtr = 0; /* number of files to be monitored; pointer to next free spot during config */
static fileInfo_t *files = NULL;
-static int currMaxFiles;
+static int allocMaxFiles; /* max file table size currently allocated */
+
+#if HAVE_INOTIFY_INIT
+/* support for inotify mode */
+
+/* we need to track directories */
+struct dirInfoFiles_s { /* associated files */
+ int idx;
+ 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;
+
+struct dirInfo_s {
+ uchar *dirName;
+ dirInfoFiles_t *files; /* associated file entries */
+ int currMaxFiles;
+ int allocMaxFiles;
+};
+typedef struct dirInfo_s dirInfo_t;
+static dirInfo_t *dirs = NULL;
+static int allocMaxDirs;
+static int currMaxDirs;
+
+/* 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 descriptor
+ * 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 */
+ int fileIdx; /* -1, if this is a dir entry, otherwise index into files table */
+ int dirIdx; /* index into dirs table, undefined if fileIdx != -1 */
+};
+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 -------------------------------------------------- */
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,
@@ -182,6 +240,122 @@ 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].fileIdx, 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;
+}
+
+
+/* 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, int dirIdx, int fileIdx)
+{
+ 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);
+ FINALIZE;
+ }
+ ++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 */
+ dbgprintf("DDDD: imfile doing wdmap mmemmov(%d, %d, %d) for ADD\n", i,i+1,nWdmap-i);
+ memmove(wdmap + i, wdmap + i + 1, nWdmap - i);
+ }
+ wdmap[i].wd = wd;
+ wdmap[i].dirIdx = dirIdx;
+ wdmap[i].fileIdx = fileIdx;
+ ++nWdmap;
+ dbgprintf("DDDD: imfile: enter into wdmap[%d]: wd %d, dir %d, file %d\n",i,wd,dirIdx,fileIdx);
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+wdmapDel(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) */
+ dbgprintf("DDDD: imfile doing wdmap mmemmov(%d, %d, %d) for DEL\n", i,i+1,nWdmap-i-1);
+ memmove(wdmap + i, wdmap + i+1, nWdmap - i-1);
+ }
+ --nWdmap;
+ dbgprintf("DDDD: imfile: wd %d deleted, was idx %d\n", wd, i);
+
+finalize_it:
+ RETiRet;
+}
+
+#endif /* #if HAVE_INOTIFY_INIT */
+
+
/* enqueue the read file line as a message. The provided string is
* not freed - thuis must be done by the caller.
*/
@@ -295,8 +469,6 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
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
*/
@@ -311,7 +483,8 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
break;
CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode, pThis->escapeLF));
++nProcessed;
- *pbHadFileData = 1; /* this is just a flag, so set it and forget it */
+ if(pbHadFileData != NULL)
+ *pbHadFileData = 1; /* this is just a flag, so set it and forget it */
CHKiRet(enqLine(pThis, pCStr)); /* process line */
rsCStrDestruct(&pCStr); /* discard string (must be done by us!) */
if(pThis->iPersistStateInterval > 0 && pThis->nRecords++ >= pThis->iPersistStateInterval) {
@@ -486,8 +659,8 @@ addListner(instanceConf_t *inst)
fileInfo_t *newFileTab;
fileInfo_t *pThis;
- if(iFilPtr == currMaxFiles) {
- newMax = 2 * currMaxFiles;
+ if(iFilPtr == allocMaxFiles) {
+ newMax = 2 * allocMaxFiles;
newFileTab = realloc(files, newMax * sizeof(fileInfo_t));
if(newFileTab == NULL) {
errmsg.LogError(0, RS_RET_OUT_OF_MEMORY,
@@ -496,13 +669,15 @@ addListner(instanceConf_t *inst)
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
}
files = newFileTab;
- currMaxFiles = newMax;
- DBGPRINTF("imfile: increased file table to %d entries\n", currMaxFiles);
+ allocMaxFiles = newMax;
+ DBGPRINTF("imfile: increased file table to %d entries\n", allocMaxFiles);
}
/* if we reach this point, there is space in the file table for the new entry */
pThis = &files[iFilPtr];
pThis->pszFileName = (uchar*) strdup((char*) inst->pszFileName);
+ pThis->pszDirName = inst->pszDirName; /* use value from inst! */
+ pThis->pszBaseName = inst->pszFileBaseName; /* use value from inst! */
pThis->pszTag = (uchar*) strdup((char*) inst->pszTag);
pThis->lenTag = ustrlen(pThis->pszTag);
pThis->pszStateFile = (uchar*) strdup((char*) inst->pszStateFile);
@@ -588,6 +763,7 @@ CODESTARTbeginCnfLoad
loadModConf = pModConf;
pModConf->pConf = pConf;
/* init our settings */
+ loadModConf->opMode = OPMODE_POLLING;
loadModConf->iPollInterval = DFLT_PollInterval;
loadModConf->configSetViaV2Method = 0;
bLegacyCnfModGlobalsPermitted = 1;
@@ -609,6 +785,7 @@ BEGINsetModCnf
struct cnfparamvals *pvals = NULL;
int i;
CODESTARTsetModCnf
+ loadModConf->opMode = OPMODE_INOTIFY; /* new style config has different default! */
pvals = nvlstGetParams(lst, &modpblk, NULL);
if(pvals == NULL) {
errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "imfile: error processing module "
@@ -626,6 +803,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);
@@ -650,7 +838,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 */
@@ -684,7 +874,7 @@ CODESTARTactivateCnf
runModConf = pModConf;
free(files); /* clear any previous instance */
CHKmalloc(files = (fileInfo_t*) malloc(sizeof(fileInfo_t) * INIT_FILE_TAB_SIZE));
- currMaxFiles = INIT_FILE_TAB_SIZE;
+ allocMaxFiles = INIT_FILE_TAB_SIZE;
iFilPtr = 0;
for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
@@ -719,10 +909,7 @@ CODESTARTfreeCnf
ENDfreeCnf
-/* 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
@@ -739,10 +926,12 @@ ENDfreeCnf
* 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.
*/
-BEGINrunInput
+static rsRetVal
+doPolling(void)
+{
int i;
int bHadFileData; /* were there at least one file with data during this run? */
-CODESTARTrunInput
+ DEFiRet;
while(glbl.GetGlobalInputTermState() == 0) {
do {
bHadFileData = 0;
@@ -751,17 +940,420 @@ CODESTARTrunInput
break; /* terminate input! */
pollFile(&files[i], &bHadFileData);
}
- } while(iFilPtr > 1 && bHadFileData == 1 && glbl.GetGlobalInputTermState() == 0); /* warning: do...while()! */
+ } while(iFilPtr > 1 && 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");
+ RETiRet;
+}
+
+
+#if HAVE_INOTIFY_INIT
+/* 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);
+ }
+ 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;
+ CHKmalloc(dirs[currMaxDirs].files= malloc(sizeof(dirInfoFiles_t) * INIT_FILE_IN_DIR_TAB_SIZE));
+ dirs[currMaxDirs].allocMaxFiles = INIT_FILE_IN_DIR_TAB_SIZE;
+ dirs[currMaxDirs].currMaxFiles= 0;
+
+ ++currMaxDirs;
+finalize_it:
+ RETiRet;
+}
+
+/* checks if a file name is already inside the dirs array. Note that wildcards
+ * apply. Returns either the array index or -1 if not found.
+ * i is the index of the dir entry to search.
+ */
+static int
+dirsFindFile(int i, uchar *fn)
+{
+ int f;
+ uchar *baseName;
+
+ for(f = 0 ; f < dirs[i].currMaxFiles ; ++f) {
+ baseName = files[dirs[i].files[f].idx].pszBaseName;
+ if(!fnmatch((char*)fn, (char*)baseName, FNM_PATHNAME | FNM_PERIOD))
+ break; /* found */
+ }
+ if(f == dirs[i].currMaxFiles)
+ f = -1;
+ //dbgprintf("DDDD: dir '%s', file '%s', found:%d\n", dirs[i].dirName, fn, f);
+ return f;
+}
+
+/* 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;
+ //dbgprintf("DDDD: dir '%s', found:%d\n", dir, i);
+ 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)
+ * i is index into file table, all other information is pulled from that table.
+ */
+static rsRetVal
+dirsAddFile(int i)
+{
+ int dirIdx;
+ int j;
+ int newMax;
+ dirInfoFiles_t *newFileTab;
+ dirInfo_t *dir;
+ DEFiRet;
+
+ dirIdx = dirsFindDir(files[i].pszDirName);
+ if(dirIdx == -1) {
+ errmsg.LogError(0, RS_RET_INTERNAL_ERROR, "imfile: could not find "
+ "directory '%s' in dirs array - ignoring",
+ files[i].pszDirName);
+ FINALIZE;
+ }
+
+ dir = dirs + dirIdx;
+ for(j = 0 ; j < dir->currMaxFiles && dir->files[j].idx != i ; ++j)
+ ; /* just scan */
+ if(j < dir->currMaxFiles) {
+ /* this is not important enough to send an user error, as all will
+ * continue to work. */
+ ++dir->files[j].refcnt;
+ DBGPRINTF("imfile: file '%s' already registered in directory '%s', recnt now %d\n",
+ files[i].pszFileName, dir->dirName, dir->files[j].refcnt);
+ FINALIZE;
+ }
+
+ if(dir->currMaxFiles == dir->allocMaxFiles) {
+ newMax = 2 * allocMaxFiles;
+ newFileTab = realloc(dirs, newMax * sizeof(dirInfoFiles_t));
+ if(newFileTab == NULL) {
+ errmsg.LogError(0, RS_RET_OUT_OF_MEMORY,
+ "cannot alloc memory to map directory '%s' file relationship "
+ "'%s' - ignoring", files[i].pszFileName, dir->dirName);
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ dir->files = newFileTab;
+ dir->allocMaxFiles = newMax;
+ DBGPRINTF("imfile: increased dir table to %d entries\n", allocMaxDirs);
+ }
+
+ dir->files[dir->currMaxFiles].idx = i;
+ dir->files[dir->currMaxFiles].refcnt = 1;
+ dbgprintf("DDDD: associated file %d[%s] to directory %d[%s]\n",
+ i, files[i].pszFileName, dirIdx, dir->dirName);
+ ++dir->currMaxFiles;
+finalize_it:
+ RETiRet;
+}
+
+/* delete a file from directory (remove association)
+ * fIdx is index into file table, all other information is pulled from that table.
+ */
+static rsRetVal
+dirsDelFile(int fIdx)
+{
+ int dirIdx;
+ int j;
+ dirInfo_t *dir;
+ DEFiRet;
+
+ dirIdx = dirsFindDir(files[fIdx].pszDirName);
+ if(dirIdx == -1) {
+ DBGPRINTF("imfile: could not find directory '%s' in dirs array - ignoring",
+ files[fIdx].pszDirName);
+ FINALIZE;
+ }
+
+ dir = dirs + dirIdx;
+ for(j = 0 ; j < dir->currMaxFiles && dir->files[j].idx != fIdx ; ++j)
+ ; /* just scan */
+ if(j == dir->currMaxFiles) {
+ DBGPRINTF("imfile: no association for file '%s' in directory '%s' "
+ "found - ignoring\n", files[fIdx].pszFileName, dir->dirName);
+ FINALIZE;
+ }
+ dir->files[j].refcnt--;
+ if(dir->files[j].refcnt == 0) {
+ /* we remove that entry (but we never shrink the table) */
+ if(j < dir->currMaxFiles - 1) {
+ /* entry in middle - need to move others */
+ memmove(dir->files+j, dir->files+j+1,
+ (dir->currMaxFiles -j-1) * sizeof(dirInfoFiles_t));
+ }
+ --dir->currMaxFiles;
+ }
+ DBGPRINTF("imfile: removed association of file '%s' to directory '%s'\n",
+ files[fIdx].pszFileName, dir->dirName);
+
+finalize_it:
+ RETiRet;
+}
+
+static void
+in_setupDirWatch(int i)
+{
+ int wd;
+ wd = inotify_add_watch(ino_fd, (char*)dirs[i].dirName, IN_CREATE);
+ if(wd < 0) {
+ DBGPRINTF("imfile: could not create dir watch for '%s'\n",
+ files[i].pszFileName);
+ goto done;
+ }
+ wdmapAdd(wd, i, -1);
+ dbgprintf("DDDD: watch %d added for dir %s\n", wd, dirs[i].dirName);
+done: return;
+}
+
+/* Setup a new file watch.
+ * 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_setupFileWatch(int i)
+{
+ int wd;
+ wd = inotify_add_watch(ino_fd, (char*)files[i].pszFileName, IN_MODIFY);
+ if(wd < 0) {
+ DBGPRINTF("imfile: could not create initial file for '%s'\n",
+ files[i].pszFileName);
+ goto done;
+ }
+ wdmapAdd(wd, -1, i);
+ dbgprintf("DDDD: watch %d added for file %s\n", wd, files[i].pszFileName);
+ dirsAddFile(i);
+ pollFile(&files[i], NULL);
+done: return;
+}
+
+/* setup our initial set of watches, based on user config */
+static rsRetVal
+in_setupInitialWatches()
+{
+ int i;
+ DEFiRet;
+
+ for(i = 0 ; i < currMaxDirs ; ++i) {
+ in_setupDirWatch(i);
+ }
+ for(i = 0 ; i < iFilPtr ; ++i) {
+ in_setupFileWatch(i);
+ }
+ RETiRet;
+}
+
+static void
+in_dbg_showEv(struct inotify_event *ev)
+{
+ if(ev->mask & IN_IGNORED) {
+ dbgprintf("watch was REMOVED\n");
+ } else if(ev->mask & IN_MODIFY) {
+ dbgprintf("watch was MODIFID\n");
+ } else if(ev->mask & IN_ACCESS) {
+ dbgprintf("watch IN_ACCESS\n");
+ } else if(ev->mask & IN_ATTRIB) {
+ dbgprintf("watch IN_ATTRIB\n");
+ } else if(ev->mask & IN_CLOSE_WRITE) {
+ dbgprintf("watch IN_CLOSE_WRITE\n");
+ } else if(ev->mask & IN_CLOSE_NOWRITE) {
+ dbgprintf("watch IN_CLOSE_NOWRITE\n");
+ } else if(ev->mask & IN_CREATE) {
+ dbgprintf("file was CREATED: %s\n", ev->name);
+ } else if(ev->mask & IN_DELETE) {
+ dbgprintf("watch IN_DELETE\n");
+ } else if(ev->mask & IN_DELETE_SELF) {
+ dbgprintf("watch IN_DELETE_SELF\n");
+ } else if(ev->mask & IN_MOVE_SELF) {
+ dbgprintf("watch IN_MOVE_SELF\n");
+ } else if(ev->mask & IN_MOVED_FROM) {
+ dbgprintf("watch IN_MOVED_FROM\n");
+ } else if(ev->mask & IN_MOVED_TO) {
+ dbgprintf("watch IN_MOVED_TO\n");
+ } else if(ev->mask & IN_OPEN) {
+ dbgprintf("watch IN_OPEN\n");
+ } else if(ev->mask & IN_ISDIR) {
+ dbgprintf("watch IN_ISDIR\n");
+ } else {
+ dbgprintf("unknown mask code %8.8x\n", ev->mask);
+ }
+}
+
+static void
+in_handleDirEvent(struct inotify_event *ev, int dirIdx)
+{
+ int fileIdx;
+ dbgprintf("DDDD: handle dir event for %s\n", dirs[dirIdx].dirName);
+ if(!(ev->mask & IN_CREATE)) {
+ DBGPRINTF("imfile: got non-expected inotify event:\n");
+ in_dbg_showEv(ev);
+ goto done;
+ }
+ fileIdx = dirsFindFile(dirIdx, (uchar*)ev->name);
+ if(fileIdx == -1) {
+ dbgprintf("imfile: file '%s' not associated with dir '%s'\n",
+ ev->name, dirs[dirIdx].dirName);
+ goto done;
+ }
+ dbgprintf("DDDD: file '%s' associated with dir '%s'\n", ev->name, dirs[dirIdx].dirName);
+ in_setupFileWatch(fileIdx);
+done: return;
+}
+
+/* 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(struct inotify_event *ev, int fIdx)
+{
+ wdmapDel(ev->wd);
+ dirsDelFile(fIdx);
+}
+
+
+static void
+in_handleFileEvent(struct inotify_event *ev, int fIdx)
+{
+ if(ev->mask & IN_MODIFY) {
+ pollFile(&files[fIdx], NULL);
+ } else if(ev->mask & IN_IGNORED) {
+ in_removeFile(ev, fIdx);
+ } 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;
+
+ etry = wdmapLookup(ev->wd);
+ if(etry == NULL) {
+ DBGPRINTF("imfile: could not lookup wd %d\n", ev->wd);
+ goto done;
+ }
+ dbgprintf("DDDD: imfile: wd %d got file %d, dir %d\n", ev->wd, etry->fileIdx, etry->dirIdx);
+ if(etry->fileIdx == -1) { /* directory? */
+ in_handleDirEvent(ev, etry->dirIdx);
+ } else {
+ in_handleFileEvent(ev, etry->fileIdx);
+ }
+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();
+ DBGPRINTF("imfile: inotify fd %d\n", ino_fd);
+ CHKiRet(in_setupInitialWatches());
+
+ while(glbl.GetGlobalInputTermState() == 0) {
+ rd = read(ino_fd, iobuf, sizeof(iobuf));
+ if(rd < 0) {
+ perror("inotify read"); exit(1);
+ }
+ currev = 0;
+ while(currev < rd) {
+ ev = (struct inotify_event*) (iobuf+currev);
+ dbgprintf("DDDD: imfile event notification: rd %d[%d], wd (%d, mask "
+ "%8.8x, cookie %4.4x, len %d)\n",
+ (int) rd, currev, ev->wd, ev->mask, ev->cookie, ev->len);
+ in_dbg_showEv(ev);
+ in_processEvent(ev);
+ currev += sizeof(struct inotify_event) + ev->len;
+ }
+ }
+
+finalize_it:
+ close(ino_fd);
+ RETiRet;
+}
+
+#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");
RETiRet; /* use it to make sure the housekeeping is done! */
ENDrunInput