diff options
Diffstat (limited to 'tools')
39 files changed, 13424 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..f5044c5 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,118 @@ +sbin_PROGRAMS = +bin_PROGRAMS = +CLEANFILES = +man1_MANS = +man_MANS = rsyslogd.8 rsyslog.conf.5 + +sbin_PROGRAMS += rsyslogd +rsyslogd_SOURCES = \ + syslogd.c \ + rsyslogd.c \ + syslogd.h \ + omshell.c \ + omshell.h \ + omusrmsg.c \ + omusrmsg.h \ + omfwd.c \ + omfwd.h \ + omfile.c \ + omfile.h \ + ompipe.c \ + ompipe.h \ + omdiscard.c \ + omdiscard.h \ + pmrfc5424.c \ + pmrfc5424.h \ + pmrfc3164.c \ + pmrfc3164.h \ + smtradfile.c \ + smtradfile.h \ + smfile.c \ + smfile.h \ + smfwd.c \ + smfwd.h \ + smtradfwd.c \ + smtradfwd.h \ + iminternal.c \ + iminternal.h \ + \ + ../dirty.h +rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) + +if ENABLE_LIBLOGGING_STDLOG +rsyslogd_CPPFLAGS += $(LIBLOGGING_STDLOG_CFLAGS) +endif + +rsyslogd_CPPFLAGS += -DSD_EXPORT_SYMBOLS + +# note: it looks like librsyslog.la must be explicitely given on LDDADD, +# otherwise dependencies are not properly calculated (resulting in a +# potentially incomplete build, a problem we had several times...) +rsyslogd_LDADD = ../grammar/libgrammar.la ../runtime/librsyslog.la ../compat/compat.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBUUID_LIBS) $(HASH_XXHASH_LIBS) + +# Note: do NOT indent the if chain - it will not work! +if OS_LINUX +rsyslogd_LDFLAGS = -export-dynamic \ + #-Wl,--whole-archive,$(top_builddir)/runtime/.libs/librsyslog.a,--no-whole-archive +exports_list_file = +else +if OS_APPLE +rsyslogd_LDFLAGS = -export-dynamic \ + -Wl,$(top_builddir)/runtime/.libs/librsyslog.a +exports_list_file = +else +if OS_AIX +rsyslogd_LDFLAGS = -brtl -bexpall -f"aix_exports_list" -lsrc +exports_list_file = aix_exports_list +else # e.g. FreeBSD +rsyslogd_LDFLAGS = -export-dynamic +exports_list_file = +endif # if OS_AIX +endif # if OS_APPLE +endif # if OS_LINUX + +EXTRA_DIST = $(man_MANS) \ + rscryutil.rst \ + recover_qi.pl + +EXTRA_rsyslogd_DEPENDENCIES = $(exports_list_file) + +if ENABLE_LIBLOGGING_STDLOG +rsyslogd_LDADD += $(LIBLOGGING_STDLOG_LIBS) +endif + +if ENABLE_DIAGTOOLS +sbin_PROGRAMS += rsyslog_diag_hostname msggen +rsyslog_diag_hostname_SOURCES = gethostn.c +msggen_SOURCES = msggen.c +endif + +if ENABLE_USERTOOLS +if ENABLE_OMMONGODB +bin_PROGRAMS += logctl +logctl_SOURCES = logctl.c +logctl_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBMONGOC_CFLAGS) +logctl_LDADD = $(LIBMONGOC_LIBS) +endif +if ENABLE_LIBGCRYPT +bin_PROGRAMS += rscryutil +rscryutil = rscryutil.c +rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) +rscryutil_LDADD = ../runtime/libgcry.la $(LIBGCRYPT_LIBS) +rscryutil_LDFLAGS = \ + -Wl,--whole-archive,--no-whole-archive +if ENABLE_GENERATE_MAN_PAGES +RSTMANFILE = rscryutil.rst +rscryutil.1: $(RSTMANFILE) + $(AM_V_GEN) $(RST2MAN) $(RSTMANFILE) $@ +man1_MANS += rscryutil.1 +CLEANFILES += rscryutil.1 +EXTRA_DIST+= rscryutil.1 +endif +endif +endif + +aix_exports_list: + echo "$(top_builddir)/runtime/.libs/librsyslog_la-*.o" > $@ + echo "$(top_builddir)/.libs/librsyslog_la-*.o" >> $@ + echo "$(top_builddir)/grammar/.libs/libgrammar_la-*.o" >> $@ diff --git a/tools/Makefile.in b/tools/Makefile.in new file mode 100644 index 0000000..01a8bb9 --- /dev/null +++ b/tools/Makefile.in @@ -0,0 +1,1447 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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 = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +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@ +sbin_PROGRAMS = rsyslogd$(EXEEXT) $(am__EXEEXT_3) +bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) +@ENABLE_LIBLOGGING_STDLOG_TRUE@am__append_1 = $(LIBLOGGING_STDLOG_CFLAGS) +@ENABLE_LIBLOGGING_STDLOG_TRUE@am__append_2 = $(LIBLOGGING_STDLOG_LIBS) +@ENABLE_DIAGTOOLS_TRUE@am__append_3 = rsyslog_diag_hostname msggen +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@am__append_4 = logctl +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@am__append_5 = rscryutil +@ENABLE_GENERATE_MAN_PAGES_TRUE@@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@am__append_6 = rscryutil.1 +@ENABLE_GENERATE_MAN_PAGES_TRUE@@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@am__append_7 = rscryutil.1 +@ENABLE_GENERATE_MAN_PAGES_TRUE@@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@am__append_8 = rscryutil.1 +subdir = tools +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_define.m4 \ + $(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) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@am__EXEEXT_1 = \ +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@ logctl$(EXEEXT) +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@am__EXEEXT_2 = rscryutil$(EXEEXT) +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ + "$(DESTDIR)$(man8dir)" +@ENABLE_DIAGTOOLS_TRUE@am__EXEEXT_3 = rsyslog_diag_hostname$(EXEEXT) \ +@ENABLE_DIAGTOOLS_TRUE@ msggen$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) +am__logctl_SOURCES_DIST = logctl.c +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@am_logctl_OBJECTS = logctl-logctl.$(OBJEXT) +logctl_OBJECTS = $(am_logctl_OBJECTS) +am__DEPENDENCIES_1 = +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@logctl_DEPENDENCIES = $(am__DEPENDENCIES_1) +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 = +am__msggen_SOURCES_DIST = msggen.c +@ENABLE_DIAGTOOLS_TRUE@am_msggen_OBJECTS = msggen.$(OBJEXT) +msggen_OBJECTS = $(am_msggen_OBJECTS) +msggen_LDADD = $(LDADD) +rscryutil_SOURCES = rscryutil.c +rscryutil_OBJECTS = rscryutil-rscryutil.$(OBJEXT) +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@rscryutil_DEPENDENCIES = ../runtime/libgcry.la \ +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@ $(am__DEPENDENCIES_1) +rscryutil_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(rscryutil_LDFLAGS) $(LDFLAGS) -o $@ +am__rsyslog_diag_hostname_SOURCES_DIST = gethostn.c +@ENABLE_DIAGTOOLS_TRUE@am_rsyslog_diag_hostname_OBJECTS = \ +@ENABLE_DIAGTOOLS_TRUE@ gethostn.$(OBJEXT) +rsyslog_diag_hostname_OBJECTS = $(am_rsyslog_diag_hostname_OBJECTS) +rsyslog_diag_hostname_LDADD = $(LDADD) +am_rsyslogd_OBJECTS = rsyslogd-syslogd.$(OBJEXT) \ + rsyslogd-rsyslogd.$(OBJEXT) rsyslogd-omshell.$(OBJEXT) \ + rsyslogd-omusrmsg.$(OBJEXT) rsyslogd-omfwd.$(OBJEXT) \ + rsyslogd-omfile.$(OBJEXT) rsyslogd-ompipe.$(OBJEXT) \ + rsyslogd-omdiscard.$(OBJEXT) rsyslogd-pmrfc5424.$(OBJEXT) \ + rsyslogd-pmrfc3164.$(OBJEXT) rsyslogd-smtradfile.$(OBJEXT) \ + rsyslogd-smfile.$(OBJEXT) rsyslogd-smfwd.$(OBJEXT) \ + rsyslogd-smtradfwd.$(OBJEXT) rsyslogd-iminternal.$(OBJEXT) +rsyslogd_OBJECTS = $(am_rsyslogd_OBJECTS) +@ENABLE_LIBLOGGING_STDLOG_TRUE@am__DEPENDENCIES_2 = \ +@ENABLE_LIBLOGGING_STDLOG_TRUE@ $(am__DEPENDENCIES_1) +rsyslogd_DEPENDENCIES = ../grammar/libgrammar.la \ + ../runtime/librsyslog.la ../compat/compat.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) +rsyslogd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(rsyslogd_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__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/gethostn.Po \ + ./$(DEPDIR)/logctl-logctl.Po ./$(DEPDIR)/msggen.Po \ + ./$(DEPDIR)/rscryutil-rscryutil.Po \ + ./$(DEPDIR)/rsyslogd-iminternal.Po \ + ./$(DEPDIR)/rsyslogd-omdiscard.Po \ + ./$(DEPDIR)/rsyslogd-omfile.Po ./$(DEPDIR)/rsyslogd-omfwd.Po \ + ./$(DEPDIR)/rsyslogd-ompipe.Po ./$(DEPDIR)/rsyslogd-omshell.Po \ + ./$(DEPDIR)/rsyslogd-omusrmsg.Po \ + ./$(DEPDIR)/rsyslogd-pmrfc3164.Po \ + ./$(DEPDIR)/rsyslogd-pmrfc5424.Po \ + ./$(DEPDIR)/rsyslogd-rsyslogd.Po \ + ./$(DEPDIR)/rsyslogd-smfile.Po ./$(DEPDIR)/rsyslogd-smfwd.Po \ + ./$(DEPDIR)/rsyslogd-smtradfile.Po \ + ./$(DEPDIR)/rsyslogd-smtradfwd.Po \ + ./$(DEPDIR)/rsyslogd-syslogd.Po +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 = $(logctl_SOURCES) $(msggen_SOURCES) rscryutil.c \ + $(rsyslog_diag_hostname_SOURCES) $(rsyslogd_SOURCES) +DIST_SOURCES = $(am__logctl_SOURCES_DIST) $(am__msggen_SOURCES_DIST) \ + rscryutil.c $(am__rsyslog_diag_hostname_SOURCES_DIST) \ + $(rsyslogd_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +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; }; \ + } +man1dir = $(mandir)/man1 +man5dir = $(mandir)/man5 +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man1_MANS) $(man_MANS) +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 +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APU_CFLAGS = @APU_CFLAGS@ +APU_LIBS = @APU_LIBS@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CIVETWEB_LIBS = @CIVETWEB_LIBS@ +CONF_FILE_PATH = @CONF_FILE_PATH@ +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@ +FAUP_LIBS = @FAUP_LIBS@ +FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ +GNUTLS_LIBS = @GNUTLS_LIBS@ +GREP = @GREP@ +GSS_LIBS = @GSS_LIBS@ +GT_KSI_LS12_CFLAGS = @GT_KSI_LS12_CFLAGS@ +GT_KSI_LS12_LIBS = @GT_KSI_LS12_LIBS@ +HASH_XXHASH_LIBS = @HASH_XXHASH_LIBS@ +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@ +IP = @IP@ +JAVA = @JAVA@ +JAVAC = @JAVAC@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBCAPNG_CFLAGS = @LIBCAPNG_CFLAGS@ +LIBCAPNG_LIBS = @LIBCAPNG_LIBS@ +LIBCAPNG_PRESENT_CFLAGS = @LIBCAPNG_PRESENT_CFLAGS@ +LIBCAPNG_PRESENT_LIBS = @LIBCAPNG_PRESENT_LIBS@ +LIBDBI_CFLAGS = @LIBDBI_CFLAGS@ +LIBDBI_LIBS = @LIBDBI_LIBS@ +LIBESTR_CFLAGS = @LIBESTR_CFLAGS@ +LIBESTR_LIBS = @LIBESTR_LIBS@ +LIBEVENT_CFLAGS = @LIBEVENT_CFLAGS@ +LIBEVENT_LIBS = @LIBEVENT_LIBS@ +LIBFASTJSON_CFLAGS = @LIBFASTJSON_CFLAGS@ +LIBFASTJSON_LIBS = @LIBFASTJSON_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBLOGGING_CFLAGS = @LIBLOGGING_CFLAGS@ +LIBLOGGING_LIBS = @LIBLOGGING_LIBS@ +LIBLOGGING_STDLOG_CFLAGS = @LIBLOGGING_STDLOG_CFLAGS@ +LIBLOGGING_STDLOG_LIBS = @LIBLOGGING_STDLOG_LIBS@ +LIBLOGNORM_CFLAGS = @LIBLOGNORM_CFLAGS@ +LIBLOGNORM_LIBS = @LIBLOGNORM_LIBS@ +LIBLZ4_CFLAGS = @LIBLZ4_CFLAGS@ +LIBLZ4_LIBS = @LIBLZ4_LIBS@ +LIBM = @LIBM@ +LIBMONGOC_CFLAGS = @LIBMONGOC_CFLAGS@ +LIBMONGOC_LIBS = @LIBMONGOC_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBRDKAFKA_CFLAGS = @LIBRDKAFKA_CFLAGS@ +LIBRDKAFKA_LIBS = @LIBRDKAFKA_LIBS@ +LIBS = @LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_JOURNAL_CFLAGS = @LIBSYSTEMD_JOURNAL_CFLAGS@ +LIBSYSTEMD_JOURNAL_LIBS = @LIBSYSTEMD_JOURNAL_LIBS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUUID_CFLAGS = @LIBUUID_CFLAGS@ +LIBUUID_LIBS = @LIBUUID_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_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@ +PG_CONFIG = @PG_CONFIG@ +PID_FILE_PATH = @PID_FILE_PATH@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTON_CFLAGS = @PROTON_CFLAGS@ +PROTON_LIBS = @PROTON_LIBS@ +PROTON_PROACTOR_CFLAGS = @PROTON_PROACTOR_CFLAGS@ +PROTON_PROACTOR_LIBS = @PROTON_PROACTOR_LIBS@ +PTHREADS_CFLAGS = @PTHREADS_CFLAGS@ +PTHREADS_LIBS = @PTHREADS_LIBS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RABBITMQ_CFLAGS = @RABBITMQ_CFLAGS@ +RABBITMQ_LIBS = @RABBITMQ_LIBS@ +RANLIB = @RANLIB@ +READLINK = @READLINK@ +REDIS = @REDIS@ +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@ +TCL_BIN_DIR = @TCL_BIN_DIR@ +TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ +TCL_LIB_FILE = @TCL_LIB_FILE@ +TCL_LIB_FLAG = @TCL_LIB_FLAG@ +TCL_LIB_SPEC = @TCL_LIB_SPEC@ +TCL_PATCH_LEVEL = @TCL_PATCH_LEVEL@ +TCL_SRC_DIR = @TCL_SRC_DIR@ +TCL_STUB_LIB_FILE = @TCL_STUB_LIB_FILE@ +TCL_STUB_LIB_FLAG = @TCL_STUB_LIB_FLAG@ +TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@ +TCL_VERSION = @TCL_VERSION@ +UDPSPOOF_CFLAGS = @UDPSPOOF_CFLAGS@ +UDPSPOOF_LIBS = @UDPSPOOF_LIBS@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +WARN_LDFLAGS = @WARN_LDFLAGS@ +WARN_SCANNERFLAGS = @WARN_SCANNERFLAGS@ +WGET = @WGET@ +YACC = @YACC@ +YACC_FOUND = @YACC_FOUND@ +YFLAGS = @YFLAGS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +ZSTD_CFLAGS = @ZSTD_CFLAGS@ +ZSTD_LIBS = @ZSTD_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@ +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@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +CLEANFILES = $(am__append_7) +man1_MANS = $(am__append_6) +man_MANS = rsyslogd.8 rsyslog.conf.5 +rsyslogd_SOURCES = \ + syslogd.c \ + rsyslogd.c \ + syslogd.h \ + omshell.c \ + omshell.h \ + omusrmsg.c \ + omusrmsg.h \ + omfwd.c \ + omfwd.h \ + omfile.c \ + omfile.h \ + ompipe.c \ + ompipe.h \ + omdiscard.c \ + omdiscard.h \ + pmrfc5424.c \ + pmrfc5424.h \ + pmrfc3164.c \ + pmrfc3164.h \ + smtradfile.c \ + smtradfile.h \ + smfile.c \ + smfile.h \ + smfwd.c \ + smfwd.h \ + smtradfwd.c \ + smtradfwd.h \ + iminternal.c \ + iminternal.h \ + \ + ../dirty.h + +rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(am__append_1) \ + -DSD_EXPORT_SYMBOLS + +# note: it looks like librsyslog.la must be explicitely given on LDDADD, +# otherwise dependencies are not properly calculated (resulting in a +# potentially incomplete build, a problem we had several times...) +rsyslogd_LDADD = ../grammar/libgrammar.la ../runtime/librsyslog.la \ + ../compat/compat.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) \ + $(SOL_LIBS) $(LIBUUID_LIBS) $(HASH_XXHASH_LIBS) \ + $(am__append_2) +@OS_AIX_FALSE@@OS_APPLE_FALSE@@OS_LINUX_FALSE@rsyslogd_LDFLAGS = -export-dynamic +@OS_AIX_TRUE@@OS_APPLE_FALSE@@OS_LINUX_FALSE@rsyslogd_LDFLAGS = -brtl -bexpall -f"aix_exports_list" -lsrc +@OS_APPLE_TRUE@@OS_LINUX_FALSE@rsyslogd_LDFLAGS = -export-dynamic \ +@OS_APPLE_TRUE@@OS_LINUX_FALSE@ -Wl,$(top_builddir)/runtime/.libs/librsyslog.a + + +# Note: do NOT indent the if chain - it will not work! +@OS_LINUX_TRUE@rsyslogd_LDFLAGS = -export-dynamic \ +@OS_LINUX_TRUE@ #-Wl,--whole-archive,$(top_builddir)/runtime/.libs/librsyslog.a,--no-whole-archive + +@OS_AIX_FALSE@@OS_APPLE_FALSE@@OS_LINUX_FALSE@exports_list_file = +@OS_AIX_TRUE@@OS_APPLE_FALSE@@OS_LINUX_FALSE@exports_list_file = aix_exports_list +@OS_APPLE_TRUE@@OS_LINUX_FALSE@exports_list_file = +@OS_LINUX_TRUE@exports_list_file = +EXTRA_DIST = $(man_MANS) rscryutil.rst recover_qi.pl $(am__append_8) +EXTRA_rsyslogd_DEPENDENCIES = $(exports_list_file) +@ENABLE_DIAGTOOLS_TRUE@rsyslog_diag_hostname_SOURCES = gethostn.c +@ENABLE_DIAGTOOLS_TRUE@msggen_SOURCES = msggen.c +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@logctl_SOURCES = logctl.c +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@logctl_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBMONGOC_CFLAGS) +@ENABLE_OMMONGODB_TRUE@@ENABLE_USERTOOLS_TRUE@logctl_LDADD = $(LIBMONGOC_LIBS) +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@rscryutil = rscryutil.c +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@rscryutil_LDADD = ../runtime/libgcry.la $(LIBGCRYPT_LIBS) +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@rscryutil_LDFLAGS = \ +@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@ -Wl,--whole-archive,--no-whole-archive + +@ENABLE_GENERATE_MAN_PAGES_TRUE@@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@RSTMANFILE = rscryutil.rst +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 tools/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tools/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + 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-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +logctl$(EXEEXT): $(logctl_OBJECTS) $(logctl_DEPENDENCIES) $(EXTRA_logctl_DEPENDENCIES) + @rm -f logctl$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(logctl_OBJECTS) $(logctl_LDADD) $(LIBS) + +msggen$(EXEEXT): $(msggen_OBJECTS) $(msggen_DEPENDENCIES) $(EXTRA_msggen_DEPENDENCIES) + @rm -f msggen$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(msggen_OBJECTS) $(msggen_LDADD) $(LIBS) + +rscryutil$(EXEEXT): $(rscryutil_OBJECTS) $(rscryutil_DEPENDENCIES) $(EXTRA_rscryutil_DEPENDENCIES) + @rm -f rscryutil$(EXEEXT) + $(AM_V_CCLD)$(rscryutil_LINK) $(rscryutil_OBJECTS) $(rscryutil_LDADD) $(LIBS) + +rsyslog_diag_hostname$(EXEEXT): $(rsyslog_diag_hostname_OBJECTS) $(rsyslog_diag_hostname_DEPENDENCIES) $(EXTRA_rsyslog_diag_hostname_DEPENDENCIES) + @rm -f rsyslog_diag_hostname$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rsyslog_diag_hostname_OBJECTS) $(rsyslog_diag_hostname_LDADD) $(LIBS) + +rsyslogd$(EXEEXT): $(rsyslogd_OBJECTS) $(rsyslogd_DEPENDENCIES) $(EXTRA_rsyslogd_DEPENDENCIES) + @rm -f rsyslogd$(EXEEXT) + $(AM_V_CCLD)$(rsyslogd_LINK) $(rsyslogd_OBJECTS) $(rsyslogd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gethostn.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logctl-logctl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msggen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rscryutil-rscryutil.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-iminternal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-omdiscard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-omfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-omfwd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-ompipe.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-omshell.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-omusrmsg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-pmrfc3164.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-pmrfc5424.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-rsyslogd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-smfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-smfwd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-smtradfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-smtradfwd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsyslogd-syslogd.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.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 $@ $< + +logctl-logctl.o: logctl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(logctl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT logctl-logctl.o -MD -MP -MF $(DEPDIR)/logctl-logctl.Tpo -c -o logctl-logctl.o `test -f 'logctl.c' || echo '$(srcdir)/'`logctl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/logctl-logctl.Tpo $(DEPDIR)/logctl-logctl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logctl.c' object='logctl-logctl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(logctl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o logctl-logctl.o `test -f 'logctl.c' || echo '$(srcdir)/'`logctl.c + +logctl-logctl.obj: logctl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(logctl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT logctl-logctl.obj -MD -MP -MF $(DEPDIR)/logctl-logctl.Tpo -c -o logctl-logctl.obj `if test -f 'logctl.c'; then $(CYGPATH_W) 'logctl.c'; else $(CYGPATH_W) '$(srcdir)/logctl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/logctl-logctl.Tpo $(DEPDIR)/logctl-logctl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logctl.c' object='logctl-logctl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(logctl_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o logctl-logctl.obj `if test -f 'logctl.c'; then $(CYGPATH_W) 'logctl.c'; else $(CYGPATH_W) '$(srcdir)/logctl.c'; fi` + +rscryutil-rscryutil.o: rscryutil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rscryutil_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rscryutil-rscryutil.o -MD -MP -MF $(DEPDIR)/rscryutil-rscryutil.Tpo -c -o rscryutil-rscryutil.o `test -f 'rscryutil.c' || echo '$(srcdir)/'`rscryutil.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rscryutil-rscryutil.Tpo $(DEPDIR)/rscryutil-rscryutil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rscryutil.c' object='rscryutil-rscryutil.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rscryutil_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rscryutil-rscryutil.o `test -f 'rscryutil.c' || echo '$(srcdir)/'`rscryutil.c + +rscryutil-rscryutil.obj: rscryutil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rscryutil_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rscryutil-rscryutil.obj -MD -MP -MF $(DEPDIR)/rscryutil-rscryutil.Tpo -c -o rscryutil-rscryutil.obj `if test -f 'rscryutil.c'; then $(CYGPATH_W) 'rscryutil.c'; else $(CYGPATH_W) '$(srcdir)/rscryutil.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rscryutil-rscryutil.Tpo $(DEPDIR)/rscryutil-rscryutil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rscryutil.c' object='rscryutil-rscryutil.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rscryutil_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rscryutil-rscryutil.obj `if test -f 'rscryutil.c'; then $(CYGPATH_W) 'rscryutil.c'; else $(CYGPATH_W) '$(srcdir)/rscryutil.c'; fi` + +rsyslogd-syslogd.o: syslogd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-syslogd.o -MD -MP -MF $(DEPDIR)/rsyslogd-syslogd.Tpo -c -o rsyslogd-syslogd.o `test -f 'syslogd.c' || echo '$(srcdir)/'`syslogd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-syslogd.Tpo $(DEPDIR)/rsyslogd-syslogd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='syslogd.c' object='rsyslogd-syslogd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-syslogd.o `test -f 'syslogd.c' || echo '$(srcdir)/'`syslogd.c + +rsyslogd-syslogd.obj: syslogd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-syslogd.obj -MD -MP -MF $(DEPDIR)/rsyslogd-syslogd.Tpo -c -o rsyslogd-syslogd.obj `if test -f 'syslogd.c'; then $(CYGPATH_W) 'syslogd.c'; else $(CYGPATH_W) '$(srcdir)/syslogd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-syslogd.Tpo $(DEPDIR)/rsyslogd-syslogd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='syslogd.c' object='rsyslogd-syslogd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-syslogd.obj `if test -f 'syslogd.c'; then $(CYGPATH_W) 'syslogd.c'; else $(CYGPATH_W) '$(srcdir)/syslogd.c'; fi` + +rsyslogd-rsyslogd.o: rsyslogd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-rsyslogd.o -MD -MP -MF $(DEPDIR)/rsyslogd-rsyslogd.Tpo -c -o rsyslogd-rsyslogd.o `test -f 'rsyslogd.c' || echo '$(srcdir)/'`rsyslogd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-rsyslogd.Tpo $(DEPDIR)/rsyslogd-rsyslogd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rsyslogd.c' object='rsyslogd-rsyslogd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-rsyslogd.o `test -f 'rsyslogd.c' || echo '$(srcdir)/'`rsyslogd.c + +rsyslogd-rsyslogd.obj: rsyslogd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-rsyslogd.obj -MD -MP -MF $(DEPDIR)/rsyslogd-rsyslogd.Tpo -c -o rsyslogd-rsyslogd.obj `if test -f 'rsyslogd.c'; then $(CYGPATH_W) 'rsyslogd.c'; else $(CYGPATH_W) '$(srcdir)/rsyslogd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-rsyslogd.Tpo $(DEPDIR)/rsyslogd-rsyslogd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rsyslogd.c' object='rsyslogd-rsyslogd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-rsyslogd.obj `if test -f 'rsyslogd.c'; then $(CYGPATH_W) 'rsyslogd.c'; else $(CYGPATH_W) '$(srcdir)/rsyslogd.c'; fi` + +rsyslogd-omshell.o: omshell.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omshell.o -MD -MP -MF $(DEPDIR)/rsyslogd-omshell.Tpo -c -o rsyslogd-omshell.o `test -f 'omshell.c' || echo '$(srcdir)/'`omshell.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omshell.Tpo $(DEPDIR)/rsyslogd-omshell.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omshell.c' object='rsyslogd-omshell.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omshell.o `test -f 'omshell.c' || echo '$(srcdir)/'`omshell.c + +rsyslogd-omshell.obj: omshell.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omshell.obj -MD -MP -MF $(DEPDIR)/rsyslogd-omshell.Tpo -c -o rsyslogd-omshell.obj `if test -f 'omshell.c'; then $(CYGPATH_W) 'omshell.c'; else $(CYGPATH_W) '$(srcdir)/omshell.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omshell.Tpo $(DEPDIR)/rsyslogd-omshell.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omshell.c' object='rsyslogd-omshell.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omshell.obj `if test -f 'omshell.c'; then $(CYGPATH_W) 'omshell.c'; else $(CYGPATH_W) '$(srcdir)/omshell.c'; fi` + +rsyslogd-omusrmsg.o: omusrmsg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omusrmsg.o -MD -MP -MF $(DEPDIR)/rsyslogd-omusrmsg.Tpo -c -o rsyslogd-omusrmsg.o `test -f 'omusrmsg.c' || echo '$(srcdir)/'`omusrmsg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omusrmsg.Tpo $(DEPDIR)/rsyslogd-omusrmsg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omusrmsg.c' object='rsyslogd-omusrmsg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omusrmsg.o `test -f 'omusrmsg.c' || echo '$(srcdir)/'`omusrmsg.c + +rsyslogd-omusrmsg.obj: omusrmsg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omusrmsg.obj -MD -MP -MF $(DEPDIR)/rsyslogd-omusrmsg.Tpo -c -o rsyslogd-omusrmsg.obj `if test -f 'omusrmsg.c'; then $(CYGPATH_W) 'omusrmsg.c'; else $(CYGPATH_W) '$(srcdir)/omusrmsg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omusrmsg.Tpo $(DEPDIR)/rsyslogd-omusrmsg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omusrmsg.c' object='rsyslogd-omusrmsg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omusrmsg.obj `if test -f 'omusrmsg.c'; then $(CYGPATH_W) 'omusrmsg.c'; else $(CYGPATH_W) '$(srcdir)/omusrmsg.c'; fi` + +rsyslogd-omfwd.o: omfwd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omfwd.o -MD -MP -MF $(DEPDIR)/rsyslogd-omfwd.Tpo -c -o rsyslogd-omfwd.o `test -f 'omfwd.c' || echo '$(srcdir)/'`omfwd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omfwd.Tpo $(DEPDIR)/rsyslogd-omfwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omfwd.c' object='rsyslogd-omfwd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omfwd.o `test -f 'omfwd.c' || echo '$(srcdir)/'`omfwd.c + +rsyslogd-omfwd.obj: omfwd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omfwd.obj -MD -MP -MF $(DEPDIR)/rsyslogd-omfwd.Tpo -c -o rsyslogd-omfwd.obj `if test -f 'omfwd.c'; then $(CYGPATH_W) 'omfwd.c'; else $(CYGPATH_W) '$(srcdir)/omfwd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omfwd.Tpo $(DEPDIR)/rsyslogd-omfwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omfwd.c' object='rsyslogd-omfwd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omfwd.obj `if test -f 'omfwd.c'; then $(CYGPATH_W) 'omfwd.c'; else $(CYGPATH_W) '$(srcdir)/omfwd.c'; fi` + +rsyslogd-omfile.o: omfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omfile.o -MD -MP -MF $(DEPDIR)/rsyslogd-omfile.Tpo -c -o rsyslogd-omfile.o `test -f 'omfile.c' || echo '$(srcdir)/'`omfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omfile.Tpo $(DEPDIR)/rsyslogd-omfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omfile.c' object='rsyslogd-omfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omfile.o `test -f 'omfile.c' || echo '$(srcdir)/'`omfile.c + +rsyslogd-omfile.obj: omfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omfile.obj -MD -MP -MF $(DEPDIR)/rsyslogd-omfile.Tpo -c -o rsyslogd-omfile.obj `if test -f 'omfile.c'; then $(CYGPATH_W) 'omfile.c'; else $(CYGPATH_W) '$(srcdir)/omfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omfile.Tpo $(DEPDIR)/rsyslogd-omfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omfile.c' object='rsyslogd-omfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omfile.obj `if test -f 'omfile.c'; then $(CYGPATH_W) 'omfile.c'; else $(CYGPATH_W) '$(srcdir)/omfile.c'; fi` + +rsyslogd-ompipe.o: ompipe.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-ompipe.o -MD -MP -MF $(DEPDIR)/rsyslogd-ompipe.Tpo -c -o rsyslogd-ompipe.o `test -f 'ompipe.c' || echo '$(srcdir)/'`ompipe.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-ompipe.Tpo $(DEPDIR)/rsyslogd-ompipe.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ompipe.c' object='rsyslogd-ompipe.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-ompipe.o `test -f 'ompipe.c' || echo '$(srcdir)/'`ompipe.c + +rsyslogd-ompipe.obj: ompipe.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-ompipe.obj -MD -MP -MF $(DEPDIR)/rsyslogd-ompipe.Tpo -c -o rsyslogd-ompipe.obj `if test -f 'ompipe.c'; then $(CYGPATH_W) 'ompipe.c'; else $(CYGPATH_W) '$(srcdir)/ompipe.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-ompipe.Tpo $(DEPDIR)/rsyslogd-ompipe.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ompipe.c' object='rsyslogd-ompipe.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-ompipe.obj `if test -f 'ompipe.c'; then $(CYGPATH_W) 'ompipe.c'; else $(CYGPATH_W) '$(srcdir)/ompipe.c'; fi` + +rsyslogd-omdiscard.o: omdiscard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omdiscard.o -MD -MP -MF $(DEPDIR)/rsyslogd-omdiscard.Tpo -c -o rsyslogd-omdiscard.o `test -f 'omdiscard.c' || echo '$(srcdir)/'`omdiscard.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omdiscard.Tpo $(DEPDIR)/rsyslogd-omdiscard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omdiscard.c' object='rsyslogd-omdiscard.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omdiscard.o `test -f 'omdiscard.c' || echo '$(srcdir)/'`omdiscard.c + +rsyslogd-omdiscard.obj: omdiscard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-omdiscard.obj -MD -MP -MF $(DEPDIR)/rsyslogd-omdiscard.Tpo -c -o rsyslogd-omdiscard.obj `if test -f 'omdiscard.c'; then $(CYGPATH_W) 'omdiscard.c'; else $(CYGPATH_W) '$(srcdir)/omdiscard.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-omdiscard.Tpo $(DEPDIR)/rsyslogd-omdiscard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='omdiscard.c' object='rsyslogd-omdiscard.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-omdiscard.obj `if test -f 'omdiscard.c'; then $(CYGPATH_W) 'omdiscard.c'; else $(CYGPATH_W) '$(srcdir)/omdiscard.c'; fi` + +rsyslogd-pmrfc5424.o: pmrfc5424.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-pmrfc5424.o -MD -MP -MF $(DEPDIR)/rsyslogd-pmrfc5424.Tpo -c -o rsyslogd-pmrfc5424.o `test -f 'pmrfc5424.c' || echo '$(srcdir)/'`pmrfc5424.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-pmrfc5424.Tpo $(DEPDIR)/rsyslogd-pmrfc5424.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pmrfc5424.c' object='rsyslogd-pmrfc5424.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-pmrfc5424.o `test -f 'pmrfc5424.c' || echo '$(srcdir)/'`pmrfc5424.c + +rsyslogd-pmrfc5424.obj: pmrfc5424.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-pmrfc5424.obj -MD -MP -MF $(DEPDIR)/rsyslogd-pmrfc5424.Tpo -c -o rsyslogd-pmrfc5424.obj `if test -f 'pmrfc5424.c'; then $(CYGPATH_W) 'pmrfc5424.c'; else $(CYGPATH_W) '$(srcdir)/pmrfc5424.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-pmrfc5424.Tpo $(DEPDIR)/rsyslogd-pmrfc5424.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pmrfc5424.c' object='rsyslogd-pmrfc5424.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-pmrfc5424.obj `if test -f 'pmrfc5424.c'; then $(CYGPATH_W) 'pmrfc5424.c'; else $(CYGPATH_W) '$(srcdir)/pmrfc5424.c'; fi` + +rsyslogd-pmrfc3164.o: pmrfc3164.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-pmrfc3164.o -MD -MP -MF $(DEPDIR)/rsyslogd-pmrfc3164.Tpo -c -o rsyslogd-pmrfc3164.o `test -f 'pmrfc3164.c' || echo '$(srcdir)/'`pmrfc3164.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-pmrfc3164.Tpo $(DEPDIR)/rsyslogd-pmrfc3164.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pmrfc3164.c' object='rsyslogd-pmrfc3164.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-pmrfc3164.o `test -f 'pmrfc3164.c' || echo '$(srcdir)/'`pmrfc3164.c + +rsyslogd-pmrfc3164.obj: pmrfc3164.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-pmrfc3164.obj -MD -MP -MF $(DEPDIR)/rsyslogd-pmrfc3164.Tpo -c -o rsyslogd-pmrfc3164.obj `if test -f 'pmrfc3164.c'; then $(CYGPATH_W) 'pmrfc3164.c'; else $(CYGPATH_W) '$(srcdir)/pmrfc3164.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-pmrfc3164.Tpo $(DEPDIR)/rsyslogd-pmrfc3164.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pmrfc3164.c' object='rsyslogd-pmrfc3164.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-pmrfc3164.obj `if test -f 'pmrfc3164.c'; then $(CYGPATH_W) 'pmrfc3164.c'; else $(CYGPATH_W) '$(srcdir)/pmrfc3164.c'; fi` + +rsyslogd-smtradfile.o: smtradfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smtradfile.o -MD -MP -MF $(DEPDIR)/rsyslogd-smtradfile.Tpo -c -o rsyslogd-smtradfile.o `test -f 'smtradfile.c' || echo '$(srcdir)/'`smtradfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smtradfile.Tpo $(DEPDIR)/rsyslogd-smtradfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtradfile.c' object='rsyslogd-smtradfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smtradfile.o `test -f 'smtradfile.c' || echo '$(srcdir)/'`smtradfile.c + +rsyslogd-smtradfile.obj: smtradfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smtradfile.obj -MD -MP -MF $(DEPDIR)/rsyslogd-smtradfile.Tpo -c -o rsyslogd-smtradfile.obj `if test -f 'smtradfile.c'; then $(CYGPATH_W) 'smtradfile.c'; else $(CYGPATH_W) '$(srcdir)/smtradfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smtradfile.Tpo $(DEPDIR)/rsyslogd-smtradfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtradfile.c' object='rsyslogd-smtradfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smtradfile.obj `if test -f 'smtradfile.c'; then $(CYGPATH_W) 'smtradfile.c'; else $(CYGPATH_W) '$(srcdir)/smtradfile.c'; fi` + +rsyslogd-smfile.o: smfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smfile.o -MD -MP -MF $(DEPDIR)/rsyslogd-smfile.Tpo -c -o rsyslogd-smfile.o `test -f 'smfile.c' || echo '$(srcdir)/'`smfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smfile.Tpo $(DEPDIR)/rsyslogd-smfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smfile.c' object='rsyslogd-smfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smfile.o `test -f 'smfile.c' || echo '$(srcdir)/'`smfile.c + +rsyslogd-smfile.obj: smfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smfile.obj -MD -MP -MF $(DEPDIR)/rsyslogd-smfile.Tpo -c -o rsyslogd-smfile.obj `if test -f 'smfile.c'; then $(CYGPATH_W) 'smfile.c'; else $(CYGPATH_W) '$(srcdir)/smfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smfile.Tpo $(DEPDIR)/rsyslogd-smfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smfile.c' object='rsyslogd-smfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smfile.obj `if test -f 'smfile.c'; then $(CYGPATH_W) 'smfile.c'; else $(CYGPATH_W) '$(srcdir)/smfile.c'; fi` + +rsyslogd-smfwd.o: smfwd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smfwd.o -MD -MP -MF $(DEPDIR)/rsyslogd-smfwd.Tpo -c -o rsyslogd-smfwd.o `test -f 'smfwd.c' || echo '$(srcdir)/'`smfwd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smfwd.Tpo $(DEPDIR)/rsyslogd-smfwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smfwd.c' object='rsyslogd-smfwd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smfwd.o `test -f 'smfwd.c' || echo '$(srcdir)/'`smfwd.c + +rsyslogd-smfwd.obj: smfwd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smfwd.obj -MD -MP -MF $(DEPDIR)/rsyslogd-smfwd.Tpo -c -o rsyslogd-smfwd.obj `if test -f 'smfwd.c'; then $(CYGPATH_W) 'smfwd.c'; else $(CYGPATH_W) '$(srcdir)/smfwd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smfwd.Tpo $(DEPDIR)/rsyslogd-smfwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smfwd.c' object='rsyslogd-smfwd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smfwd.obj `if test -f 'smfwd.c'; then $(CYGPATH_W) 'smfwd.c'; else $(CYGPATH_W) '$(srcdir)/smfwd.c'; fi` + +rsyslogd-smtradfwd.o: smtradfwd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smtradfwd.o -MD -MP -MF $(DEPDIR)/rsyslogd-smtradfwd.Tpo -c -o rsyslogd-smtradfwd.o `test -f 'smtradfwd.c' || echo '$(srcdir)/'`smtradfwd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smtradfwd.Tpo $(DEPDIR)/rsyslogd-smtradfwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtradfwd.c' object='rsyslogd-smtradfwd.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smtradfwd.o `test -f 'smtradfwd.c' || echo '$(srcdir)/'`smtradfwd.c + +rsyslogd-smtradfwd.obj: smtradfwd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-smtradfwd.obj -MD -MP -MF $(DEPDIR)/rsyslogd-smtradfwd.Tpo -c -o rsyslogd-smtradfwd.obj `if test -f 'smtradfwd.c'; then $(CYGPATH_W) 'smtradfwd.c'; else $(CYGPATH_W) '$(srcdir)/smtradfwd.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-smtradfwd.Tpo $(DEPDIR)/rsyslogd-smtradfwd.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtradfwd.c' object='rsyslogd-smtradfwd.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-smtradfwd.obj `if test -f 'smtradfwd.c'; then $(CYGPATH_W) 'smtradfwd.c'; else $(CYGPATH_W) '$(srcdir)/smtradfwd.c'; fi` + +rsyslogd-iminternal.o: iminternal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-iminternal.o -MD -MP -MF $(DEPDIR)/rsyslogd-iminternal.Tpo -c -o rsyslogd-iminternal.o `test -f 'iminternal.c' || echo '$(srcdir)/'`iminternal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-iminternal.Tpo $(DEPDIR)/rsyslogd-iminternal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iminternal.c' object='rsyslogd-iminternal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-iminternal.o `test -f 'iminternal.c' || echo '$(srcdir)/'`iminternal.c + +rsyslogd-iminternal.obj: iminternal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rsyslogd-iminternal.obj -MD -MP -MF $(DEPDIR)/rsyslogd-iminternal.Tpo -c -o rsyslogd-iminternal.obj `if test -f 'iminternal.c'; then $(CYGPATH_W) 'iminternal.c'; else $(CYGPATH_W) '$(srcdir)/iminternal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/rsyslogd-iminternal.Tpo $(DEPDIR)/rsyslogd-iminternal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iminternal.c' object='rsyslogd-iminternal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rsyslogd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rsyslogd-iminternal.obj `if test -f 'iminternal.c'; then $(CYGPATH_W) 'iminternal.c'; else $(CYGPATH_W) '$(srcdir)/iminternal.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man1: $(man1_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + @list1='$(man1_MANS)'; \ + list2='$(man_MANS)'; \ + test -n "$(man1dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.1[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ + done; } + +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.1[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) +install-man5: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man5dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.5[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ + done; } + +uninstall-man5: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man5dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.5[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + @list1=''; \ + list2='$(man_MANS)'; \ + test -n "$(man8dir)" \ + && test -n "`echo $$list1$$list2`" \ + || exit 0; \ + echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ + { for i in $$list1; do echo "$$i"; done; \ + if test -n "$$list2"; then \ + for i in $$list2; do echo "$$i"; done \ + | sed -n '/\.8[a-z]*$$/p'; \ + fi; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) + +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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 $(PROGRAMS) $(MANS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; 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: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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-binPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/gethostn.Po + -rm -f ./$(DEPDIR)/logctl-logctl.Po + -rm -f ./$(DEPDIR)/msggen.Po + -rm -f ./$(DEPDIR)/rscryutil-rscryutil.Po + -rm -f ./$(DEPDIR)/rsyslogd-iminternal.Po + -rm -f ./$(DEPDIR)/rsyslogd-omdiscard.Po + -rm -f ./$(DEPDIR)/rsyslogd-omfile.Po + -rm -f ./$(DEPDIR)/rsyslogd-omfwd.Po + -rm -f ./$(DEPDIR)/rsyslogd-ompipe.Po + -rm -f ./$(DEPDIR)/rsyslogd-omshell.Po + -rm -f ./$(DEPDIR)/rsyslogd-omusrmsg.Po + -rm -f ./$(DEPDIR)/rsyslogd-pmrfc3164.Po + -rm -f ./$(DEPDIR)/rsyslogd-pmrfc5424.Po + -rm -f ./$(DEPDIR)/rsyslogd-rsyslogd.Po + -rm -f ./$(DEPDIR)/rsyslogd-smfile.Po + -rm -f ./$(DEPDIR)/rsyslogd-smfwd.Po + -rm -f ./$(DEPDIR)/rsyslogd-smtradfile.Po + -rm -f ./$(DEPDIR)/rsyslogd-smtradfwd.Po + -rm -f ./$(DEPDIR)/rsyslogd-syslogd.Po + -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-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man1 install-man5 install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/gethostn.Po + -rm -f ./$(DEPDIR)/logctl-logctl.Po + -rm -f ./$(DEPDIR)/msggen.Po + -rm -f ./$(DEPDIR)/rscryutil-rscryutil.Po + -rm -f ./$(DEPDIR)/rsyslogd-iminternal.Po + -rm -f ./$(DEPDIR)/rsyslogd-omdiscard.Po + -rm -f ./$(DEPDIR)/rsyslogd-omfile.Po + -rm -f ./$(DEPDIR)/rsyslogd-omfwd.Po + -rm -f ./$(DEPDIR)/rsyslogd-ompipe.Po + -rm -f ./$(DEPDIR)/rsyslogd-omshell.Po + -rm -f ./$(DEPDIR)/rsyslogd-omusrmsg.Po + -rm -f ./$(DEPDIR)/rsyslogd-pmrfc3164.Po + -rm -f ./$(DEPDIR)/rsyslogd-pmrfc5424.Po + -rm -f ./$(DEPDIR)/rsyslogd-rsyslogd.Po + -rm -f ./$(DEPDIR)/rsyslogd-smfile.Po + -rm -f ./$(DEPDIR)/rsyslogd-smfwd.Po + -rm -f ./$(DEPDIR)/rsyslogd-smtradfile.Po + -rm -f ./$(DEPDIR)/rsyslogd-smtradfwd.Po + -rm -f ./$(DEPDIR)/rsyslogd-syslogd.Po + -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-binPROGRAMS uninstall-man \ + uninstall-sbinPROGRAMS + +uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS 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-binPROGRAMS 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-man1 install-man5 \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS 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-binPROGRAMS \ + uninstall-man uninstall-man1 uninstall-man5 uninstall-man8 \ + uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + +@ENABLE_GENERATE_MAN_PAGES_TRUE@@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@rscryutil.1: $(RSTMANFILE) +@ENABLE_GENERATE_MAN_PAGES_TRUE@@ENABLE_LIBGCRYPT_TRUE@@ENABLE_USERTOOLS_TRUE@ $(AM_V_GEN) $(RST2MAN) $(RSTMANFILE) $@ + +aix_exports_list: + echo "$(top_builddir)/runtime/.libs/librsyslog_la-*.o" > $@ + echo "$(top_builddir)/.libs/librsyslog_la-*.o" >> $@ + echo "$(top_builddir)/grammar/.libs/libgrammar_la-*.o" >> $@ + +# 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/tools/gethostn.c b/tools/gethostn.c new file mode 100644 index 0000000..a43c53c --- /dev/null +++ b/tools/gethostn.c @@ -0,0 +1,46 @@ +/* gethostn - a small diagnostic utility to show what the + * gethostname() API returns. Of course, this tool duplicates + * functionality already found in other tools. But the point is + * that the API shall be called by a program that is compiled like + * rsyslogd and does exactly what rsyslog does. + * + * Copyright 2008 Rainer Gerhards and 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[]) +{ + char hostname[4096]; /* this should always be sufficient ;) */ + int err; + + err = gethostname(hostname, sizeof(hostname)); + + if(err) { + perror("gethostname failed"); + exit(1); + } + + printf("hostname of this system is '%s'.\n", hostname); + + return 0; +} diff --git a/tools/iminternal.c b/tools/iminternal.c new file mode 100644 index 0000000..c4dd548 --- /dev/null +++ b/tools/iminternal.c @@ -0,0 +1,182 @@ +/* iminternal.c + * This file set implements the internal messages input module for rsyslog. + * Note: we currently do not have an input module spec, but + * we will have one in the future. This module needs then to be + * adapted. + * + * File begun on 2007-08-03 by RGerhards + * + * Copyright 2007-2022 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <signal.h> + +#include "syslogd.h" +#include "linkedlist.h" +#include "iminternal.h" +#include "unicode-helper.h" + +static linkedList_t llMsgs; +static pthread_mutex_t mutList = PTHREAD_MUTEX_INITIALIZER; + + +/* destructs an iminternal object + */ +static rsRetVal iminternalDestruct(iminternal_t *pThis) +{ + DEFiRet; + + if(pThis->pMsg != NULL) + msgDestruct(&pThis->pMsg); + + free(pThis); + + RETiRet; +} + + +/* Construct an iminternal object + */ +static rsRetVal iminternalConstruct(iminternal_t **ppThis) +{ + DEFiRet; + if((*ppThis = (iminternal_t*) calloc(1, sizeof(iminternal_t))) == NULL) { + iRet = RS_RET_OUT_OF_MEMORY; + } + RETiRet; +} + + +/* add a message to the linked list + * Note: the pMsg reference counter is not incremented. Consequently, + * the caller must NOT decrement it. The caller actually hands over + * full ownership of the pMsg object. + */ +rsRetVal iminternalAddMsg(smsg_t *pMsg) +{ + DEFiRet; + iminternal_t *pThis = NULL; + struct timespec to; + int r; + int is_locked = 0; + + /* we guard against deadlock, so we can guarantee rsyslog will never + * block due to internal messages. The 1 second timeout should be + * sufficient under all circumstances. + */ + to.tv_sec = time(NULL) + 1; + to.tv_nsec = 0; + #if !defined(__APPLE__) + r = pthread_mutex_timedlock(&mutList, &to); + #else + r = pthread_mutex_trylock(&mutList); // must check + #endif + if(r != 0) { + dbgprintf("iminternalAddMsg: timedlock for mutex failed with %d, msg %s\n", + r, getMSG(pMsg)); + /* the message is lost, nothing we can do against this! */ + msgDestruct(&pMsg); + ABORT_FINALIZE(RS_RET_ERR); + } + is_locked = 1; + CHKiRet(iminternalConstruct(&pThis)); + pThis->pMsg = pMsg; + CHKiRet(llAppend(&llMsgs, NULL, (void*) pThis)); + + if(PREFER_FETCH_32BIT(bHaveMainQueue)) { + DBGPRINTF("signaling new internal message via SIGTTOU: '%s'\n", + pThis->pMsg->pszRawMsg); + kill(glblGetOurPid(), SIGTTOU); + } + +finalize_it: + if(is_locked) { + pthread_mutex_unlock(&mutList); + } + if(iRet != RS_RET_OK) { + dbgprintf("iminternalAddMsg() error %d - can not otherwise report this error, message lost\n", iRet); + if(pThis != NULL) + iminternalDestruct(pThis); + } + + RETiRet; +} + + +/* pull the first error message from the linked list, remove it + * from the list and return it to the caller. The caller is + * responsible for freeing the message! + */ +rsRetVal iminternalRemoveMsg(smsg_t **ppMsg) +{ + DEFiRet; + iminternal_t *pThis; + linkedListCookie_t llCookie = NULL; + + pthread_mutex_lock(&mutList); + CHKiRet(llGetNextElt(&llMsgs, &llCookie, (void*)&pThis)); + if(!strcmp((char*)pThis->pMsg->pszHOSTNAME, "[localhost]")) { + /* early (pre-conf) startup message detected, need to set real hostname now */ + MsgSetHOSTNAME(pThis->pMsg, glblGetLocalHostName(), ustrlen(glblGetLocalHostName())); + } + *ppMsg = pThis->pMsg; + pThis->pMsg = NULL; /* we do no longer own it - important for destructor */ + + if(llDestroyRootElt(&llMsgs) != RS_RET_OK) { + dbgprintf("Root element of iminternal linked list could not be destroyed - there is " + "nothing we can do against it, we ignore it for now. Things may go wild " + "from here on. This is most probably a program logic error.\n"); + } + +finalize_it: + pthread_mutex_unlock(&mutList); + RETiRet; +} + + +/* initialize the iminternal subsystem + * must be called once at the start of the program + */ +rsRetVal modInitIminternal(void) +{ + DEFiRet; + iRet = llInit(&llMsgs, iminternalDestruct, NULL, NULL); + RETiRet; +} + + +/* de-initialize the iminternal subsystem + * must be called once at the end of the program + * Note: the error list must have been pulled first. We do + * NOT care if there are any errors left - we simply destroy + * them. + */ +rsRetVal modExitIminternal(void) +{ + DEFiRet; + iRet = llDestroy(&llMsgs); + RETiRet; +} diff --git a/tools/iminternal.h b/tools/iminternal.h new file mode 100644 index 0000000..f7c15e5 --- /dev/null +++ b/tools/iminternal.h @@ -0,0 +1,45 @@ +/* Definition of the internal messages input module. + * + * Note: we currently do not have an input module spec, but + * we will have one in the future. This module needs then to be + * adapted. + * + * Copyright 2007 Rainer Gerhards and 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. + */ + +#ifndef IMINTERNAL_H_INCLUDED +#define IMINTERNAL_H_INCLUDED +#include "template.h" + +/* this is a single entry for a parse routine. It describes exactly + * one entry point/handler. + * The short name is cslch (Configfile SysLine CommandHandler) + */ +struct iminternal_s { /* config file sysline parse entry */ + smsg_t *pMsg; /* the message (in all its glory) */ +}; +typedef struct iminternal_s iminternal_t; + +/* prototypes */ +rsRetVal modInitIminternal(void); +rsRetVal modExitIminternal(void); +rsRetVal iminternalAddMsg(smsg_t *pMsg); +rsRetVal iminternalRemoveMsg(smsg_t **ppMsg); + +#endif /* #ifndef IMINTERNAL_H_INCLUDED */ diff --git a/tools/logctl.c b/tools/logctl.c new file mode 100644 index 0000000..1010409 --- /dev/null +++ b/tools/logctl.c @@ -0,0 +1,486 @@ +/** + * logctl - a tool to access lumberjack logs in MongoDB + * ... and potentially other sources in the future. + * + * Copyright 2012-2022 Ulrike Gerhards and Adiscon GmbH. + * + * Copyright 2017 Hugo Soszynski and aDvens + * + * long short + + * level l read records with level x + * severity s read records with severity x + * ret r number of records to return + * skip k number of records to skip + * sys y read records of system x + * msg m read records with message containing x + * datef f read records starting on time received x + * dateu u read records until time received x + * + * examples: + * + * logctl -f 15/05/2012-12:00:00 -u 15/05/2012-12:37:00 + * logctl -s 50 --ret 10 + * logctl -m "closed" + * logctl -l "INFO" + * logctl -s 3 + * logctl -y "ubuntu" + * + * 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" +#define _XOPEN_SOURCE 700 /* Need to define POSIX version to use strptime() */ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <getopt.h> +#include <unistd.h> + +/* we need this to avoid issues with older versions of libbson */ +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-attributes" +#pragma GCC diagnostic ignored "-Wexpansion-to-defined" +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#pragma GCC diagnostic ignored "-Wold-style-definition" +#endif +#include <mongoc.h> +#include <bson.h> +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#define N 80 + +static struct option long_options[] = + { + {"level", required_argument, NULL, 'l'}, + {"severity", required_argument, NULL, 's'}, + {"ret", required_argument, NULL, 'r'}, + {"skip", required_argument, NULL, 'k'}, + {"sys", required_argument, NULL, 'y'}, + {"msg", required_argument, NULL, 'm'}, + {"datef", required_argument, NULL, 'f'}, + {"dateu", required_argument, NULL, 'u'}, + {NULL, 0, NULL, 0} + }; + +struct queryopt +{ + int32_t e_sever; + int32_t e_ret; + int32_t e_skip; + char* e_date; + char* e_level; + char* e_msg; + char* e_sys; + char* e_dateu; + int bsever; + int blevel; + int bskip; + int bret; + int bsys; + int bmsg; + int bdate; + int bdatef; + int bdateu; +}; + +struct ofields +{ + const char* msg; + const char* syslog_tag; + const char* prog; + char* date; + int64_t date_r; +}; + +struct query_doc +{ + bson_t* query; +}; + +struct select_doc +{ + bson_t* select; +}; + +struct db_connect +{ + mongoc_client_t* conn; +}; + +struct db_collection +{ + mongoc_collection_t* collection; +}; + +struct db_cursor +{ + mongoc_cursor_t* cursor; +}; + +struct results +{ + const bson_t* result; +}; + + +static void formater(struct ofields* fields) +{ + char str[N]; + time_t rtime; + struct tm now; + + rtime = (time_t) (fields->date_r / 1000); + strftime (str, N, "%b %d %H:%M:%S", gmtime_r (&rtime, &now)); + printf ("%s %s %s %s\n", str, fields->prog, fields->syslog_tag, + fields->msg); +} + +static struct ofields* get_data(struct results* res) +{ + struct ofields* fields; + const char* msg; + const char* prog; + const char* syslog_tag; + int64_t date_r; + bson_iter_t c; + + fields = malloc (sizeof (struct ofields)); + bson_iter_init_find (&c, res->result, "msg"); + if (!(msg = bson_iter_utf8 (&c, NULL))) + { + perror ("bson_cursor_get_string()"); + exit (1); + } + + bson_iter_init_find (&c, res->result, "sys"); + if (!(prog = bson_iter_utf8 (&c, NULL))) + { + perror ("bson_cursor_get_string()"); + exit (1); + } + + bson_iter_init_find (&c, res->result, "syslog_tag"); + if (!(syslog_tag = bson_iter_utf8 (&c, NULL))) + { + perror ("bson_cursor_get_string()"); + exit (1); + } + + bson_iter_init_find (&c, res->result, "time_rcvd"); + if (!(date_r = bson_iter_date_time (&c))) + { + perror ("bson_cursor_get_utc_datetime()"); + exit (1); + } + + fields->msg = msg; + fields->prog = prog; + fields->syslog_tag = syslog_tag; + fields->date_r = date_r; + + return fields; +} + +static void getoptions(int argc, char* argv[], struct queryopt* opt) +{ + int iarg; + + while ((iarg = getopt_long (argc, argv, "l:s:r:k:y:f:u:m:", + long_options, NULL)) != -1) + { + /* check to see if a single character or long option came through */ + switch (iarg) + { + /* short option 's' */ + case 's': + opt->bsever = 1; + opt->e_sever = atoi (optarg); + break; + /* short option 'r' */ + case 'r': + opt->bret = 1; + opt->e_ret = atoi (optarg); + break; + /* short option 'f' : date from */ + case 'f': + opt->bdate = 1; + opt->bdatef = 1; + opt->e_date = optarg; + break; + /* short option 'u': date until */ + case 'u': + opt->bdate = 1; + opt->bdateu = 1; + opt->e_dateu = optarg; + break; + /* short option 'k' */ + case 'k': + opt->bskip = 1; + opt->e_skip = atoi (optarg); + break; + /* short option 'l' */ + case 'l': + opt->blevel = 1; + opt->e_level = optarg; + break; + /* short option 'm' */ + case 'm': + opt->bmsg = 1; + opt->e_msg = optarg; + break; + /* short option 'y' */ + case 'y': + opt->bsys = 1; + opt->e_sys = optarg; + break; + default: + break; + } /* end switch iarg */ + } /* end while */ + +} /* end void getoptions */ + +static struct select_doc* create_select(void) +/* BSON object indicating the fields to return */ +{ + struct select_doc* s_doc; + + s_doc = malloc (sizeof (struct select_doc)); + s_doc->select = bson_new (); + bson_append_utf8 (s_doc->select, "syslog_tag", 10, "s", 1); + bson_append_utf8 (s_doc->select, "msg", 3, "ERROR", 5); + bson_append_utf8 (s_doc->select, "sys", 3, "sys", 3); + bson_append_date_time (s_doc->select, "time_rcvd", 9, 1ll); + return s_doc; +} + +static struct query_doc* create_query(struct queryopt* opt) +{ + struct query_doc* qu_doc; + bson_t* query_what, * order_what, * msg_what, * date_what; + struct tm tm; + time_t t; + int64_t ts; + + qu_doc = malloc (sizeof (struct query_doc)); + qu_doc->query = bson_new (); + query_what = bson_new (); + bson_init (query_what); + bson_append_document_begin (qu_doc->query, "$query", 6, query_what); + if (opt->bsever == 1) + { + bson_append_int32 (query_what, "syslog_sever", 12, + opt->e_sever); + } + if (opt->blevel == 1) + { + bson_append_utf8 (query_what, "level", 5, opt->e_level, -1); + } + + if (opt->bmsg == 1) + { + msg_what = bson_new (); + bson_init (msg_what); + bson_append_document_begin (query_what, "msg", 3, msg_what); + bson_append_utf8 (msg_what, "$regex", 6, opt->e_msg, -1); + bson_append_utf8 (msg_what, "$options", 8, "i", 1); + bson_append_document_end (query_what, msg_what); + } + + if (opt->bdate == 1) + { + date_what = bson_new (); + bson_init (date_what); + bson_append_document_begin (query_what, "time_rcvd", 9, + date_what); + if (opt->bdatef == 1) + { + tm.tm_isdst = -1; + strptime (opt->e_date, "%d/%m/%Y-%H:%M:%S", &tm); + tm.tm_hour = tm.tm_hour + 1; + t = mktime (&tm); + ts = 1000 * (int64_t) t; + bson_append_date_time (date_what, "$gt", 3, ts); + } + + if (opt->bdateu == 1) + { + tm.tm_isdst = -1; + strptime (opt->e_dateu, "%d/%m/%Y-%H:%M:%S", &tm); + tm.tm_hour = tm.tm_hour + 1; + t = mktime (&tm); + ts = 1000 * (int64_t) t; + bson_append_date_time (date_what, "$lt", 3, ts); + } + bson_append_document_end (query_what, date_what); + } + + if (opt->bsys == 1) + { + bson_append_utf8 (query_what, "sys", 3, opt->e_sys, -1); + } + + bson_append_document_end (qu_doc->query, query_what); + + order_what = bson_new (); + bson_init (order_what); + bson_append_document_begin (qu_doc->query, "$orderby", 8, order_what); + bson_append_date_time (order_what, "time_rcvd", 9, 1ll); + bson_append_document_end (qu_doc->query, order_what); + + bson_free (order_what); + return qu_doc; +} + +static struct db_connect* create_conn(void) +{ + struct db_connect* db_conn; + + db_conn = malloc (sizeof (struct db_connect)); + db_conn->conn = mongoc_client_new ("mongodb://localhost:27017"); + if (!db_conn->conn) + { + perror ("mongo_sync_connect()"); + exit (1); + } + return db_conn; +} + +static void close_conn(struct db_connect* db_conn) +{ + mongoc_client_destroy (db_conn->conn); + free (db_conn); +} + +static void free_cursor(struct db_cursor* db_c) +{ + mongoc_cursor_destroy (db_c->cursor); + free (db_c); +} + +static struct db_cursor* launch_query(struct queryopt* opt, + __attribute__((unused)) struct select_doc* s_doc, + struct query_doc* qu_doc, + struct db_collection* db_coll) +{ + struct db_cursor* out; +#if MONGOC_CHECK_VERSION (1, 5, 0) /* Declaration before code (ISO C90) */ + const bson_t* opts = BCON_NEW ( + "skip", BCON_INT32 (opt->e_skip), + "limit", BCON_INT32 (opt->e_ret) + ); +#endif /* MONGOC_CHECK_VERSION (1, 5, 0) */ + + out = malloc (sizeof (struct db_cursor)); + if (!out) + { + perror ("mongo_sync_cmd_query()"); + printf ("malloc failed\n"); + exit (1); + } +#if MONGOC_CHECK_VERSION (1, 5, 0) + out->cursor = mongoc_collection_find_with_opts (db_coll->collection, + qu_doc->query, opts, + NULL); +#else /* !MONGOC_CHECK_VERSION (1, 5, 0) */ + out->cursor = mongoc_collection_find (db_coll->collection, + MONGOC_QUERY_NONE, + (uint32_t)opt->e_skip, + (uint32_t)opt->e_ret, 0, + qu_doc->query, s_doc->select, + NULL); +#endif /* MONGOC_CHECK_VERSION (1, 5, 0) */ + if (!out->cursor) + { + perror ("mongo_sync_cmd_query()"); + printf ("no records found\n"); + exit (1); + } + return out; +} + +static int cursor_next(struct db_cursor* db_c, struct results* res) +{ + if (mongoc_cursor_next (db_c->cursor, &res->result)) + return true; + return false; +} + +static struct db_collection* get_collection(struct db_connect* db_conn) +{ + struct db_collection* coll; + + coll = malloc (sizeof (struct db_collection)); + coll->collection = mongoc_client_get_collection (db_conn->conn, + "syslog", "log"); + return coll; +} + +static void release_collection(struct db_collection* db_coll) +{ + mongoc_collection_destroy (db_coll->collection); + free (db_coll); +} + +int main(int argc, char* argv[]) +{ + + struct queryopt opt; + struct ofields* fields; + struct select_doc* s_doc; + struct query_doc* qu_doc; + struct db_connect* db_conn; + struct db_cursor* db_c; + struct db_collection* db_coll; + struct results* res; + + memset (&opt, 0, sizeof (struct queryopt)); + + mongoc_init (); /* Initialisation of mongo-c-driver */ + + getoptions (argc, argv, &opt); + qu_doc = create_query (&opt); /* create query */ + s_doc = create_select (); + db_conn = create_conn (); /* create connection */ + db_coll = get_collection (db_conn); /* Get the collection to perform query on */ + db_c = launch_query (&opt, s_doc, qu_doc, db_coll); /* launch the query and get the related cursor */ + + res = malloc (sizeof (struct results)); + while (cursor_next (db_c, res)) /* Move cursor & get pointed data */ + { + fields = get_data (res); + formater (fields); /* format output */ + free (fields); + } + + free (res); + free_cursor (db_c); + release_collection (db_coll); + close_conn (db_conn); + free (s_doc); + free (qu_doc); + + mongoc_cleanup (); /* Cleanup of mongo-c-driver */ + + return (0); +} diff --git a/tools/msggen.c b/tools/msggen.c new file mode 100644 index 0000000..ceb952b --- /dev/null +++ b/tools/msggen.c @@ -0,0 +1,36 @@ +/* msggen - a small diagnostic utility that does very quick + * syslog() calls. + * + * Copyright 2008-2014 Rainer Gerhards and 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 <stdio.h> +#include <syslog.h> + +int main(int __attribute__((unused)) argc, char __attribute__((unused)) *argv[]) +{ + int i; + + openlog("msggen", 0 , LOG_LOCAL0); + + for(i = 0 ; i < 10 ; ++i) + syslog(LOG_NOTICE, "This is message number %d", i); + + closelog(); + return 0; +} diff --git a/tools/omdiscard.c b/tools/omdiscard.c new file mode 100644 index 0000000..e1a85dc --- /dev/null +++ b/tools/omdiscard.c @@ -0,0 +1,158 @@ +/* omdiscard.c + * This is the implementation of the built-in discard output module. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2007-07-24 by RGerhards + * + * Copyright 2007-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 "syslogd.h" +#include "syslogd-types.h" +#include "omdiscard.h" +#include "module-template.h" +#include "errmsg.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP + +/* internal structures + */ +DEF_OMOD_STATIC_DATA + +typedef struct _instanceData { + EMPTY_STRUCT +} instanceData; + +typedef struct wrkrInstanceData { + instanceData *pData; +} wrkrInstanceData_t; + +/* we do not need a createInstance()! +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance +*/ + + +BEGINcreateWrkrInstance +CODESTARTcreateWrkrInstance +ENDcreateWrkrInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + /* do nothing */ +ENDdbgPrintInstInfo + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + /* we are not compatible with repeated msg reduction feature, so do not allow it */ +ENDisCompatibleWithFeature + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +BEGINdoAction_NoStrings +CODESTARTdoAction + (void)pMsgData; /* Suppress compiler warning on unused var */ + dbgprintf("\n"); + iRet = RS_RET_DISCARDMSG; +ENDdoAction + + +BEGINfreeInstance +CODESTARTfreeInstance + /* we do not have instance data, so we do not need to + * do anything here. -- rgerhards, 2007-07-25 + */ +ENDfreeInstance + + +BEGINfreeWrkrInstance +CODESTARTfreeWrkrInstance +ENDfreeWrkrInstance + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(0) + pData = NULL; /* this action does not have any instance data */ + p = *pp; + + if(*p == '~') { + dbgprintf("discard\n"); + LogMsg(0, RS_RET_DEPRECATED, LOG_WARNING, + "warning: ~ action is deprecated, consider " + "using the 'stop' statement instead"); + } else { + iRet = RS_RET_CONFLINE_UNPROCESSED; + } +/* we do not use the macro + * CODE_STD_FINALIZERparseSelectorAct + * here as this causes a Coverity ID "false positive" (CID 185431). + * We don't see an issue with using the copy&pasted code as it is unlikly + * to change for this (outdated) module. + */ +finalize_it: ATTR_UNUSED; /* semi-colon needed according to gcc doc! */ + if(iRet == RS_RET_OK || iRet == RS_RET_OK_WARN || iRet == RS_RET_SUSPENDED) { + *ppModData = pData; + *pp = p; + } else { + /* cleanup, we failed */ + if(*ppOMSR != NULL) { + OMSRdestruct(*ppOMSR); + *ppOMSR = NULL; + } + } +/* END modified macro text */ +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_OMOD8_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(Discard) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr +ENDmodInit +/* + * vi:set ai: + */ diff --git a/tools/omdiscard.h b/tools/omdiscard.h new file mode 100644 index 0000000..bc7eb66 --- /dev/null +++ b/tools/omdiscard.h @@ -0,0 +1,33 @@ +/* omdiscard.h + * These are the definitions for the built-in discard output module. + * + * File begun on 2007-07-24 by RGerhards + * + * Copyright 2007-2012 Rainer Gerhards and 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. + */ +#ifndef OMDISCARD_H_INCLUDED +#define OMDISCARD_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitDiscard(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef OMDISCARD_H_INCLUDED */ +/* vi:set ai: + */ diff --git a/tools/omfile.c b/tools/omfile.c new file mode 100644 index 0000000..cf464cd --- /dev/null +++ b/tools/omfile.c @@ -0,0 +1,1644 @@ +/* omfile.c + * This is the implementation of the build-in file output module. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2007-07-21 by RGerhards (extracted from syslogd.c, which + * at the time of the fork from sysklogd was under BSD license) + * + * A large re-write of this file was done in June, 2009. The focus was + * to introduce many more features (like zipped writing), clean up the code + * and make it more reliable. In short, that rewrite tries to provide a new + * solid basis for the next three to five years to come. During it, bugs + * may have been introduced ;) -- rgerhards, 2009-06-04 + * + * Note that as of 2010-02-28 this module does no longer handle + * pipes. These have been moved to ompipe, to reduced the entanglement + * between the two different functionalities. -- rgerhards + * + * Copyright 2007-2024 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 <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include <libgen.h> +#include <unistd.h> +#include <sys/file.h> +#include <fcntl.h> +#ifdef HAVE_ATOMIC_BUILTINS +# include <pthread.h> +#endif + +#include "rsyslog.h" +#include "conf.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "template.h" +#include "outchannel.h" +#include "omfile.h" +#include "cfsysline.h" +#include "module-template.h" +#include "errmsg.h" +#include "stream.h" +#include "unicode-helper.h" +#include "atomic.h" +#include "statsobj.h" +#include "sigprov.h" +#include "cryprov.h" +#include "parserif.h" +#include "janitor.h" +#include "rsconf.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omfile") + +/* forward definitions */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + +/* internal structures + */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(strm) +DEFobjCurrIf(statsobj) + +/* for our current LRU mechanism, we need a monotonically increasing counters. We use + * it much like a "Lamport logical clock": we do not need the actual time, we just need + * to know the sequence in which files were accessed. So we use a simple counter to + * create that sequence. We use an unsigned 64 bit value which is extremely unlike to + * wrap within the lifetime of a process. If we process 1,000,000 file writes per + * second, the process could still exist over 500,000 years before a wrap to 0 happens. + * That should be sufficient (and even than, there would no really bad effect ;)). + * The variable below is the global counter/clock. + */ +#ifdef HAVE_ATOMIC_BUILTINS64 +static uint64 clockFileAccess = 0; +#else +static unsigned clockFileAccess = 0; +#endif +/* and the "tick" function */ +#ifndef HAVE_ATOMIC_BUILTINS +static pthread_mutex_t mutClock; +#endif +static uint64 +getClockFileAccess(void) +{ +#ifdef HAVE_ATOMIC_BUILTINS64 + return ATOMIC_INC_AND_FETCH_uint64(&clockFileAccess, &mutClock); +#else + return ATOMIC_INC_AND_FETCH_unsigned(&clockFileAccess, &mutClock); +#endif +} + + +/* The following structure is a dynafile name cache entry. + */ +struct s_dynaFileCacheEntry { + uchar *pName; /* name currently open, if dynamic name */ + strm_t *pStrm; /* our output stream */ + void *sigprovFileData; /* opaque data ptr for provider use */ + uint64 clkTickAccessed;/* for LRU - based on clockFileAccess */ + short nInactive; /* number of minutes not writen - for close timeout */ +}; +typedef struct s_dynaFileCacheEntry dynaFileCacheEntry; + + +#define IOBUF_DFLT_SIZE 4096 /* default size for io buffers */ +#define FLUSH_INTRVL_DFLT 1 /* default buffer flush interval (in seconds) */ +#define USE_ASYNCWRITER_DFLT 0 /* default buffer use async writer */ +#define FLUSHONTX_DFLT 1 /* default for flush on TX end */ + + +typedef struct _instanceData { + pthread_mutex_t mutWrite; /* guard against multiple instances writing to single file */ + uchar *fname; /* file or template name (display only) */ + uchar *tplName; /* name of assigned template */ + strm_t *pStrm; /* our output stream */ + short nInactive; /* number of minutes not writen (STATIC files only) */ + char bDynamicName; /* 0 - static name, 1 - dynamic name (with properties) */ + int isDevNull; /* do we "write" to /dev/null? - if so, do nothing */ + int fCreateMode; /* file creation mode for open() */ + int fDirCreateMode; /* creation mode for mkdir() */ + int bCreateDirs; /* auto-create directories? */ + int bSyncFile; /* should the file by sync()'ed? 1- yes, 0- no */ + uint8_t iNumTpls; /* number of tpls we use */ + uid_t fileUID; /* IDs for creation */ + uid_t dirUID; + gid_t fileGID; + gid_t dirGID; + int bFailOnChown; /* fail creation if chown fails? */ + uchar *sigprovName; /* signature provider */ + uchar *sigprovNameFull;/* full internal signature provider name */ + sigprov_if_t sigprov; /* ptr to signature provider interface */ + void *sigprovData; /* opaque data ptr for provider use */ + void *sigprovFileData;/* opaque data ptr for file instance */ + sbool useSigprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ + uchar *cryprovName; /* crypto provider */ + uchar *cryprovNameFull;/* full internal crypto provider name */ + void *cryprovData; /* opaque data ptr for provider use */ + cryprov_if_t cryprov; /* ptr to crypto provider interface */ + sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ + int iCurrElt; /* currently active cache element (-1 = none) */ + int iCurrCacheSize; /* currently cache size (1-based) */ + int iDynaFileCacheSize; /* size of file handle cache */ + /* The cache is implemented as an array. An empty element is indicated + * by a NULL pointer. Memory is allocated as needed. The following + * pointer points to the overall structure. + */ + dynaFileCacheEntry **dynCache; + off_t iSizeLimit; /* file size limit, 0 = no limit */ + uchar *pszSizeLimitCmd; /* command to carry out when size limit is reached */ + int iZipLevel; /* zip mode to use for this selector */ + int iIOBufSize; /* size of associated io buffer */ + int iFlushInterval; /* how fast flush buffer on inactivity? */ + short iCloseTimeout; /* after how many *minutes* shall the file be closed if inactive? */ + sbool bFlushOnTXEnd; /* flush write buffers when transaction has ended? */ + sbool bUseAsyncWriter; /* use async stream writer? */ + sbool bVeryRobustZip; + statsobj_t *stats; /* dynafile, primarily cache stats */ + STATSCOUNTER_DEF(ctrRequests, mutCtrRequests); + STATSCOUNTER_DEF(ctrLevel0, mutCtrLevel0); + STATSCOUNTER_DEF(ctrEvict, mutCtrEvict); + STATSCOUNTER_DEF(ctrMiss, mutCtrMiss); + STATSCOUNTER_DEF(ctrMax, mutCtrMax); + STATSCOUNTER_DEF(ctrCloseTimeouts, mutCtrCloseTimeouts); + char janitorID[128]; /* holds ID for janitor calls */ +} instanceData; + + +typedef struct wrkrInstanceData { + instanceData *pData; +} wrkrInstanceData_t; + + +typedef struct configSettings_s { + int iDynaFileCacheSize; /* max cache for dynamic files */ + int fCreateMode; /* mode to use when creating files */ + int fDirCreateMode; /* mode to use when creating files */ + int bFailOnChown; /* fail if chown fails? */ + uid_t fileUID; /* UID to be used for newly created files */ + uid_t fileGID; /* GID to be used for newly created files */ + uid_t dirUID; /* UID to be used for newly created directories */ + uid_t dirGID; /* GID to be used for newly created directories */ + int bCreateDirs;/* auto-create directories for dynaFiles: 0 - no, 1 - yes */ + int bEnableSync;/* enable syncing of files (no dash in front of pathname in conf): 0 - no, 1 - yes */ + int iZipLevel; /* zip compression mode (0..9 as usual) */ + sbool bFlushOnTXEnd;/* flush write buffers when transaction has ended? */ + int64 iIOBufSize; /* size of an io buffer */ + int iFlushInterval; /* how often flush the output buffer on inactivity? */ + int bUseAsyncWriter; /* should we enable asynchronous writing? */ + EMPTY_STRUCT +} configSettings_t; +static configSettings_t cs; +uchar *pszFileDfltTplName; /* name of the default template to use */ + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + uchar *tplName; /* default template */ + int fCreateMode; /* default mode to use when creating files */ + int fDirCreateMode; /* default mode to use when creating files */ + uid_t fileUID; /* default IDs for creation */ + uid_t dirUID; + gid_t fileGID; + gid_t dirGID; + int bDynafileDoNotSuspend; + strm_compressionDriver_t compressionDriver; + int compressionDriver_workers; +}; + +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 */ +/* module-global parameters */ +static struct cnfparamdescr modpdescr[] = { + { "template", eCmdHdlrGetWord, 0 }, + { "compression.driver", eCmdHdlrGetWord, 0 }, + { "compression.zstd.workers", eCmdHdlrPositiveInt, 0 }, + { "dircreatemode", eCmdHdlrFileCreateMode, 0 }, + { "filecreatemode", eCmdHdlrFileCreateMode, 0 }, + { "dirowner", eCmdHdlrUID, 0 }, + { "dirownernum", eCmdHdlrInt, 0 }, + { "dirgroup", eCmdHdlrGID, 0 }, + { "dirgroupnum", eCmdHdlrInt, 0 }, + { "fileowner", eCmdHdlrUID, 0 }, + { "fileownernum", eCmdHdlrInt, 0 }, + { "filegroup", eCmdHdlrGID, 0 }, + { "dynafile.donotsuspend", eCmdHdlrBinary, 0 }, + { "filegroupnum", eCmdHdlrInt, 0 }, +}; +static struct cnfparamblk modpblk = + { CNFPARAMBLK_VERSION, + sizeof(modpdescr)/sizeof(struct cnfparamdescr), + modpdescr + }; + +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "dynafilecachesize", eCmdHdlrInt, 0 }, /* legacy: dynafilecachesize */ + { "ziplevel", eCmdHdlrInt, 0 }, /* legacy: omfileziplevel */ + { "flushinterval", eCmdHdlrInt, 0 }, /* legacy: omfileflushinterval */ + { "asyncwriting", eCmdHdlrBinary, 0 }, /* legacy: omfileasyncwriting */ + { "veryrobustzip", eCmdHdlrBinary, 0 }, + { "flushontxend", eCmdHdlrBinary, 0 }, /* legacy: omfileflushontxend */ + { "iobuffersize", eCmdHdlrSize, 0 }, /* legacy: omfileiobuffersize */ + { "dirowner", eCmdHdlrUID, 0 }, /* legacy: dirowner */ + { "dirownernum", eCmdHdlrInt, 0 }, /* legacy: dirownernum */ + { "dirgroup", eCmdHdlrGID, 0 }, /* legacy: dirgroup */ + { "dirgroupnum", eCmdHdlrInt, 0 }, /* legacy: dirgroupnum */ + { "fileowner", eCmdHdlrUID, 0 }, /* legacy: fileowner */ + { "fileownernum", eCmdHdlrInt, 0 }, /* legacy: fileownernum */ + { "filegroup", eCmdHdlrGID, 0 }, /* legacy: filegroup */ + { "filegroupnum", eCmdHdlrInt, 0 }, /* legacy: filegroupnum */ + { "dircreatemode", eCmdHdlrFileCreateMode, 0 }, /* legacy: dircreatemode */ + { "filecreatemode", eCmdHdlrFileCreateMode, 0 }, /* legacy: filecreatemode */ + { "failonchownfailure", eCmdHdlrBinary, 0 }, /* legacy: failonchownfailure */ + { "createdirs", eCmdHdlrBinary, 0 }, /* legacy: createdirs */ + { "sync", eCmdHdlrBinary, 0 }, /* legacy: actionfileenablesync */ + { "file", eCmdHdlrString, 0 }, /* either "file" or ... */ + { "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */ + { "sig.provider", eCmdHdlrGetWord, 0 }, + { "cry.provider", eCmdHdlrGetWord, 0 }, + { "closetimeout", eCmdHdlrPositiveInt, 0 }, + { "rotation.sizelimit", eCmdHdlrSize, 0 }, + { "rotation.sizelimitcommand", eCmdHdlrString, 0 }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + + +/* this function gets the default template. It coordinates action between + * old-style and new-style configuration parts. + */ +static uchar* +getDfltTpl(void) +{ + if(loadModConf != NULL && loadModConf->tplName != NULL) + return loadModConf->tplName; + else if(pszFileDfltTplName == NULL) + return (uchar*)"RSYSLOG_FileFormat"; + else + return pszFileDfltTplName; +} + + +BEGINinitConfVars /* (re)set config variables to default values */ +CODESTARTinitConfVars + pszFileDfltTplName = NULL; /* make sure this can be free'ed! */ + iRet = resetConfigVariables(NULL, NULL); /* params are dummies */ +ENDinitConfVars + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + if(pData->bDynamicName) { + dbgprintf("[dynamic]\n"); + } else { /* regular file */ + dbgprintf("%s%s\n", pData->fname, + (pData->pStrm == NULL) ? " (closed)" : ""); + } + + dbgprintf("\ttemplate='%s'\n", pData->fname); + dbgprintf("\tuse async writer=%d\n", pData->bUseAsyncWriter); + dbgprintf("\tflush on TX end=%d\n", pData->bFlushOnTXEnd); + dbgprintf("\tflush interval=%d\n", pData->iFlushInterval); + dbgprintf("\tfile cache size=%d\n", pData->iDynaFileCacheSize); + dbgprintf("\tcreate directories: %s\n", pData->bCreateDirs ? "on" : "off"); + dbgprintf("\tvery robust zip: %s\n", pData->bCreateDirs ? "on" : "off"); + dbgprintf("\tfile owner %d, group %d\n", (int) pData->fileUID, (int) pData->fileGID); + dbgprintf("\tdirectory owner %d, group %d\n", (int) pData->dirUID, (int) pData->dirGID); + dbgprintf("\tdir create mode 0%3.3o, file create mode 0%3.3o\n", + pData->fDirCreateMode, pData->fCreateMode); + dbgprintf("\tfail if owner/group can not be set: %s\n", pData->bFailOnChown ? "yes" : "no"); +ENDdbgPrintInstInfo + + + +/* set the default template to be used + * This is a module-global parameter, and as such needs special handling. It needs to + * be coordinated with values set via the v2 config system (rsyslog v6+). What we do + * is we do not permit this directive after the v2 config system has been used to set + * the parameter. + */ +static rsRetVal +setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal) +{ + DEFiRet; + + if(loadModConf != NULL && loadModConf->tplName != NULL) { + free(newVal); + parser_errmsg("omfile: default template already set via module " + "global parameter - can no longer be changed"); + ABORT_FINALIZE(RS_RET_ERR); + } + free(pszFileDfltTplName); + pszFileDfltTplName = newVal; +finalize_it: + RETiRet; +} + + +/* set the dynaFile cache size. Does some limit checking. + * rgerhards, 2007-07-31 + */ +static rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal) +{ + DEFiRet; + + if(iNewVal < 1) { + errno = 0; + parser_errmsg( + "DynaFileCacheSize must be greater 0 (%d given), changed to 1.", iNewVal); + iRet = RS_RET_VAL_OUT_OF_RANGE; + iNewVal = 1; + } else if(iNewVal > 25000) { + errno = 0; + parser_warnmsg("DynaFileCacheSize is larger than 25,000 (%d given) - this looks very " + "large. Is it intended?", iNewVal); + } + + cs.iDynaFileCacheSize = iNewVal; + DBGPRINTF("DynaFileCacheSize changed to %d.\n", iNewVal); + + RETiRet; +} + + +/* Helper to cfline(). Parses a output channel name up until the first + * comma and then looks for the template specifier. Tries + * to find that template. Maps the output channel to the + * proper filed structure settings. Everything is stored in the + * filed struct. Over time, the dependency on filed might be + * removed. + * rgerhards 2005-06-21 + */ +static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringRequest_t *pOMSR, + int iEntry, int iTplOpts) +{ + DEFiRet; + size_t i; + struct outchannel *pOch; + char szBuf[128]; /* should be more than sufficient */ + + ++p; /* skip '$' */ + i = 0; + /* get outchannel name */ + while(*p && *p != ';' && *p != ' ' && + i < (sizeof(szBuf) - 1) ) { + szBuf[i++] = *p++; + } + szBuf[i] = '\0'; + + /* got the name, now look up the channel... */ + pOch = ochFind(szBuf, i); + + if(pOch == NULL) { + parser_errmsg( + "outchannel '%s' not found - ignoring action line", + szBuf); + ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + + /* check if there is a file name in the outchannel... */ + if(pOch->pszFileTemplate == NULL) { + parser_errmsg( + "outchannel '%s' has no file name template - ignoring action line", + szBuf); + ABORT_FINALIZE(RS_RET_ERR); + } + + /* OK, we finally got a correct template. So let's use it... */ + pData->fname = ustrdup(pOch->pszFileTemplate); + pData->iSizeLimit = pOch->uSizeLimit; + /* WARNING: It is dangerous "just" to pass the pointer. As we + * never rebuild the output channel description, this is acceptable here. + */ + pData->pszSizeLimitCmd = pOch->cmdOnSizeLimit; + + iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts, getDfltTpl()); + +finalize_it: + RETiRet; +} + + +/* This function deletes an entry from the dynamic file name + * cache. A pointer to the cache must be passed in as well + * as the index of the to-be-deleted entry. This index may + * point to an unallocated entry, in whcih case the + * function immediately returns. Parameter bFreeEntry is 1 + * if the entry should be free()ed and 0 if not. + */ +static rsRetVal +dynaFileDelCacheEntry(instanceData *__restrict__ const pData, const int iEntry, const int bFreeEntry) +{ + dynaFileCacheEntry **pCache = pData->dynCache; + DEFiRet; + assert(pCache != NULL); + + if(pCache[iEntry] == NULL) + FINALIZE; + + DBGPRINTF("Removing entry %d for file '%s' from dynaCache.\n", iEntry, + pCache[iEntry]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[iEntry]->pName); + + if(pCache[iEntry]->pName != NULL) { + free(pCache[iEntry]->pName); + pCache[iEntry]->pName = NULL; + } + + if(pCache[iEntry]->pStrm != NULL) { + if(iEntry == pData->iCurrElt) { + pData->iCurrElt = -1; + pData->pStrm = NULL; + } + strm.Destruct(&pCache[iEntry]->pStrm); + if(pData->useSigprov) { + pData->sigprov.OnFileClose(pCache[iEntry]->sigprovFileData); + pCache[iEntry]->sigprovFileData = NULL; + } + } + + if(bFreeEntry) { + free(pCache[iEntry]); + pCache[iEntry] = NULL; + } + +finalize_it: + RETiRet; +} + + +/* This function frees all dynamic file name cache entries and closes the + * relevant files. Part of Shutdown and HUP processing. + * rgerhards, 2008-10-23 + */ +static void +dynaFileFreeCacheEntries(instanceData *__restrict__ const pData) +{ + register int i; + assert(pData != NULL); + + for(i = 0 ; i < pData->iCurrCacheSize ; ++i) { + dynaFileDelCacheEntry(pData, i, 1); + } + /* invalidate current element */ + pData->iCurrElt = -1; + pData->pStrm = NULL; +} + + +/* This function frees the dynamic file name cache. + */ +static void dynaFileFreeCache(instanceData *__restrict__ const pData) +{ + assert(pData != NULL); + + dynaFileFreeCacheEntries(pData); + if(pData->dynCache != NULL) + free(pData->dynCache); +} + + +/* close current file */ +static rsRetVal +closeFile(instanceData *__restrict__ const pData) +{ + DEFiRet; + if(pData->useSigprov) { + pData->sigprov.OnFileClose(pData->sigprovFileData); + pData->sigprovFileData = NULL; + } + strm.Destruct(&pData->pStrm); + RETiRet; +} + + +/* This prepares the signature provider to process a file */ +static rsRetVal +sigprovPrepare(instanceData *__restrict__ const pData, uchar *__restrict__ const fn) +{ + DEFiRet; + pData->sigprov.OnFileOpen(pData->sigprovData, fn, &pData->sigprovFileData); + RETiRet; +} + +/* This is now shared code for all types of files. It simply prepares + * file access, which, among others, means the the file wil be opened + * and any directories in between will be created (based on config, of + * course). -- rgerhards, 2008-10-22 + * changed to iRet interface - 2009-03-19 + */ +static rsRetVal +prepareFile(instanceData *__restrict__ const pData, const uchar *__restrict__ const newFileName) +{ + int fd; + char errStr[1024]; /* buffer for strerr() */ + DEFiRet; + + pData->pStrm = NULL; + if(access((char*)newFileName, F_OK) != 0) { + /* file does not exist, create it (and eventually parent directories */ + if(pData->bCreateDirs) { + /* We first need to create parent dirs if they are missing. + * We do not report any errors here ourselfs but let the code + * fall through to error handler below. + */ + if(makeFileParentDirs(newFileName, ustrlen(newFileName), + pData->fDirCreateMode, pData->dirUID, + pData->dirGID, pData->bFailOnChown) != 0) { + rs_strerror_r(errno, errStr, sizeof(errStr)); + parser_errmsg( "omfile: creating parent " + "directories for file '%s' failed: %s", + newFileName, errStr); + ABORT_FINALIZE(RS_RET_ERR); /* we give up */ + } + } + /* no matter if we needed to create directories or not, we now try to create + * the file. -- rgerhards, 2008-12-18 (based on patch from William Tisater) + */ + fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC, + pData->fCreateMode); + if(fd != -1) { + /* check and set uid/gid */ + if(pData->fileUID != (uid_t)-1 || pData->fileGID != (gid_t) -1) { + /* we need to set owner/group */ + if(fchown(fd, pData->fileUID, pData->fileGID) != 0) { + rs_strerror_r(errno, errStr, sizeof(errStr)); + parser_errmsg( + "omfile: chown for file '%s' failed: %s", + newFileName, errStr); + if(pData->bFailOnChown) { + close(fd); + ABORT_FINALIZE(RS_RET_ERR); /* we give up */ + } + /* we will silently ignore the chown() failure + * if configured to do so. + */ + } + } + close(fd); /* close again, as we need a stream further on */ + } + } + + /* the copies below are clumpsy, but there is no way around given the + * anomalies in dirname() and basename() [they MODIFY the provided buffer...] + */ + uchar szNameBuf[MAXFNAME+1]; + uchar szDirName[MAXFNAME+1]; + uchar szBaseName[MAXFNAME+1]; + ustrncpy(szNameBuf, newFileName, MAXFNAME); + szNameBuf[MAXFNAME] = '\0'; + ustrncpy(szDirName, (uchar*)dirname((char*)szNameBuf), MAXFNAME); + szDirName[MAXFNAME] = '\0'; + ustrncpy(szNameBuf, newFileName, MAXFNAME); + szNameBuf[MAXFNAME] = '\0'; + ustrncpy(szBaseName, (uchar*)basename((char*)szNameBuf), MAXFNAME); + szBaseName[MAXFNAME] = '\0'; + + CHKiRet(strm.Construct(&pData->pStrm)); + CHKiRet(strm.SetFName(pData->pStrm, szBaseName, ustrlen(szBaseName))); + CHKiRet(strm.SetDir(pData->pStrm, szDirName, ustrlen(szDirName))); + CHKiRet(strm.SetiZipLevel(pData->pStrm, pData->iZipLevel)); + CHKiRet(strm.SetbVeryReliableZip(pData->pStrm, pData->bVeryRobustZip)); + CHKiRet(strm.SetsIOBufSize(pData->pStrm, (size_t) pData->iIOBufSize)); + CHKiRet(strm.SettOperationsMode(pData->pStrm, STREAMMODE_WRITE_APPEND)); + CHKiRet(strm.SettOpenMode(pData->pStrm, cs.fCreateMode)); + CHKiRet(strm.SetcompressionDriver(pData->pStrm, runModConf->compressionDriver)); + CHKiRet(strm.SetCompressionWorkers(pData->pStrm, runModConf->compressionDriver_workers)); + CHKiRet(strm.SetbSync(pData->pStrm, pData->bSyncFile)); + CHKiRet(strm.SetsType(pData->pStrm, STREAMTYPE_FILE_SINGLE)); + CHKiRet(strm.SetiSizeLimit(pData->pStrm, pData->iSizeLimit)); + if(pData->useCryprov) { + CHKiRet(strm.Setcryprov(pData->pStrm, &pData->cryprov)); + CHKiRet(strm.SetcryprovData(pData->pStrm, pData->cryprovData)); + } + /* set the flush interval only if we actually use it - otherwise it will activate + * async processing, which is a real performance waste if we do not do buffered + * writes! -- rgerhards, 2009-07-06 + */ + if(pData->bUseAsyncWriter) + CHKiRet(strm.SetiFlushInterval(pData->pStrm, pData->iFlushInterval)); + if(pData->pszSizeLimitCmd != NULL) + CHKiRet(strm.SetpszSizeLimitCmd(pData->pStrm, ustrdup(pData->pszSizeLimitCmd))); + CHKiRet(strm.ConstructFinalize(pData->pStrm)); + + if(pData->useSigprov) + sigprovPrepare(pData, szNameBuf); + +finalize_it: + if(iRet != RS_RET_OK) { + if(pData->pStrm != NULL) { + closeFile(pData); + } + } + RETiRet; +} + + +/* This function handles dynamic file names. It checks if the + * requested file name is already open and, if not, does everything + * needed to switch to the it. + * Function returns 0 if all went well and non-zero otherwise. + * On successful return pData->fd must point to the correct file to + * be written. + * This is a helper to writeFile(). rgerhards, 2007-07-03 + */ +static rsRetVal ATTR_NONNULL() +prepareDynFile(instanceData *__restrict__ const pData, const uchar *__restrict__ const newFileName) +{ + uint64 ctOldest; /* "timestamp" of oldest element */ + int iOldest; + int i; + int iFirstFree; + rsRetVal localRet; + dynaFileCacheEntry **pCache; + DEFiRet; + + assert(pData != NULL); + assert(newFileName != NULL); + + pCache = pData->dynCache; + + /* first check, if we still have the current file */ + if( (pData->iCurrElt != -1) + && !ustrcmp(newFileName, pCache[pData->iCurrElt]->pName)) { + /* great, we are all set */ + pCache[pData->iCurrElt]->clkTickAccessed = getClockFileAccess(); + STATSCOUNTER_INC(pData->ctrLevel0, pData->mutCtrLevel0); + /* LRU needs only a strictly monotonically increasing counter, so such a one could do */ + FINALIZE; + } + + /* ok, no luck - current file cannot be re-used */ + + /* if we need to flush (at least) on TXEnd, we need to flush now - because + * we do not know if we will otherwise come back to this file to flush it + * at end of TX. see https://github.com/rsyslog/rsyslog/issues/2502 + */ + if(((runModConf->pConf->globals.glblDevOptions & DEV_OPTION_8_1905_HANG_TEST) == 0) && + pData->bFlushOnTXEnd && pData->pStrm != NULL) { + CHKiRet(strm.Flush(pData->pStrm)); + } + + /* Now let's search the table if we find a matching spot. + * While doing so, we also prepare for creation of a new one. + */ + pData->iCurrElt = -1; /* invalid current element pointer */ + iFirstFree = -1; /* not yet found */ + iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */ + ctOldest = getClockFileAccess(); /* there must always be an older one */ + for(i = 0 ; i < pData->iCurrCacheSize ; ++i) { + if(pCache[i] == NULL || pCache[i]->pName == NULL) { + if(iFirstFree == -1) + iFirstFree = i; + } else { /* got an element, let's see if it matches */ + if(!ustrcmp(newFileName, pCache[i]->pName)) { + /* we found our element! */ + pData->pStrm = pCache[i]->pStrm; + if(pData->useSigprov) + pData->sigprovFileData = pCache[i]->sigprovFileData; + pData->iCurrElt = i; + pCache[i]->clkTickAccessed = getClockFileAccess(); /* update "timestamp" for LRU */ + FINALIZE; + } + /* did not find it - so lets keep track of the counters for LRU */ + if(pCache[i]->clkTickAccessed < ctOldest) { + ctOldest = pCache[i]->clkTickAccessed; + iOldest = i; + } + } + } + + /* we have not found an entry */ + STATSCOUNTER_INC(pData->ctrMiss, pData->mutCtrMiss); + + /* similarly, we need to set the current pStrm to NULL, because otherwise, if prepareFile() fails, + * we may end up using an old stream. This bug depends on how exactly prepareFile fails, + * but it could be triggered in the common case of a failed open() system call. + * rgerhards, 2010-03-22 + */ + pData->pStrm = NULL, pData->sigprovFileData = NULL; + + if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) { + /* there is space left, so set it to that index */ + iFirstFree = pData->iCurrCacheSize++; + STATSCOUNTER_SETMAX_NOMUT(pData->ctrMax, (unsigned) pData->iCurrCacheSize); + } + + /* Note that the following code sequence does not work with the cache entry itself, + * but rather with pData->pStrm, the (sole) stream pointer in the non-dynafile case. + * The cache array is only updated after the open was successful. -- rgerhards, 2010-03-21 + */ + if(iFirstFree == -1) { + dynaFileDelCacheEntry(pData, iOldest, 0); + STATSCOUNTER_INC(pData->ctrEvict, pData->mutCtrEvict); + iFirstFree = iOldest; /* this one *is* now free ;) */ + } else { + /* we need to allocate memory for the cache structure */ + CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry))); + } + + /* Ok, we finally can open the file */ + localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */ + + /* check if we had an error */ + if(localRet != RS_RET_OK) { + /* We do no longer care about internal messages. The errmsg rate limiter + * will take care of too-frequent error messages. + */ + parser_errmsg("Could not open dynamic file '%s' [state %d] - discarding " + "message", newFileName, localRet); + ABORT_FINALIZE(localRet); + } + + if((pCache[iFirstFree]->pName = ustrdup(newFileName)) == NULL) { + closeFile(pData); /* need to free failed entry! */ + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + pCache[iFirstFree]->pStrm = pData->pStrm; + if(pData->useSigprov) + pCache[iFirstFree]->sigprovFileData = pData->sigprovFileData; + pCache[iFirstFree]->clkTickAccessed = getClockFileAccess(); + pData->iCurrElt = iFirstFree; + DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName); + +finalize_it: + if(iRet == RS_RET_OK) + pCache[pData->iCurrElt]->nInactive = 0; + RETiRet; +} + + +/* do the actual write process. This function is to be called once we are ready for writing. + * It will do buffered writes and persist data only when the buffer is full. Note that we must + * be careful to detect when the file handle changed. + * rgerhards, 2009-06-03 + */ +static rsRetVal +doWrite(instanceData *__restrict__ const pData, uchar *__restrict__ const pszBuf, const int lenBuf) +{ + DEFiRet; + assert(pData != NULL); + assert(pszBuf != NULL); + + DBGPRINTF("omfile: write to stream, pData->pStrm %p, lenBuf %d, strt data %.128s\n", + pData->pStrm, lenBuf, pszBuf); + if(pData->pStrm != NULL){ + CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf)); + if(pData->useSigprov) { + CHKiRet(pData->sigprov.OnRecordWrite(pData->sigprovFileData, pszBuf, lenBuf)); + } + } + +finalize_it: + RETiRet; +} + + +/* rgerhards 2004-11-11: write to a file output. */ +static rsRetVal +writeFile(instanceData *__restrict__ const pData, + const actWrkrIParams_t *__restrict__ const pParam, + const int iMsg) +{ + DEFiRet; + + STATSCOUNTER_INC(pData->ctrRequests, pData->mutCtrRequests); + /* first check if we have a dynamic file name and, if so, + * check if it still is ok or a new file needs to be created + */ + if(pData->bDynamicName) { + DBGPRINTF("omfile: file to log to: %s\n", + actParam(pParam, pData->iNumTpls, iMsg, 1).param); + CHKiRet(prepareDynFile(pData, actParam(pParam, pData->iNumTpls, iMsg, 1).param)); + } else { /* "regular", non-dynafile */ + if(pData->pStrm == NULL) { + CHKiRet(prepareFile(pData, pData->fname)); + if(pData->pStrm == NULL) { + parser_errmsg( + "Could not open output file '%s'", pData->fname); + } + } + pData->nInactive = 0; + } + + iRet = doWrite(pData, actParam(pParam, pData->iNumTpls, iMsg, 0).param, + actParam(pParam, pData->iNumTpls, iMsg, 0).lenStr); + +finalize_it: + RETiRet; +} + + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + pModConf->tplName = NULL; + pModConf->fCreateMode = 0644; + pModConf->fDirCreateMode = 0700; + pModConf->fileUID = -1; + pModConf->dirUID = -1; + pModConf->fileGID = -1; + pModConf->dirGID = -1; + pModConf->bDynafileDoNotSuspend = 1; +ENDbeginCnfLoad + +BEGINsetModCnf + struct cnfparamvals *pvals = NULL; + int i; +CODESTARTsetModCnf + pvals = nvlstGetParams(lst, &modpblk, NULL); + if(pvals == NULL) { + parser_errmsg("error processing module " + "config parameters [module(...)]"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("module (global) param blk for omfile:\n"); + cnfparamsPrint(&modpblk, pvals); + } + + for(i = 0 ; i < modpblk.nParams ; ++i) { + if(!pvals[i].bUsed) { + continue; + } + + if(!strcmp(modpblk.descr[i].name, "template")) { + loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + if(pszFileDfltTplName != NULL) { + parser_errmsg("omfile: warning: default template was already " + "set via legacy directive - may lead to inconsistent " + "results."); + } + } else if(!strcmp(modpblk.descr[i].name, "compression.driver")) { + if(!es_strcasebufcmp(pvals[i].val.d.estr, (const unsigned char*) "zlib", 4)) { + loadModConf->compressionDriver = STRM_COMPRESS_ZIP; + } else if(!es_strcasebufcmp(pvals[i].val.d.estr, (const unsigned char*) "zstd", 4)) { + loadModConf->compressionDriver = STRM_COMPRESS_ZSTD; + } else { + parser_errmsg("omfile: error: invalid compression.driver driver " + "name - noch applying setting. Valid drivers: 'zlib' and " + "'zstd'."); + } + } else if(!strcmp(modpblk.descr[i].name, "compression.zstd.workers")) { + loadModConf->compressionDriver_workers = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "dircreatemode")) { + loadModConf->fDirCreateMode = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "filecreatemode")) { + loadModConf->fCreateMode = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "dirowner")) { + loadModConf->dirUID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "dirownernum")) { + loadModConf->dirUID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "dirgroup")) { + loadModConf->dirGID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "dirgroupnum")) { + loadModConf->dirGID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "fileowner")) { + loadModConf->fileUID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "fileownernum")) { + loadModConf->fileUID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "filegroup")) { + loadModConf->fileGID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "filegroupnum")) { + loadModConf->fileGID = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "dynafile.donotsuspend")) { + loadModConf->bDynafileDoNotSuspend = (int) pvals[i].val.d.n; + } else { + dbgprintf("omfile: program error, non-handled " + "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); + } + } +finalize_it: + if(pvals != NULL) + cnfparamvalsDestruct(pvals, &modpblk); +ENDsetModCnf + +/* This function checks dynafile cache for janitor action */ +static void +janitorChkDynaFiles(instanceData *__restrict__ const pData) +{ + int i; + dynaFileCacheEntry **pCache = pData->dynCache; + + for(i = 0 ; i < pData->iCurrCacheSize ; ++i) { + if(pCache[i] == NULL) + continue; + DBGPRINTF("omfile janitor: checking dynafile %d:%s, inactive since %d\n", i, + pCache[i]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[i]->pName, + (int) pCache[i]->nInactive); + if(pCache[i]->nInactive >= pData->iCloseTimeout) { + STATSCOUNTER_INC(pData->ctrCloseTimeouts, pData->mutCtrCloseTimeouts); + dynaFileDelCacheEntry(pData, i, 1); + if(pData->iCurrElt == i) + pData->iCurrElt = -1; /* no longer available! */ + } else { + pCache[i]->nInactive += runModConf->pConf->globals.janitorInterval; + } + } +} + +/* callback for the janitor. This cleans out files (if so configured) */ +static void +janitorCB(void *pUsr) +{ + instanceData *__restrict__ const pData = (instanceData *) pUsr; + pthread_mutex_lock(&pData->mutWrite); + if(pData->bDynamicName) { + janitorChkDynaFiles(pData); + } else { + if(pData->pStrm != NULL) { + DBGPRINTF("omfile janitor: checking file %s, inactive since %d\n", + pData->fname, pData->nInactive); + if(pData->nInactive >= pData->iCloseTimeout) { + STATSCOUNTER_INC(pData->ctrCloseTimeouts, pData->mutCtrCloseTimeouts); + closeFile(pData); + } else { + pData->nInactive += runModConf->pConf->globals.janitorInterval; + } + } + } + pthread_mutex_unlock(&pData->mutWrite); +} + + +BEGINendCnfLoad +CODESTARTendCnfLoad + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(pszFileDfltTplName); + pszFileDfltTplName = NULL; +ENDendCnfLoad + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + +BEGINactivateCnf +CODESTARTactivateCnf + runModConf = pModConf; +ENDactivateCnf + +BEGINfreeCnf +CODESTARTfreeCnf + free(pModConf->tplName); +ENDfreeCnf + + +BEGINcreateInstance +CODESTARTcreateInstance + pData->pStrm = NULL; + pthread_mutex_init(&pData->mutWrite, NULL); +ENDcreateInstance + + +BEGINcreateWrkrInstance +CODESTARTcreateWrkrInstance +ENDcreateWrkrInstance + + +BEGINfreeInstance +CODESTARTfreeInstance + free(pData->tplName); + free(pData->fname); + if(pData->iCloseTimeout > 0) + janitorDelEtry(pData->janitorID); + if(pData->bDynamicName) { + dynaFileFreeCache(pData); + } else if(pData->pStrm != NULL) + closeFile(pData); + if(pData->stats != NULL) + statsobj.Destruct(&(pData->stats)); + if(pData->useSigprov) { + pData->sigprov.Destruct(&pData->sigprovData); + obj.ReleaseObj(__FILE__, pData->sigprovNameFull+2, pData->sigprovNameFull, + (void*) &pData->sigprov); + free(pData->sigprovName); + free(pData->sigprovNameFull); + } + if(pData->useCryprov) { + pData->cryprov.Destruct(&pData->cryprovData); + obj.ReleaseObj(__FILE__, pData->cryprovNameFull+2, pData->cryprovNameFull, + (void*) &pData->cryprov); + free(pData->cryprovName); + free(pData->cryprovNameFull); + } + pthread_mutex_destroy(&pData->mutWrite); +ENDfreeInstance + + +BEGINfreeWrkrInstance +CODESTARTfreeWrkrInstance +ENDfreeWrkrInstance + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +BEGINbeginTransaction +CODESTARTbeginTransaction + /* we have nothing to do to begin a transaction */ +ENDbeginTransaction + + +BEGINcommitTransaction + instanceData *__restrict__ const pData = pWrkrData->pData; + unsigned i; +CODESTARTcommitTransaction + + if(pData->isDevNull) { + goto terminate; + } + + pthread_mutex_lock(&pData->mutWrite); + + for(i = 0 ; i < nParams ; ++i) { + writeFile(pData, pParams, i); + } + /* Note: pStrm may be NULL if there was an error opening the stream */ + /* if bFlushOnTXEnd is set, we need to flush on transaction end - in + * any case. It is not relevant if this is using background writes + * (which then become pretty slow) or not. And, similarly, no flush + * happens when it is not set. Please see + * https://github.com/rsyslog/rsyslog/issues/1297 + * for a discussion of why we actually need this. + * rgerhards, 2017-01-13 + */ + if(pData->bFlushOnTXEnd && pData->pStrm != NULL) { + CHKiRet(strm.Flush(pData->pStrm)); + } + +finalize_it: + pthread_mutex_unlock(&pData->mutWrite); + if(iRet == RS_RET_FILE_OPEN_ERROR || iRet == RS_RET_FILE_NOT_FOUND) { + iRet = (pData->bDynamicName && runModConf->bDynafileDoNotSuspend) ? + RS_RET_OK : RS_RET_SUSPENDED; + } + +terminate: +ENDcommitTransaction + + +static void +setInstParamDefaults(instanceData *__restrict__ const pData) +{ + pData->fname = NULL; + pData->tplName = NULL; + pData->fileUID = loadModConf->fileUID; + pData->fileGID = loadModConf->fileGID; + pData->dirUID = loadModConf->dirUID; + pData->dirGID = loadModConf->dirGID; + pData->bFailOnChown = 1; + pData->iDynaFileCacheSize = 10; + pData->fCreateMode = loadModConf->fCreateMode; + pData->fDirCreateMode = loadModConf->fDirCreateMode; + pData->bCreateDirs = 1; + pData->bSyncFile = 0; + pData->iZipLevel = 0; + pData->bVeryRobustZip = 0; + pData->bFlushOnTXEnd = FLUSHONTX_DFLT; + pData->iIOBufSize = IOBUF_DFLT_SIZE; + pData->iFlushInterval = FLUSH_INTRVL_DFLT; + pData->bUseAsyncWriter = USE_ASYNCWRITER_DFLT; + pData->sigprovName = NULL; + pData->cryprovName = NULL; + pData->useSigprov = 0; + pData->useCryprov = 0; + pData->iCloseTimeout = -1; + pData->iSizeLimit = 0; + pData->isDevNull = 0; + pData->pszSizeLimitCmd = NULL; +} + + +static rsRetVal +setupInstStatsCtrs(instanceData *__restrict__ const pData) +{ + uchar ctrName[512]; + DEFiRet; + + if(!pData->bDynamicName) { + FINALIZE; + } + + /* support statistics gathering */ + snprintf((char*)ctrName, sizeof(ctrName), "dynafile cache %s", pData->fname); + ctrName[sizeof(ctrName)-1] = '\0'; /* be on the save side */ + CHKiRet(statsobj.Construct(&(pData->stats))); + CHKiRet(statsobj.SetName(pData->stats, ctrName)); + CHKiRet(statsobj.SetOrigin(pData->stats, (uchar*)"omfile")); + STATSCOUNTER_INIT(pData->ctrRequests, pData->mutCtrRequests); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("requests"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->ctrRequests))); + STATSCOUNTER_INIT(pData->ctrLevel0, pData->mutCtrLevel0); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("level0"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->ctrLevel0))); + STATSCOUNTER_INIT(pData->ctrMiss, pData->mutCtrMiss); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("missed"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->ctrMiss))); + STATSCOUNTER_INIT(pData->ctrEvict, pData->mutCtrEvict); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("evicted"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->ctrEvict))); + STATSCOUNTER_INIT(pData->ctrMax, pData->mutCtrMax); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("maxused"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->ctrMax))); + STATSCOUNTER_INIT(pData->ctrCloseTimeouts, pData->mutCtrCloseTimeouts); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("closetimeouts"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->ctrCloseTimeouts))); + CHKiRet(statsobj.ConstructFinalize(pData->stats)); + +finalize_it: + RETiRet; +} + +static void +initSigprov(instanceData *__restrict__ const pData, struct nvlst *lst) +{ + uchar szDrvrName[1024]; + + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmsig_%s", pData->sigprovName) + == sizeof(szDrvrName)) { + parser_errmsg("omfile: signature provider " + "name is too long: '%s' - signatures disabled", + pData->sigprovName); + goto done; + } + pData->sigprovNameFull = ustrdup(szDrvrName); + + pData->sigprov.ifVersion = sigprovCURR_IF_VERSION; + /* The pDrvrName+2 below is a hack to obtain the object name. It + * safes us to have yet another variable with the name without "lm" in + * front of it. If we change the module load interface, we may re-think + * about this hack, but for the time being it is efficient and clean enough. + */ + if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pData->sigprov) + != RS_RET_OK) { + parser_errmsg("omfile: could not load " + "signature provider '%s' - signatures disabled", + szDrvrName); + goto done; + } + + if(pData->sigprov.Construct(&pData->sigprovData) != RS_RET_OK) { + parser_errmsg("omfile: error constructing " + "signature provider %s dataset - signatures disabled", + szDrvrName); + goto done; + } + pData->sigprov.SetCnfParam(pData->sigprovData, lst); + + dbgprintf("loaded signature provider %s, data instance at %p\n", + szDrvrName, pData->sigprovData); + pData->useSigprov = 1; +done: return; +} + +static rsRetVal +initCryprov(instanceData *__restrict__ const pData, struct nvlst *lst) +{ + uchar szDrvrName[1024]; + DEFiRet; + + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pData->cryprovName) + == sizeof(szDrvrName)) { + parser_errmsg("omfile: crypto provider " + "name is too long: '%s' - encryption disabled", + pData->cryprovName); + ABORT_FINALIZE(RS_RET_ERR); + } + pData->cryprovNameFull = ustrdup(szDrvrName); + + pData->cryprov.ifVersion = cryprovCURR_IF_VERSION; + /* The pDrvrName+2 below is a hack to obtain the object name. It + * safes us to have yet another variable with the name without "lm" in + * front of it. If we change the module load interface, we may re-think + * about this hack, but for the time being it is efficient and clean enough. + */ + if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pData->cryprov) + != RS_RET_OK) { + parser_errmsg("omfile: could not load " + "crypto provider '%s' - encryption disabled", + szDrvrName); + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); + } + + if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) { + parser_errmsg("omfile: error constructing " + "crypto provider %s dataset - encryption disabled", + szDrvrName); + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); + } + CHKiRet(pData->cryprov.SetCnfParam(pData->cryprovData, lst, CRYPROV_PARAMTYPE_REGULAR)); + + dbgprintf("loaded crypto provider %s, data instance at %p\n", + szDrvrName, pData->cryprovData); + pData->useCryprov = 1; +finalize_it: + RETiRet; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + uchar *tplToUse; + int i; +CODESTARTnewActInst + DBGPRINTF("newActInst (omfile)\n"); + + pvals = nvlstGetParams(lst, &actpblk, NULL); + if(pvals == NULL) { + parser_errmsg("omfile: either the \"file\" or " + "\"dynafile\" parameter must be given"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("action param blk in omfile:\n"); + cnfparamsPrint(&actpblk, pvals); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "dynafilecachesize")) { + pData->iDynaFileCacheSize = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "ziplevel")) { + pData->iZipLevel = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "flushinterval")) { + pData->iFlushInterval = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "veryrobustzip")) { + pData->bVeryRobustZip = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "asyncwriting")) { + pData->bUseAsyncWriter = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "flushontxend")) { + pData->bFlushOnTXEnd = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "iobuffersize")) { + pData->iIOBufSize = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dirowner")) { + pData->dirUID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dirownernum")) { + pData->dirUID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dirgroup")) { + pData->dirGID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dirgroupnum")) { + pData->dirGID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "fileowner")) { + pData->fileUID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "fileownernum")) { + pData->fileUID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "filegroup")) { + pData->fileGID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "filegroupnum")) { + pData->fileGID = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dircreatemode")) { + pData->fDirCreateMode = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "filecreatemode")) { + pData->fCreateMode = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "failonchownfailure")) { + pData->bFailOnChown = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "sync")) { + pData->bSyncFile = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "createdirs")) { + pData->bCreateDirs = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "file")) { + pData->fname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + CODE_STD_STRING_REQUESTnewActInst(1) + pData->bDynamicName = 0; + } else if(!strcmp(actpblk.descr[i].name, "dynafile")) { + if(pData->fname != NULL) { + parser_errmsg("omfile: both \"file\" and \"dynafile\" set, will use dynafile"); + } + pData->fname = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + CODE_STD_STRING_REQUESTnewActInst(2) + pData->bDynamicName = 1; + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "sig.provider")) { + pData->sigprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "cry.provider")) { + pData->cryprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "closetimeout")) { + pData->iCloseTimeout = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "rotation.sizelimit")) { + pData->iSizeLimit = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "rotation.sizelimitcommand")) { + pData->pszSizeLimitCmd = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("omfile: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->fname == NULL || *pData->fname == '\0') { + parser_errmsg("omfile: either the \"file\" or " + "\"dynafile\" parameter must be given"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + int allWhiteSpace = 1; + for(const char *p = (const char*) pData->fname ; *p ; ++p) { + if(!isspace(*p)) { + allWhiteSpace = 0; + break; + } + } + if(allWhiteSpace) { + parser_errmsg("omfile: \"file\" or \"dynafile\" parameter " + "consist only of whitespace - this is not permitted"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(!strcmp((const char*) pData->fname, "/dev/null")) { + pData->isDevNull = 1; + } + + if(pData->sigprovName != NULL) { + initSigprov(pData, lst); + } + + if(pData->cryprovName != NULL) { + CHKiRet(initCryprov(pData, lst)); + } + + tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); + CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS)); + pData->iNumTpls = 1; + + if(pData->bDynamicName) { + /* "filename" is actually a template name, we need this as string 1. So let's add it + * to the pOMSR. -- rgerhards, 2007-07-27 + */ + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->fname), OMSR_NO_RQD_TPL_OPTS)); + pData->iNumTpls = 2; + // TODO: create unified code for this (legacy+v6 system) + /* we now allocate the cache table */ + CHKmalloc(pData->dynCache = (dynaFileCacheEntry**) + calloc(pData->iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))); + pData->iCurrElt = -1; /* no current element */ + } +// TODO: add pData->iSizeLimit = 0; /* default value, use outchannels to configure! */ + setupInstStatsCtrs(pData); + + if(pData->iCloseTimeout == -1) { /* unset? */ + pData->iCloseTimeout = (pData->bDynamicName) ? 10 : 0; + } + + snprintf(pData->janitorID, sizeof(pData->janitorID), "omfile:%sfile:%s:%p", + (pData->bDynamicName) ? "dyna" : "", pData->fname, pData); + pData->janitorID[sizeof(pData->janitorID)-1] = '\0'; /* just in case... */ + + if(pData->iCloseTimeout > 0) + janitorAddEtry(janitorCB, pData->janitorID, pData); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + +BEGINparseSelectorAct + uchar fname[MAXFNAME]; +CODESTARTparseSelectorAct + /* Note: the indicator sequence permits us to use '$' to signify + * outchannel, what otherwise is not possible due to truely + * unresolvable grammar conflicts (*this time no way around*). + * rgerhards, 2011-07-09 + */ + if(!strncmp((char*) p, ":omfile:", sizeof(":omfile:") - 1)) { + p += sizeof(":omfile:") - 1; + } + if(!(*p == '$' || *p == '?' || *p == '/' || *p == '.' || *p == '-')) + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + + CHKiRet(createInstance(&pData)); + + if(*p == '-') { + pData->bSyncFile = 0; + p++; + } else { + pData->bSyncFile = cs.bEnableSync; + } + pData->iSizeLimit = 0; /* default value, use outchannels to configure! */ + + switch(*p) { + case '$': + CODE_STD_STRING_REQUESTparseSelectorAct(1) + pData->iNumTpls = 1; + /* rgerhards 2005-06-21: this is a special setting for output-channel + * definitions. In the long term, this setting will probably replace + * anything else, but for the time being we must co-exist with the + * traditional mode lines. + * rgerhards, 2007-07-24: output-channels will go away. We keep them + * for compatibility reasons, but seems to have been a bad idea. + */ + CHKiRet(cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS)); + pData->bDynamicName = 0; + break; + + case '?': /* This is much like a regular file handle, but we need to obtain + * a template name. rgerhards, 2007-07-03 + */ + CODE_STD_STRING_REQUESTparseSelectorAct(2) + pData->iNumTpls = 2; + ++p; /* eat '?' */ + CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl())); + pData->fname = ustrdup(fname); + pData->bDynamicName = 1; + pData->iCurrElt = -1; /* no current element */ + /* "filename" is actually a template name, we need this as string 1. So let's add it + * to the pOMSR. -- rgerhards, 2007-07-27 + */ + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->fname), OMSR_NO_RQD_TPL_OPTS)); + /* we now allocate the cache table */ + CHKmalloc(pData->dynCache = (dynaFileCacheEntry**) + calloc(cs.iDynaFileCacheSize, sizeof(dynaFileCacheEntry*))); + break; + + case '/': + case '.': + CODE_STD_STRING_REQUESTparseSelectorAct(1) + pData->iNumTpls = 1; + CHKiRet(cflineParseFileName(p, fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl())); + pData->fname = ustrdup(fname); + pData->bDynamicName = 0; + break; + default: + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + } + + /* freeze current paremeters for this action */ + pData->iDynaFileCacheSize = cs.iDynaFileCacheSize; + pData->fCreateMode = cs.fCreateMode; + pData->fDirCreateMode = cs.fDirCreateMode; + pData->bCreateDirs = cs.bCreateDirs; + pData->bFailOnChown = cs.bFailOnChown; + pData->fileUID = cs.fileUID; + pData->fileGID = cs.fileGID; + pData->dirUID = cs.dirUID; + pData->dirGID = cs.dirGID; + pData->iZipLevel = cs.iZipLevel; + pData->bFlushOnTXEnd = cs.bFlushOnTXEnd; + pData->iIOBufSize = (int) cs.iIOBufSize; + pData->iFlushInterval = cs.iFlushInterval; + pData->bUseAsyncWriter = cs.bUseAsyncWriter; + pData->bVeryRobustZip = 0; /* cannot be specified via legacy conf */ + pData->iCloseTimeout = 0; /* cannot be specified via legacy conf */ + setupInstStatsCtrs(pData); +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +/* Reset config variables for this module to default values. + * rgerhards, 2007-07-17 + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + cs.fileUID = -1; + cs.fileGID = -1; + cs.dirUID = -1; + cs.dirGID = -1; + cs.bFailOnChown = 1; + cs.iDynaFileCacheSize = 10; + cs.fCreateMode = 0644; + cs.fDirCreateMode = 0700; + cs.bCreateDirs = 1; + cs.bEnableSync = 0; + cs.iZipLevel = 0; + cs.bFlushOnTXEnd = FLUSHONTX_DFLT; + cs.iIOBufSize = IOBUF_DFLT_SIZE; + cs.iFlushInterval = FLUSH_INTRVL_DFLT; + cs.bUseAsyncWriter = USE_ASYNCWRITER_DFLT; + free(pszFileDfltTplName); + pszFileDfltTplName = NULL; + return RS_RET_OK; +} + + +BEGINdoHUP +CODESTARTdoHUP + pthread_mutex_lock(&pData->mutWrite); + if(pData->bDynamicName) { + dynaFileFreeCacheEntries(pData); + } else { + if(pData->pStrm != NULL) { + closeFile(pData); + } + } + pthread_mutex_unlock(&pData->mutWrite); +ENDdoHUP + + +BEGINmodExit +CODESTARTmodExit + objRelease(strm, CORE_COMPONENT); + objRelease(statsobj, CORE_COMPONENT); + DESTROY_ATOMIC_HELPER_MUT(mutClock); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMODTX_QUERIES +CODEqueryEtryPt_STD_OMOD8_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +CODEqueryEtryPt_doHUP +ENDqueryEtryPt + + +BEGINmodInit(File) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr +INITLegCnfVars + CHKiRet(objUse(strm, CORE_COMPONENT)); + CHKiRet(objUse(statsobj, CORE_COMPONENT)); + + INIT_ATOMIC_HELPER_MUT(mutClock); + + INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING); + DBGPRINTF("omfile: %susing transactional output interface.\n", bCoreSupportsBatching ? "" : "not "); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dynafilecachesize", 0, eCmdHdlrInt, setDynaFileCacheSize, + NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileziplevel", 0, eCmdHdlrInt, NULL, &cs.iZipLevel, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushinterval", 0, eCmdHdlrInt, NULL, &cs.iFlushInterval, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileasyncwriting", 0, eCmdHdlrBinary, NULL, &cs.bUseAsyncWriter, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushontxend", 0, eCmdHdlrBinary, NULL, &cs.bFlushOnTXEnd, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileiobuffersize", 0, eCmdHdlrSize, NULL, &cs.iIOBufSize, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirowner", 0, eCmdHdlrUID, NULL, &cs.dirUID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirownernum", 0, eCmdHdlrInt, NULL, &cs.dirUID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroup", 0, eCmdHdlrGID, NULL, &cs.dirGID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroupnum", 0, eCmdHdlrInt, NULL, &cs.dirGID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileowner", 0, eCmdHdlrUID, NULL, &cs.fileUID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileownernum", 0, eCmdHdlrInt, NULL, &cs.fileUID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"filegroup", 0, eCmdHdlrGID, NULL, &cs.fileGID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"filegroupnum", 0, eCmdHdlrInt, NULL, &cs.fileGID, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"dircreatemode", 0, eCmdHdlrFileCreateMode, NULL, + &cs.fDirCreateMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"filecreatemode", 0, eCmdHdlrFileCreateMode, NULL, + &cs.fCreateMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"createdirs", 0, eCmdHdlrBinary, NULL, &cs.bCreateDirs, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"failonchownfailure", 0, eCmdHdlrBinary, NULL, &cs.bFailOnChown, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileforcechown", 0, eCmdHdlrGoneAway, NULL, NULL, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfileenablesync", 0, eCmdHdlrBinary, NULL, &cs.bEnableSync, + STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, setLegacyDfltTpl, + NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, + NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit diff --git a/tools/omfile.h b/tools/omfile.h new file mode 100644 index 0000000..dc7298d --- /dev/null +++ b/tools/omfile.h @@ -0,0 +1,38 @@ +/* omfile.h + * These are the definitions for the build-in file output module. + * + * File begun on 2007-07-21 by RGerhards + * + * Copyright 2007-2012 Rainer Gerhards and 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. + */ +#ifndef OMFILE_H_INCLUDED +#define OMFILE_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitFile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), + rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +/* the define below is dirty, but we need it for ompipe integration. There is no + * other way to have the functionality (well, one way would be to go through the + * globals, but that seems not yet justified. -- rgerhards, 2010-03-01 + */ +extern uchar *pszFileDfltTplName; +#endif /* #ifndef OMFILE_H_INCLUDED */ +/* vi:set ai: + */ diff --git a/tools/omfwd.c b/tools/omfwd.c new file mode 100644 index 0000000..d1eeeff --- /dev/null +++ b/tools/omfwd.c @@ -0,0 +1,1778 @@ +/* omfwd.c + * This is the implementation of the build-in forwarding output module. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * Copyright 2007-2021 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 <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fnmatch.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <stdint.h> +#include <fcntl.h> +#include <zlib.h> +#include <pthread.h> +#include "rsyslog.h" +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "net.h" +#include "netstrms.h" +#include "netstrm.h" +#include "omfwd.h" +#include "template.h" +#include "msg.h" +#include "tcpclt.h" +#include "cfsysline.h" +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "unicode-helper.h" +#include "parserif.h" +#include "ratelimit.h" +#include "statsobj.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omfwd") + +/* internal structures + */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(glbl) +DEFobjCurrIf(net) +DEFobjCurrIf(netstrms) +DEFobjCurrIf(netstrm) +DEFobjCurrIf(tcpclt) +DEFobjCurrIf(statsobj) + + +/* some local constants (just) for better readybility */ +#define IS_FLUSH 1 +#define NO_FLUSH 0 + +typedef struct _instanceData { + uchar *tplName; /* name of assigned template */ + uchar *pszStrmDrvr; + uchar *pszStrmDrvrAuthMode; + uchar *pszStrmDrvrPermitExpiredCerts; + permittedPeers_t *pPermPeers; + int iStrmDrvrMode; + int iStrmDrvrExtendedCertCheck; /* verify also purpose OID in certificate extended field */ + int iStrmDrvrSANPreference; /* ignore CN when any SAN set */ + int iStrmTlsVerifyDepth; /**< Verify Depth for certificate chains */ + const uchar *pszStrmDrvrCAFile; + const uchar *pszStrmDrvrCRLFile; + const uchar *pszStrmDrvrKeyFile; + const uchar *pszStrmDrvrCertFile; + char *target; + char *address; + char *device; + int compressionLevel; /* 0 - no compression, else level for zlib */ + char *port; + int protocol; + char *networkNamespace; + int originalNamespace; + int iRebindInterval; /* rebind interval */ + sbool bKeepAlive; + int iKeepAliveIntvl; + int iKeepAliveProbes; + int iKeepAliveTime; + int iConErrSkip; /* skipping excessive connection errors */ + uchar *gnutlsPriorityString; + int ipfreebind; + +# define FORW_UDP 0 +# define FORW_TCP 1 + /* following fields for UDP-based delivery */ + int bSendToAll; + int iUDPSendDelay; + int UDPSendBuf; + /* following fields for TCP-based delivery */ + TCPFRAMINGMODE tcp_framing; + uchar tcp_framingDelimiter; + int bResendLastOnRecon; /* should the last message be re-sent on a successful reconnect? */ +# define COMPRESS_NEVER 0 +# define COMPRESS_SINGLE_MSG 1 /* old, single-message compression */ + /* all other settings are for stream-compression */ +# define COMPRESS_STREAM_ALWAYS 2 + uint8_t compressionMode; + int errsToReport; /* max number of errors to report (per instance) */ + sbool strmCompFlushOnTxEnd; /* flush stream compression on transaction end? */ + unsigned int ratelimitInterval; + unsigned int ratelimitBurst; + ratelimit_t *ratelimiter; + statsobj_t *stats; /* dynafile, primarily cache stats */ + intctr_t sentBytes; + DEF_ATOMIC_HELPER_MUT64(mut_sentBytes) +} instanceData; + +typedef struct wrkrInstanceData { + instanceData *pData; + netstrms_t *pNS; /* netstream subsystem */ + netstrm_t *pNetstrm; /* our output netstream */ + struct addrinfo *f_addr; + int *pSockArray; /* sockets to use for UDP */ + int bIsConnected; /* are we connected to remote host? 0 - no, 1 - yes, UDP means addr resolved */ + int nXmit; /* number of transmissions since last (re-)bind */ + tcpclt_t *pTCPClt; /* our tcpclt object */ + sbool bzInitDone; /* did we do an init of zstrm already? */ + z_stream zstrm; /* zip stream to use for tcp compression */ + uchar sndBuf[16*1024]; /* this is intensionally fixed -- see no good reason to make configurable */ + unsigned offsSndBuf; /* next free spot in send buffer */ + int errsToReport; /* (remaining) number of errors to report */ +} wrkrInstanceData_t; + +/* config data */ +typedef struct configSettings_s { + uchar *pszTplName; /* name of the default template to use */ + uchar *pszStrmDrvr; /* name of the stream driver to use */ + int iStrmDrvrMode; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ + int bResendLastOnRecon; /* should the last message be re-sent on a successful reconnect? */ + uchar *pszStrmDrvrAuthMode; /* authentication mode to use */ + uchar *pszStrmDrvrPermitExpiredCerts; /* control how to handly expired certificates */ + int iTCPRebindInterval; /* support for automatic re-binding (load balancers!). 0 - no rebind */ + int iUDPRebindInterval; /* support for automatic re-binding (load balancers!). 0 - no rebind */ + int bKeepAlive; + int iKeepAliveIntvl; + int iKeepAliveProbes; + int iKeepAliveTime; + int iConErrSkip; + uchar *gnutlsPriorityString; + permittedPeers_t *pPermPeers; +} configSettings_t; +static configSettings_t cs; + +/* tables for interfacing with the v6 config system */ +/* module-global parameters */ +static struct cnfparamdescr modpdescr[] = { + { "template", eCmdHdlrGetWord, 0 }, +}; +static struct cnfparamblk modpblk = + { CNFPARAMBLK_VERSION, + sizeof(modpdescr)/sizeof(struct cnfparamdescr), + modpdescr + }; + +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "target", eCmdHdlrGetWord, 0 }, + { "address", eCmdHdlrGetWord, 0 }, + { "device", eCmdHdlrGetWord, 0 }, + { "port", eCmdHdlrGetWord, 0 }, + { "protocol", eCmdHdlrGetWord, 0 }, + { "networknamespace", eCmdHdlrGetWord, 0 }, + { "tcp_framing", eCmdHdlrGetWord, 0 }, + { "tcp_framedelimiter", eCmdHdlrInt, 0 }, + { "ziplevel", eCmdHdlrInt, 0 }, + { "compression.mode", eCmdHdlrGetWord, 0 }, + { "compression.stream.flushontxend", eCmdHdlrBinary, 0 }, + { "ipfreebind", eCmdHdlrInt, 0 }, + { "maxerrormessages", eCmdHdlrInt, CNFPARAM_DEPRECATED }, + { "rebindinterval", eCmdHdlrInt, 0 }, + { "keepalive", eCmdHdlrBinary, 0 }, + { "keepalive.probes", eCmdHdlrNonNegInt, 0 }, + { "keepalive.time", eCmdHdlrNonNegInt, 0 }, + { "keepalive.interval", eCmdHdlrNonNegInt, 0 }, + { "conerrskip", eCmdHdlrNonNegInt, 0 }, + { "gnutlsprioritystring", eCmdHdlrString, 0 }, + { "streamdriver", eCmdHdlrGetWord, 0 }, + { "streamdrivermode", eCmdHdlrInt, 0 }, + { "streamdriverauthmode", eCmdHdlrGetWord, 0 }, + { "streamdriverpermittedpeers", eCmdHdlrGetWord, 0 }, + { "streamdriver.permitexpiredcerts", eCmdHdlrGetWord, 0 }, + { "streamdriver.CheckExtendedKeyPurpose", eCmdHdlrBinary, 0 }, + { "streamdriver.PrioritizeSAN", eCmdHdlrBinary, 0 }, + { "streamdriver.TlsVerifyDepth", eCmdHdlrPositiveInt, 0 }, + { "streamdriver.cafile", eCmdHdlrString, 0 }, + { "streamdriver.keyfile", eCmdHdlrString, 0 }, + { "streamdriver.certfile", eCmdHdlrString, 0 }, + { "resendlastmsgonreconnect", eCmdHdlrBinary, 0 }, + { "udp.sendtoall", eCmdHdlrBinary, 0 }, + { "udp.senddelay", eCmdHdlrInt, 0 }, + { "udp.sendbuf", eCmdHdlrSize, 0 }, + { "template", eCmdHdlrGetWord, 0 }, + { "ratelimit.interval", eCmdHdlrInt, 0 }, + { "ratelimit.burst", eCmdHdlrInt, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + uchar *tplName; /* default template */ +}; + +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 */ + + +static rsRetVal initTCP(wrkrInstanceData_t *pWrkrData); + + +BEGINinitConfVars /* (re)set config variables to default values */ +CODESTARTinitConfVars + cs.pszTplName = NULL; /* name of the default template to use */ + cs.pszStrmDrvr = NULL; /* name of the stream driver to use */ + cs.iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ + cs.bResendLastOnRecon = 0; /* should the last message be re-sent on a successful reconnect? */ + cs.pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ + cs.iUDPRebindInterval = 0; /* support for automatic re-binding (load balancers!). 0 - no rebind */ + cs.iTCPRebindInterval = 0; /* support for automatic re-binding (load balancers!). 0 - no rebind */ + cs.pPermPeers = NULL; +ENDinitConfVars + + +static rsRetVal doTryResume(wrkrInstanceData_t *); +static rsRetVal doZipFinish(wrkrInstanceData_t *); + +/* this function gets the default template. It coordinates action between + * old-style and new-style configuration parts. + */ +static uchar* +getDfltTpl(void) +{ + if(loadModConf != NULL && loadModConf->tplName != NULL) + return loadModConf->tplName; + else if(cs.pszTplName == NULL) + return (uchar*)"RSYSLOG_TraditionalForwardFormat"; + else + return cs.pszTplName; +} + + +/* set the default template to be used + * This is a module-global parameter, and as such needs special handling. It needs to + * be coordinated with values set via the v2 config system (rsyslog v6+). What we do + * is we do not permit this directive after the v2 config system has been used to set + * the parameter. + */ +static rsRetVal +setLegacyDfltTpl(void __attribute__((unused)) *pVal, uchar* newVal) +{ + DEFiRet; + + if(loadModConf != NULL && loadModConf->tplName != NULL) { + free(newVal); + LogError(0, RS_RET_ERR, "omfwd default template already set via module " + "global parameter - can no longer be changed"); + ABORT_FINALIZE(RS_RET_ERR); + } + free(cs.pszTplName); + cs.pszTplName = newVal; +finalize_it: + RETiRet; +} + +/* Close the UDP sockets. + * rgerhards, 2009-05-29 + */ +static rsRetVal +closeUDPSockets(wrkrInstanceData_t *pWrkrData) +{ + DEFiRet; + if(pWrkrData->pSockArray != NULL) { + net.closeUDPListenSockets(pWrkrData->pSockArray); + pWrkrData->pSockArray = NULL; + freeaddrinfo(pWrkrData->f_addr); + pWrkrData->f_addr = NULL; + } +pWrkrData->bIsConnected = 0; // TODO: remove this variable altogether + RETiRet; +} + + +/* destruct the TCP helper objects + * This, for example, is needed after something went wrong. + * This function is void because it "can not" fail. + * rgerhards, 2008-06-04 + * Note that we DO NOT discard the current buffer contents + * (if any). This permits us to save data between sessions. In + * the worst case, some duplication occurs, but we do not + * loose data. + */ +static void +DestructTCPInstanceData(wrkrInstanceData_t *pWrkrData) +{ + doZipFinish(pWrkrData); + if(pWrkrData->pNetstrm != NULL) + netstrm.Destruct(&pWrkrData->pNetstrm); + if(pWrkrData->pNS != NULL) + netstrms.Destruct(&pWrkrData->pNS); +} + + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + pModConf->tplName = NULL; +ENDbeginCnfLoad + +BEGINsetModCnf + int i; +CODESTARTsetModCnf + const struct cnfparamvals *const __restrict__ pvals = nvlstGetParams(lst, &modpblk, NULL); + if(pvals == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("module (global) param blk for omfwd:\n"); + cnfparamsPrint(&modpblk, pvals); + } + + for(i = 0 ; i < modpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(modpblk.descr[i].name, "template")) { + loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + if(cs.pszTplName != NULL) { + LogError(0, RS_RET_DUP_PARAM, "omfwd: warning: default template " + "was already set via legacy directive - may lead to inconsistent " + "results."); + } + } else { + dbgprintf("omfwd: program error, non-handled " + "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); + } + } +finalize_it: + if(pvals != NULL) + cnfparamvalsDestruct(pvals, &modpblk); +ENDsetModCnf + +BEGINendCnfLoad +CODESTARTendCnfLoad + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(cs.pszTplName); + cs.pszTplName = NULL; +ENDendCnfLoad + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + +BEGINactivateCnf +CODESTARTactivateCnf + runModConf = pModConf; +ENDactivateCnf + +BEGINfreeCnf +CODESTARTfreeCnf + free(pModConf->tplName); +ENDfreeCnf + +BEGINcreateInstance +CODESTARTcreateInstance + if(cs.pszStrmDrvr != NULL) + CHKmalloc(pData->pszStrmDrvr = (uchar*)strdup((char*)cs.pszStrmDrvr)); + if(cs.pszStrmDrvrAuthMode != NULL) + CHKmalloc(pData->pszStrmDrvrAuthMode = + (uchar*)strdup((char*)cs.pszStrmDrvrAuthMode)); +finalize_it: +ENDcreateInstance + + +BEGINcreateWrkrInstance +CODESTARTcreateWrkrInstance + dbgprintf("DDDD: createWrkrInstance: pWrkrData %p\n", pWrkrData); + pWrkrData->offsSndBuf = 0; + iRet = initTCP(pWrkrData); +ENDcreateWrkrInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + if(pData->stats != NULL) + statsobj.Destruct(&(pData->stats)); + free(pData->pszStrmDrvr); + free(pData->pszStrmDrvrAuthMode); + free(pData->pszStrmDrvrPermitExpiredCerts); + free(pData->gnutlsPriorityString); + free(pData->port); + free(pData->networkNamespace); + free(pData->target); + free(pData->address); + free(pData->device); + free((void*)pData->pszStrmDrvrCAFile); + free((void*)pData->pszStrmDrvrCRLFile); + free((void*)pData->pszStrmDrvrKeyFile); + free((void*)pData->pszStrmDrvrCertFile); + net.DestructPermittedPeers(&pData->pPermPeers); + if (pData->ratelimiter != NULL){ + ratelimitDestruct(pData->ratelimiter); + pData->ratelimiter = NULL; + } + +ENDfreeInstance + + +BEGINfreeWrkrInstance +CODESTARTfreeWrkrInstance + DestructTCPInstanceData(pWrkrData); + closeUDPSockets(pWrkrData); + + if(pWrkrData->pData->protocol == FORW_TCP) { + tcpclt.Destruct(&pWrkrData->pTCPClt); + } +ENDfreeWrkrInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + dbgprintf("omfwd\n"); + dbgprintf("\ttarget='%s'\n", pData->target); + dbgprintf("\tratelimit.interval='%u'\n", pData->ratelimitInterval); + dbgprintf("\tratelimit.burst='%u'\n", pData->ratelimitBurst); +ENDdbgPrintInstInfo + + +/* Send a message via UDP + * rgehards, 2007-12-20 + */ +#define UDP_MAX_MSGSIZE 65507 /* limit per RFC definition */ +static rsRetVal UDPSend(wrkrInstanceData_t *__restrict__ const pWrkrData, + uchar *__restrict__ const msg, + size_t len) +{ + DEFiRet; + struct addrinfo *r; + int i; + ssize_t lsent = 0; + sbool bSendSuccess; + sbool reInit = RSFALSE; + int lasterrno = ENOENT; + int lasterr_sock = -1; + + if(pWrkrData->pData->iRebindInterval && (pWrkrData->nXmit++ % pWrkrData->pData->iRebindInterval == 0)) { + dbgprintf("omfwd dropping UDP 'connection' (as configured)\n"); + pWrkrData->nXmit = 1; /* else we have an addtl wrap at 2^31-1 */ + CHKiRet(closeUDPSockets(pWrkrData)); + } + + if(pWrkrData->pSockArray == NULL) { + CHKiRet(doTryResume(pWrkrData)); + } + + if(pWrkrData->pSockArray == NULL) { + FINALIZE; + } + + + if(len > UDP_MAX_MSGSIZE) { + LogError(0, RS_RET_UDP_MSGSIZE_TOO_LARGE, "omfwd/udp: message is %u " + "bytes long, but UDP can send at most %d bytes (by RFC limit) " + "- truncating message", (unsigned) len, UDP_MAX_MSGSIZE); + len = UDP_MAX_MSGSIZE; + } + + /* we need to track if we have success sending to the remote + * peer. Success is indicated by at least one sendto() call + * succeeding. We track this be bSendSuccess. We can not simply + * rely on lsent, as a call might initially work, but a later + * call fails. Then, lsent has the error status, even though + * the sendto() succeeded. -- rgerhards, 2007-06-22 + */ + bSendSuccess = RSFALSE; + for (r = pWrkrData->f_addr; r; r = r->ai_next) { + int runSockArrayLoop = 1; + for (i = 0; runSockArrayLoop && (i < *pWrkrData->pSockArray) ; i++) { + int try_send = 1; + size_t lenThisTry = len; + while(try_send) { + lsent = sendto(pWrkrData->pSockArray[i+1], msg, lenThisTry, 0, + r->ai_addr, r->ai_addrlen); + if (lsent == (ssize_t) lenThisTry) { + bSendSuccess = RSTRUE; + ATOMIC_ADD_uint64(&pWrkrData->pData->sentBytes, + &pWrkrData->pData->mut_sentBytes, lenThisTry); + try_send = 0; + runSockArrayLoop = 0; + } else if(errno == EMSGSIZE) { + const size_t newlen = (lenThisTry > 1024) ? lenThisTry - 1024 : 512; + LogError(0, RS_RET_UDP_MSGSIZE_TOO_LARGE, + "omfwd/udp: send failed due to message being too " + "large for this system. Message size was %u bytes. " + "Truncating to %u bytes and retrying.", + (unsigned) lenThisTry, (unsigned) newlen); + lenThisTry = newlen; + } else { + reInit = RSTRUE; + lasterrno = errno; + lasterr_sock = pWrkrData->pSockArray[i+1]; + LogError(lasterrno, RS_RET_ERR_UDPSEND, + "omfwd/udp: socket %d: sendto() error", + lasterr_sock); + try_send = 0; + } + } + } + if (lsent == (ssize_t) len && !pWrkrData->pData->bSendToAll) + break; + } + + /* one or more send failures; close sockets and re-init */ + if (reInit == RSTRUE) { + CHKiRet(closeUDPSockets(pWrkrData)); + } + + /* finished looping */ + if(bSendSuccess == RSTRUE) { + if(pWrkrData->pData->iUDPSendDelay > 0) { + srSleep(pWrkrData->pData->iUDPSendDelay / 1000000, + pWrkrData->pData->iUDPSendDelay % 1000000); + } + } else { + LogError(lasterrno, RS_RET_ERR_UDPSEND, + "omfwd: socket %d: error %d sending via udp", lasterr_sock, lasterrno); + iRet = RS_RET_SUSPENDED; + } + +finalize_it: + RETiRet; +} + + +/* set the permitted peers -- rgerhards, 2008-05-19 + */ +static rsRetVal +setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID) +{ + DEFiRet; + CHKiRet(net.AddPermittedPeer(&cs.pPermPeers, pszID)); + free(pszID); /* no longer needed, but we must free it as of interface def */ +finalize_it: + RETiRet; +} + + + +/* CODE FOR SENDING TCP MESSAGES */ + +static rsRetVal +TCPSendBufUncompressed(wrkrInstanceData_t *pWrkrData, uchar *const buf, const unsigned len) +{ + DEFiRet; + unsigned alreadySent; + ssize_t lenSend; + + alreadySent = 0; + CHKiRet(netstrm.CheckConnection(pWrkrData->pNetstrm)); + /* hack for plain tcp syslog - see ptcp driver for details */ + + while(alreadySent != len) { + lenSend = len - alreadySent; + CHKiRet(netstrm.Send(pWrkrData->pNetstrm, buf+alreadySent, &lenSend)); + DBGPRINTF("omfwd: TCP sent %ld bytes, requested %u\n", (long) lenSend, len - alreadySent); + alreadySent += lenSend; + } + + ATOMIC_ADD_uint64(&pWrkrData->pData->sentBytes, &pWrkrData->pData->mut_sentBytes, len); + +finalize_it: + if(iRet != RS_RET_OK) { + if(iRet == RS_RET_IO_ERROR) { + static unsigned int conErrCnt = 0; + const int skipFactor = pWrkrData->pData->iConErrSkip; + if (skipFactor <= 1) { + /* All the connection errors are printed. */ + LogError(0, iRet, "omfwd: remote server at %s:%s seems to have closed connection. " + "This often happens when the remote peer (or an interim system like a load " + "balancer or firewall) shuts down or aborts a connection. Rsyslog will " + "re-open the connection if configured to do so (we saw a generic IO Error, " + "which usually goes along with that behaviour).", + pWrkrData->pData->target, pWrkrData->pData->port); + } else if ((conErrCnt++ % skipFactor) == 0) { + /* Every N'th error message is printed where N is a skipFactor. */ + LogError(0, iRet, "omfwd: remote server at %s:%s seems to have closed connection. " + "This often happens when the remote peer (or an interim system like a load " + "balancer or firewall) shuts down or aborts a connection. Rsyslog will " + "re-open the connection if configured to do so (we saw a generic IO Error, " + "which usually goes along with that behaviour). Note that the next %d " + "connection error messages will be skipped.", + pWrkrData->pData->target, pWrkrData->pData->port, skipFactor-1); + } + } else { + LogError(0, iRet, "omfwd: TCPSendBuf error %d, destruct TCP Connection to %s:%s", + iRet, pWrkrData->pData->target, pWrkrData->pData->port); + } + DestructTCPInstanceData(pWrkrData); + iRet = RS_RET_SUSPENDED; + } + RETiRet; +} + +static rsRetVal +TCPSendBufCompressed(wrkrInstanceData_t *pWrkrData, uchar *buf, unsigned len, sbool bIsFlush) +{ + int zRet; /* zlib return state */ + unsigned outavail; + uchar zipBuf[32*1024]; + int op; + DEFiRet; + + if(!pWrkrData->bzInitDone) { + /* allocate deflate state */ + pWrkrData->zstrm.zalloc = Z_NULL; + pWrkrData->zstrm.zfree = Z_NULL; + pWrkrData->zstrm.opaque = Z_NULL; + /* see note in file header for the params we use with deflateInit2() */ + zRet = deflateInit(&pWrkrData->zstrm, pWrkrData->pData->compressionLevel); + if(zRet != Z_OK) { + DBGPRINTF("error %d returned from zlib/deflateInit()\n", zRet); + ABORT_FINALIZE(RS_RET_ZLIB_ERR); + } + pWrkrData->bzInitDone = RSTRUE; + } + + /* now doing the compression */ + pWrkrData->zstrm.next_in = (Bytef*) buf; + pWrkrData->zstrm.avail_in = len; + if(pWrkrData->pData->strmCompFlushOnTxEnd && bIsFlush) + op = Z_SYNC_FLUSH; + else + op = Z_NO_FLUSH; + /* run deflate() on buffer until everything has been compressed */ + do { + DBGPRINTF("omfwd: in deflate() loop, avail_in %d, total_in %ld, isFlush %d\n", + pWrkrData->zstrm.avail_in, pWrkrData->zstrm.total_in, bIsFlush); + pWrkrData->zstrm.avail_out = sizeof(zipBuf); + pWrkrData->zstrm.next_out = zipBuf; + zRet = deflate(&pWrkrData->zstrm, op); /* no bad return value */ + DBGPRINTF("after deflate, ret %d, avail_out %d\n", zRet, pWrkrData->zstrm.avail_out); + outavail = sizeof(zipBuf) - pWrkrData->zstrm.avail_out; + if(outavail != 0) { + CHKiRet(TCPSendBufUncompressed(pWrkrData, zipBuf, outavail)); + } + } while (pWrkrData->zstrm.avail_out == 0); + +finalize_it: + RETiRet; +} + +static rsRetVal +TCPSendBuf(wrkrInstanceData_t *pWrkrData, uchar *buf, unsigned len, sbool bIsFlush) +{ + DEFiRet; + if(pWrkrData->pData->compressionMode >= COMPRESS_STREAM_ALWAYS) + iRet = TCPSendBufCompressed(pWrkrData, buf, len, bIsFlush); + else + iRet = TCPSendBufUncompressed(pWrkrData, buf, len); + RETiRet; +} + +/* finish zlib buffer, to be called before closing the ZIP file (if + * running in stream mode). + */ +static rsRetVal +doZipFinish(wrkrInstanceData_t *pWrkrData) +{ + int zRet; /* zlib return state */ + DEFiRet; + unsigned outavail; + uchar zipBuf[32*1024]; + + if(!pWrkrData->bzInitDone) + goto done; + + // TODO: can we get this into a single common function? + pWrkrData->zstrm.avail_in = 0; + /* run deflate() on buffer until everything has been compressed */ + do { + DBGPRINTF("in deflate() loop, avail_in %d, total_in %ld\n", pWrkrData->zstrm.avail_in, + pWrkrData->zstrm.total_in); + pWrkrData->zstrm.avail_out = sizeof(zipBuf); + pWrkrData->zstrm.next_out = zipBuf; + zRet = deflate(&pWrkrData->zstrm, Z_FINISH); /* no bad return value */ + DBGPRINTF("after deflate, ret %d, avail_out %d\n", zRet, pWrkrData->zstrm.avail_out); + outavail = sizeof(zipBuf) - pWrkrData->zstrm.avail_out; + if(outavail != 0) { + CHKiRet(TCPSendBufUncompressed(pWrkrData, zipBuf, outavail)); + } + } while (pWrkrData->zstrm.avail_out == 0); + +finalize_it: + zRet = deflateEnd(&pWrkrData->zstrm); + if(zRet != Z_OK) { + DBGPRINTF("error %d returned from zlib/deflateEnd()\n", zRet); + } + + pWrkrData->bzInitDone = 0; +done: RETiRet; +} + + +/* Add frame to send buffer (or send, if requried) + */ +static rsRetVal TCPSendFrame(void *pvData, char *msg, size_t len) +{ + DEFiRet; + wrkrInstanceData_t *pWrkrData = (wrkrInstanceData_t *) pvData; + + DBGPRINTF("omfwd: add %u bytes to send buffer (curr offs %u)\n", + (unsigned) len, pWrkrData->offsSndBuf); + if(pWrkrData->offsSndBuf != 0 && pWrkrData->offsSndBuf + len >= sizeof(pWrkrData->sndBuf)) { + /* no buffer space left, need to commit previous records. With the + * current API, there unfortunately is no way to signal this + * state transition to the upper layer. + */ + DBGPRINTF("omfwd: we need to do a tcp send due to buffer " + "out of space. If the transaction fails, this will " + "lead to duplication of messages"); + CHKiRet(TCPSendBuf(pWrkrData, pWrkrData->sndBuf, pWrkrData->offsSndBuf, NO_FLUSH)); + pWrkrData->offsSndBuf = 0; + } + + /* check if the message is too large to fit into buffer */ + if(len > sizeof(pWrkrData->sndBuf)) { + CHKiRet(TCPSendBuf(pWrkrData, (uchar*)msg, len, NO_FLUSH)); + ABORT_FINALIZE(RS_RET_OK); /* committed everything so far */ + } + + /* we now know the buffer has enough free space */ + memcpy(pWrkrData->sndBuf + pWrkrData->offsSndBuf, msg, len); + pWrkrData->offsSndBuf += len; + iRet = RS_RET_DEFER_COMMIT; + +finalize_it: + RETiRet; +} + + +/* This function is called immediately before a send retry is attempted. + * It shall clean up whatever makes sense. + * rgerhards, 2007-12-28 + */ +static rsRetVal TCPSendPrepRetry(void *pvData) +{ + DEFiRet; + wrkrInstanceData_t *pWrkrData = (wrkrInstanceData_t *) pvData; + + assert(pWrkrData != NULL); + DestructTCPInstanceData(pWrkrData); + RETiRet; +} + + +/* initializes everything so that TCPSend can work. + * rgerhards, 2007-12-28 + */ +static rsRetVal TCPSendInit(void *pvData) +{ + DEFiRet; + wrkrInstanceData_t *pWrkrData = (wrkrInstanceData_t *) pvData; + instanceData *pData; + + assert(pWrkrData != NULL); + pData = pWrkrData->pData; + + if(pWrkrData->pNetstrm == NULL) { + dbgprintf("TCPSendInit CREATE\n"); + CHKiRet(netstrms.Construct(&pWrkrData->pNS)); + /* the stream driver must be set before the object is finalized! */ + CHKiRet(netstrms.SetDrvrName(pWrkrData->pNS, pData->pszStrmDrvr)); + CHKiRet(netstrms.ConstructFinalize(pWrkrData->pNS)); + + /* now create the actual stream and connect to the server */ + CHKiRet(netstrms.CreateStrm(pWrkrData->pNS, &pWrkrData->pNetstrm)); + CHKiRet(netstrm.ConstructFinalize(pWrkrData->pNetstrm)); + CHKiRet(netstrm.SetDrvrMode(pWrkrData->pNetstrm, pData->iStrmDrvrMode)); + CHKiRet(netstrm.SetDrvrCheckExtendedKeyUsage(pWrkrData->pNetstrm, pData->iStrmDrvrExtendedCertCheck)); + CHKiRet(netstrm.SetDrvrPrioritizeSAN(pWrkrData->pNetstrm, pData->iStrmDrvrSANPreference)); + CHKiRet(netstrm.SetDrvrTlsVerifyDepth(pWrkrData->pNetstrm, pData->iStrmTlsVerifyDepth)); + /* now set optional params, but only if they were actually configured */ + if(pData->pszStrmDrvrAuthMode != NULL) { + CHKiRet(netstrm.SetDrvrAuthMode(pWrkrData->pNetstrm, pData->pszStrmDrvrAuthMode)); + } + /* Call SetDrvrPermitExpiredCerts required + * when param is NULL default handling for ExpiredCerts is set! */ + CHKiRet(netstrm.SetDrvrPermitExpiredCerts(pWrkrData->pNetstrm, + pData->pszStrmDrvrPermitExpiredCerts)); + CHKiRet(netstrm.SetDrvrTlsCAFile(pWrkrData->pNetstrm, pData->pszStrmDrvrCAFile)); + CHKiRet(netstrm.SetDrvrTlsCRLFile(pWrkrData->pNetstrm, pData->pszStrmDrvrCRLFile)); + CHKiRet(netstrm.SetDrvrTlsKeyFile(pWrkrData->pNetstrm, pData->pszStrmDrvrKeyFile)); + CHKiRet(netstrm.SetDrvrTlsCertFile(pWrkrData->pNetstrm, pData->pszStrmDrvrCertFile)); + + if(pData->pPermPeers != NULL) { + CHKiRet(netstrm.SetDrvrPermPeers(pWrkrData->pNetstrm, pData->pPermPeers)); + } + /* params set, now connect */ + if(pData->gnutlsPriorityString != NULL) { + CHKiRet(netstrm.SetGnutlsPriorityString(pWrkrData->pNetstrm, pData->gnutlsPriorityString)); + } + CHKiRet(netstrm.Connect(pWrkrData->pNetstrm, glbl.GetDefPFFamily(runModConf->pConf), + (uchar*)pData->port, (uchar*)pData->target, pData->device)); + + /* set keep-alive if enabled */ + if(pData->bKeepAlive) { + CHKiRet(netstrm.SetKeepAliveProbes(pWrkrData->pNetstrm, pData->iKeepAliveProbes)); + CHKiRet(netstrm.SetKeepAliveIntvl(pWrkrData->pNetstrm, pData->iKeepAliveIntvl)); + CHKiRet(netstrm.SetKeepAliveTime(pWrkrData->pNetstrm, pData->iKeepAliveTime)); + CHKiRet(netstrm.EnableKeepAlive(pWrkrData->pNetstrm)); + } + } + +finalize_it: + if(iRet != RS_RET_OK) { + dbgprintf("TCPSendInit FAILED with %d.\n", iRet); + DestructTCPInstanceData(pWrkrData); + } + + RETiRet; +} + + +/* change to network namespace pData->networkNamespace and keep the file + * descriptor to the original namespace. + */ +static rsRetVal changeToNs(instanceData *const pData __attribute__((unused))) +{ + DEFiRet; +#ifdef HAVE_SETNS + int iErr; + int destinationNs = -1; + char *nsPath = NULL; + + if(pData->networkNamespace) { + /* keep file descriptor of original network namespace */ + pData->originalNamespace = open("/proc/self/ns/net", O_RDONLY); + if (pData->originalNamespace < 0) { + LogError(0, RS_RET_IO_ERROR, "omfwd: could not read /proc/self/ns/net"); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + /* build network namespace path */ + if (asprintf(&nsPath, "/var/run/netns/%s", pData->networkNamespace) == -1) { + LogError(0, RS_RET_OUT_OF_MEMORY, "omfwd: asprintf failed"); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + /* keep file descriptor of destination network namespace */ + destinationNs = open(nsPath, 0); + if (destinationNs < 0) { + LogError(0, RS_RET_IO_ERROR, "omfwd: could not change to namespace '%s'", + pData->networkNamespace); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + /* actually change in the destination network namespace */ + if((iErr = (setns(destinationNs, CLONE_NEWNET))) != 0) { + LogError(0, RS_RET_IO_ERROR, "could not change to namespace '%s': %s", + pData->networkNamespace, gai_strerror(iErr)); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + dbgprintf("omfwd: changed to network namespace '%s'\n", pData->networkNamespace); + } + +finalize_it: + free(nsPath); + if(destinationNs >= 0) { + close(destinationNs); + } +#else /* #ifdef HAVE_SETNS */ + dbgprintf("omfwd: OS does not support network namespaces\n"); +#endif /* #ifdef HAVE_SETNS */ + RETiRet; +} + + +/* return to the original network namespace. This should be called after + * changeToNs(). + */ +static rsRetVal returnToOriginalNs(instanceData *const pData __attribute__((unused))) +{ + DEFiRet; +#ifdef HAVE_SETNS + int iErr; + + /* only in case a network namespace is given and a file descriptor to + * the original namespace exists */ + if(pData->networkNamespace && pData->originalNamespace >= 0) { + /* actually change to the original network namespace */ + if((iErr = (setns(pData->originalNamespace, CLONE_NEWNET))) != 0) { + LogError(0, RS_RET_IO_ERROR, "could not return to original namespace: %s", + gai_strerror(iErr)); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + close(pData->originalNamespace); + dbgprintf("omfwd: returned to original network namespace\n"); + } + +finalize_it: +#endif /* #ifdef HAVE_SETNS */ + RETiRet; +} + + +/* try to resume connection if it is not ready + * rgerhards, 2007-08-02 + */ +static rsRetVal doTryResume(wrkrInstanceData_t *pWrkrData) +{ + int iErr; + struct addrinfo *res = NULL; + struct addrinfo hints; + instanceData *pData; + int bBindRequired = 0; + const char *address; + DEFiRet; + + if(pWrkrData->bIsConnected) + FINALIZE; + pData = pWrkrData->pData; + + /* The remote address is not yet known and needs to be obtained */ + if(pData->protocol == FORW_UDP) { + memset(&hints, 0, sizeof(hints)); + /* port must be numeric, because config file syntax requires this */ + hints.ai_flags = AI_NUMERICSERV; + hints.ai_family = glbl.GetDefPFFamily(runModConf->pConf); + hints.ai_socktype = SOCK_DGRAM; + if((iErr = (getaddrinfo(pData->target, pData->port, &hints, &res))) != 0) { + LogError(0, RS_RET_SUSPENDED, + "omfwd: could not get addrinfo for hostname '%s':'%s': %s", + pData->target, pData->port, gai_strerror(iErr)); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + address = pData->target; + if(pData->address) { + struct addrinfo *addr; + /* The AF of the bind addr must match that of target */ + hints.ai_family = res->ai_family; + hints.ai_flags |= AI_PASSIVE; + iErr = getaddrinfo(pData->address, pData->port, &hints, &addr); + freeaddrinfo(addr); + if(iErr != 0) { + LogError(0, RS_RET_SUSPENDED, + "omfwd: cannot use bind address '%s' for host '%s': %s", + pData->address, pData->target, gai_strerror(iErr)); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + bBindRequired = 1; + address = pData->address; + } + DBGPRINTF("%s found, resuming.\n", pData->target); + pWrkrData->f_addr = res; + res = NULL; + if(pWrkrData->pSockArray == NULL) { + CHKiRet(changeToNs(pData)); + pWrkrData->pSockArray = net.create_udp_socket((uchar*)address, + NULL, bBindRequired, 0, pData->UDPSendBuf, pData->ipfreebind, pData->device); + CHKiRet(returnToOriginalNs(pData)); + } + if(pWrkrData->pSockArray != NULL) { + pWrkrData->bIsConnected = 1; + } + } else { + CHKiRet(changeToNs(pData)); + CHKiRet(TCPSendInit((void*)pWrkrData)); + CHKiRet(returnToOriginalNs(pData)); + } + +finalize_it: + DBGPRINTF("omfwd: doTryResume %s iRet %d\n", pWrkrData->pData->target, iRet); + if(res != NULL) { + freeaddrinfo(res); + } + if(iRet != RS_RET_OK) { + returnToOriginalNs(pData); + if(pWrkrData->f_addr != NULL) { + freeaddrinfo(pWrkrData->f_addr); + pWrkrData->f_addr = NULL; + } + iRet = RS_RET_SUSPENDED; + } + + RETiRet; +} + + +BEGINtryResume +CODESTARTtryResume + dbgprintf("omfwd: tryResume: pWrkrData %p\n", pWrkrData); + iRet = doTryResume(pWrkrData); +ENDtryResume + + +BEGINbeginTransaction +CODESTARTbeginTransaction + dbgprintf("omfwd: beginTransaction\n"); + iRet = doTryResume(pWrkrData); +ENDbeginTransaction + + +static rsRetVal +processMsg(wrkrInstanceData_t *__restrict__ const pWrkrData, + actWrkrIParams_t *__restrict__ const iparam) +{ + uchar *psz; /* temporary buffering */ + register unsigned l; + int iMaxLine; + Bytef *out = NULL; /* for compression */ + instanceData *__restrict__ const pData = pWrkrData->pData; + DEFiRet; + + iMaxLine = glbl.GetMaxLine(runModConf->pConf); + + psz = iparam->param; + l = iparam->lenStr; + if((int) l > iMaxLine) + l = iMaxLine; + + /* Check if we should compress and, if so, do it. We also + * check if the message is large enough to justify compression. + * The smaller the message, the less likely is a gain in compression. + * To save CPU cycles, we do not try to compress very small messages. + * What "very small" means needs to be configured. Currently, it is + * hard-coded but this may be changed to a config parameter. + * rgerhards, 2006-11-30 + */ + if(pData->compressionMode == COMPRESS_SINGLE_MSG && (l > CONF_MIN_SIZE_FOR_COMPRESS)) { + uLongf destLen = iMaxLine + iMaxLine/100 +12; /* recommended value from zlib doc */ + uLong srcLen = l; + int ret; + CHKmalloc(out = (Bytef*) malloc(destLen)); + out[0] = 'z'; + out[1] = '\0'; + ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz, + srcLen, pData->compressionLevel); + dbgprintf("Compressing message, length was %d now %d, return state %d.\n", + l, (int) destLen, ret); + if(ret != Z_OK) { + /* if we fail, we complain, but only in debug mode + * Otherwise, we are silent. In any case, we ignore the + * failed compression and just sent the uncompressed + * data, which is still valid. So this is probably the + * best course of action. + * rgerhards, 2006-11-30 + */ + dbgprintf("Compression failed, sending uncompressed message\n"); + } else if(destLen+1 < l) { + /* only use compression if there is a gain in using it! */ + dbgprintf("there is gain in compression, so we do it\n"); + psz = out; + l = destLen + 1; /* take care for the "z" at message start! */ + } + ++destLen; + } + + if(pData->protocol == FORW_UDP) { + /* forward via UDP */ + CHKiRet(UDPSend(pWrkrData, psz, l)); + } else { + /* forward via TCP */ + iRet = tcpclt.Send(pWrkrData->pTCPClt, pWrkrData, (char *)psz, l); + if(iRet != RS_RET_OK && iRet != RS_RET_DEFER_COMMIT && iRet != RS_RET_PREVIOUS_COMMITTED) { + /* error! */ + LogError(0, iRet, "omfwd: error forwarding via tcp to %s:%s, suspending action", + pWrkrData->pData->target, pWrkrData->pData->port); + DestructTCPInstanceData(pWrkrData); + iRet = RS_RET_SUSPENDED; + } + } +finalize_it: + free(out); /* is NULL if it was never used... */ + RETiRet; +} + +BEGINcommitTransaction + unsigned i; + char namebuf[264]; /* 256 for FGDN, 5 for port and 3 for transport => 264 */ +CODESTARTcommitTransaction + CHKiRet(doTryResume(pWrkrData)); + + DBGPRINTF(" %s:%s/%s\n", pWrkrData->pData->target, pWrkrData->pData->port, + pWrkrData->pData->protocol == FORW_UDP ? "udp" : "tcp"); + + if(pWrkrData->pData->ratelimiter) { + snprintf(namebuf, sizeof namebuf, "%s:[%s]:%s", + pWrkrData->pData->protocol == FORW_UDP ? "udp" : "tcp", + pWrkrData->pData->target, + pWrkrData->pData->port); + } + + for(i = 0 ; i < nParams ; ++i) { + /* If rate limiting is enabled, check whether this message has to be discarded */ + if(pWrkrData->pData->ratelimiter) { + iRet = ratelimitMsgCount(pWrkrData->pData->ratelimiter, 0, namebuf); + if (iRet == RS_RET_DISCARDMSG) { + iRet = RS_RET_OK; + continue; + } else if (iRet != RS_RET_OK) { + LogError(0, RS_RET_ERR, "omfwd: error during rate limit : %d.\n",iRet); + } + } + iRet = processMsg(pWrkrData, &actParam(pParams, 1, i, 0)); + if(iRet != RS_RET_OK && iRet != RS_RET_DEFER_COMMIT && iRet != RS_RET_PREVIOUS_COMMITTED) + FINALIZE; + } + + if(pWrkrData->offsSndBuf != 0) { + iRet = TCPSendBuf(pWrkrData, pWrkrData->sndBuf, pWrkrData->offsSndBuf, IS_FLUSH); + pWrkrData->offsSndBuf = 0; + } +finalize_it: +ENDcommitTransaction + + +/* This function loads TCP support, if not already loaded. It will be called + * during config processing. To server ressources, TCP support will only + * be loaded if it actually is used. -- rgerhard, 2008-04-17 + */ +static rsRetVal +loadTCPSupport(void) +{ + DEFiRet; + CHKiRet(objUse(netstrms, LM_NETSTRMS_FILENAME)); + CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME)); + CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME)); + +finalize_it: + RETiRet; +} + + +/* initialize TCP structures (if necessary) after the instance has been + * created. + */ +static rsRetVal +initTCP(wrkrInstanceData_t *pWrkrData) +{ + instanceData *pData; + DEFiRet; + + pData = pWrkrData->pData; + if(pData->protocol == FORW_TCP) { + /* create our tcpclt */ + CHKiRet(tcpclt.Construct(&pWrkrData->pTCPClt)); + CHKiRet(tcpclt.SetResendLastOnRecon(pWrkrData->pTCPClt, pData->bResendLastOnRecon)); + /* and set callbacks */ + CHKiRet(tcpclt.SetSendInit(pWrkrData->pTCPClt, TCPSendInit)); + CHKiRet(tcpclt.SetSendFrame(pWrkrData->pTCPClt, TCPSendFrame)); + CHKiRet(tcpclt.SetSendPrepRetry(pWrkrData->pTCPClt, TCPSendPrepRetry)); + CHKiRet(tcpclt.SetFraming(pWrkrData->pTCPClt, pData->tcp_framing)); + CHKiRet(tcpclt.SetFramingDelimiter(pWrkrData->pTCPClt, pData->tcp_framingDelimiter)); + CHKiRet(tcpclt.SetRebindInterval(pWrkrData->pTCPClt, pData->iRebindInterval)); + } +finalize_it: + RETiRet; +} + + +static void +setInstParamDefaults(instanceData *pData) +{ + pData->tplName = NULL; + pData->protocol = FORW_UDP; + pData->networkNamespace = NULL; + pData->originalNamespace = -1; + pData->tcp_framing = TCP_FRAMING_OCTET_STUFFING; + pData->tcp_framingDelimiter = '\n'; + pData->pszStrmDrvr = NULL; + pData->pszStrmDrvrAuthMode = NULL; + pData->pszStrmDrvrPermitExpiredCerts = NULL; + pData->iStrmDrvrMode = 0; + pData->iStrmDrvrExtendedCertCheck = 0; + pData->iStrmDrvrSANPreference = 0; + pData->iStrmTlsVerifyDepth = 0; + pData->pszStrmDrvrCAFile = NULL; + pData->pszStrmDrvrCRLFile = NULL; + pData->pszStrmDrvrKeyFile = NULL; + pData->pszStrmDrvrCertFile = NULL; + pData->iRebindInterval = 0; + pData->bKeepAlive = 0; + pData->iKeepAliveProbes = 0; + pData->iKeepAliveIntvl = 0; + pData->iKeepAliveTime = 0; + pData->iConErrSkip = 0; + pData->gnutlsPriorityString = NULL; + pData->bResendLastOnRecon = 0; + pData->bSendToAll = -1; /* unspecified */ + pData->iUDPSendDelay = 0; + pData->UDPSendBuf = 0; + pData->pPermPeers = NULL; + pData->compressionLevel = 9; + pData->strmCompFlushOnTxEnd = 1; + pData->compressionMode = COMPRESS_NEVER; + pData->ipfreebind = IPFREEBIND_ENABLED_WITH_LOG; + pData->ratelimiter = NULL; + pData->ratelimitInterval = 0; + pData->ratelimitBurst = 200; +} + + +static rsRetVal +setupInstStatsCtrs(instanceData *__restrict__ const pData) +{ + uchar ctrName[512]; + DEFiRet; + + /* support statistics gathering */ + snprintf((char*)ctrName, sizeof(ctrName), "%s-%s-%s", + (pData->protocol == FORW_TCP) ? "TCP" : "UDP", + pData->target, pData->port); + ctrName[sizeof(ctrName)-1] = '\0'; /* be on the save side */ + CHKiRet(statsobj.Construct(&(pData->stats))); + CHKiRet(statsobj.SetName(pData->stats, ctrName)); + CHKiRet(statsobj.SetOrigin(pData->stats, (uchar*)"omfwd")); + pData->sentBytes = 0; + INIT_ATOMIC_HELPER_MUT64(pData->mut_sentBytes); + CHKiRet(statsobj.AddCounter(pData->stats, UCHAR_CONSTANT("bytes.sent"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pData->sentBytes))); + CHKiRet(statsobj.ConstructFinalize(pData->stats)); + +finalize_it: + RETiRet; +} + + +BEGINnewActInst + struct cnfparamvals *pvals; + uchar *tplToUse; + char *cstr; + int i; + rsRetVal localRet; + int complevel = -1; +CODESTARTnewActInst + DBGPRINTF("newActInst (omfwd)\n"); + + pvals = nvlstGetParams(lst, &actpblk, NULL); + if(pvals == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("action param blk in omfwd:\n"); + cnfparamsPrint(&actpblk, pvals); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "target")) { + pData->target = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "address")) { + pData->address = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "device")) { + pData->device = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "port")) { + pData->port = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "protocol")) { + if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"udp", 3)) { + pData->protocol = FORW_UDP; + } else if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"tcp", 3)) { + localRet = loadTCPSupport(); + if(localRet != RS_RET_OK) { + LogError(0, localRet, "could not activate network stream modules for TCP " + "(internal error %d) - are modules missing?", localRet); + ABORT_FINALIZE(localRet); + } + pData->protocol = FORW_TCP; + } else { + uchar *str; + str = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + LogError(0, RS_RET_INVLD_PROTOCOL, + "omfwd: invalid protocol \"%s\"", str); + free(str); + ABORT_FINALIZE(RS_RET_INVLD_PROTOCOL); + } + } else if(!strcmp(actpblk.descr[i].name, "networknamespace")) { + pData->networkNamespace = es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "tcp_framing")) { + if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"traditional", 11)) { + pData->tcp_framing = TCP_FRAMING_OCTET_STUFFING; + } else if(!es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"octet-counted", 13)) { + pData->tcp_framing = TCP_FRAMING_OCTET_COUNTING; + } else { + uchar *str; + str = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + LogError(0, RS_RET_CNF_INVLD_FRAMING, + "omfwd: invalid framing \"%s\"", str); + free(str); + ABORT_FINALIZE(RS_RET_CNF_INVLD_FRAMING ); + } + } else if(!strcmp(actpblk.descr[i].name, "rebindinterval")) { + pData->iRebindInterval = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "keepalive")) { + pData->bKeepAlive = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "keepalive.probes")) { + pData->iKeepAliveProbes = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "keepalive.interval")) { + pData->iKeepAliveIntvl = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "keepalive.time")) { + pData->iKeepAliveTime = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "conerrskip")) { + pData->iConErrSkip = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "gnutlsprioritystring")) { + pData->gnutlsPriorityString = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdriver")) { + pData->pszStrmDrvr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdrivermode")) { + pData->iStrmDrvrMode = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.CheckExtendedKeyPurpose")) { + pData->iStrmDrvrExtendedCertCheck = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.PrioritizeSAN")) { + pData->iStrmDrvrSANPreference = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.TlsVerifyDepth")) { + if (pvals[i].val.d.n >= 2) { + pData->iStrmTlsVerifyDepth = pvals[i].val.d.n; + } else { + parser_errmsg("streamdriver.TlsVerifyDepth must be 2 or higher but is %d", + (int) pvals[i].val.d.n); + } + } else if(!strcmp(actpblk.descr[i].name, "streamdriverauthmode")) { + pData->pszStrmDrvrAuthMode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.permitexpiredcerts")) { + uchar *val = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + if( es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"off", 3) + && es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"on", 2) + && es_strcasebufcmp(pvals[i].val.d.estr, (uchar*)"warn", 4) + ) { + parser_errmsg("streamdriver.permitExpiredCerts must be 'warn', 'off' or 'on' " + "but is '%s' - ignoring parameter, using 'off' instead.", val); + free(val); + } else { + pData->pszStrmDrvrPermitExpiredCerts = val; + } + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.cafile")) { + pData->pszStrmDrvrCAFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.crlfile")) { + pData->pszStrmDrvrCRLFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.keyfile")) { + pData->pszStrmDrvrKeyFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdriver.certfile")) { + pData->pszStrmDrvrCertFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "streamdriverpermittedpeers")) { + uchar *start, *str; + uchar *p; + int lenStr; + str = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + start = str; + lenStr = ustrlen(start); /* we need length after '\0' has been dropped... */ + while(lenStr > 0) { + p = start; + while(*p && *p != ',' && lenStr--) + p++; + if(*p == ',') { + *p = '\0'; + } + if(*start == '\0') { + DBGPRINTF("omfwd: ignoring empty permitted peer\n"); + } else { + dbgprintf("omfwd: adding permitted peer: '%s'\n", start); + CHKiRet(net.AddPermittedPeer(&(pData->pPermPeers), start)); + } + start = p+1; + if(lenStr) + --lenStr; + } + free(str); + } else if(!strcmp(actpblk.descr[i].name, "ziplevel")) { + complevel = pvals[i].val.d.n; + if(complevel >= 0 && complevel <= 10) { + pData->compressionLevel = complevel; + pData->compressionMode = COMPRESS_SINGLE_MSG; + } else { + LogError(0, NO_ERRCODE, "Invalid ziplevel %d specified in " + "forwarding action - NOT turning on compression.", + complevel); + } + } else if(!strcmp(actpblk.descr[i].name, "tcp_framedelimiter")) { + if(pvals[i].val.d.n > 255) { + parser_errmsg("tcp_frameDelimiter must be below 255 but is %d", + (int) pvals[i].val.d.n); + ABORT_FINALIZE(RS_RET_PARAM_ERROR); + } + pData->tcp_framingDelimiter = (uchar) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "resendlastmsgonreconnect")) { + pData->bResendLastOnRecon = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "udp.sendtoall")) { + pData->bSendToAll = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "udp.senddelay")) { + pData->iUDPSendDelay = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "udp.sendbuf")) { + pData->UDPSendBuf = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "compression.stream.flushontxend")) { + pData->strmCompFlushOnTxEnd = (sbool) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "compression.mode")) { + cstr = es_str2cstr(pvals[i].val.d.estr, NULL); + if(!strcasecmp(cstr, "stream:always")) { + pData->compressionMode = COMPRESS_STREAM_ALWAYS; + } else if(!strcasecmp(cstr, "none")) { + pData->compressionMode = COMPRESS_NEVER; + } else if(!strcasecmp(cstr, "single")) { + pData->compressionMode = COMPRESS_SINGLE_MSG; + } else { + LogError(0, RS_RET_PARAM_ERROR, "omfwd: invalid value for 'compression.mode' " + "parameter (given is '%s')", cstr); + free(cstr); + ABORT_FINALIZE(RS_RET_PARAM_ERROR); + } + free(cstr); + } else if(!strcmp(actpblk.descr[i].name, "ipfreebind")) { + pData->ipfreebind = (int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "ratelimit.burst")) { + pData->ratelimitBurst = (unsigned int) pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "ratelimit.interval")) { + pData->ratelimitInterval = (unsigned int) pvals[i].val.d.n; + } else { + LogError(0, RS_RET_INTERNAL_ERROR, + "omfwd: program error, non-handled parameter '%s'", + actpblk.descr[i].name); + } + } + + /* check if no port is set. If so, we use the IANA-assigned port of 514 */ + if(pData->port == NULL) { + CHKmalloc(pData->port = strdup("514")); + } + + if(complevel != -1) { + pData->compressionLevel = complevel; + if(pData->compressionMode == COMPRESS_NEVER) { + /* to keep compatible with pre-7.3.11, only setting the + * compresion level means old-style single-message mode. + */ + pData->compressionMode = COMPRESS_SINGLE_MSG; + } + } + + CODE_STD_STRING_REQUESTnewActInst(1) + + tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); + CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS)); + + if(pData->bSendToAll == -1) { + pData->bSendToAll = send_to_all; + } else { + if(pData->protocol == FORW_TCP) { + LogError(0, RS_RET_PARAM_ERROR, "omfwd: parameter udp.sendToAll " + "cannot be used with tcp transport -- ignored"); + } + } + + if(pData->address && (pData->protocol == FORW_TCP)) { + LogError(0, RS_RET_PARAM_ERROR, + "omfwd: parameter \"address\" not supported for tcp -- ignored"); + } + + if( pData->ratelimitInterval > 0) { + CHKiRet(ratelimitNew(&pData->ratelimiter, "omfwd", NULL)); + ratelimitSetLinuxLike(pData->ratelimiter, pData->ratelimitInterval, pData->ratelimitBurst); + ratelimitSetNoTimeCache(pData->ratelimiter); + } + + setupInstStatsCtrs(pData); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + +BEGINparseSelectorAct + uchar *q; + int i; + rsRetVal localRet; + struct addrinfo; + TCPFRAMINGMODE tcp_framing = TCP_FRAMING_OCTET_STUFFING; +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + if(*p != '@') + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + + CHKiRet(createInstance(&pData)); + pData->tcp_framingDelimiter = '\n'; + + ++p; /* eat '@' */ + if(*p == '@') { /* indicator for TCP! */ + localRet = loadTCPSupport(); + if(localRet != RS_RET_OK) { + LogError(0, localRet, "could not activate network stream modules for TCP " + "(internal error %d) - are modules missing?", localRet); + ABORT_FINALIZE(localRet); + } + pData->protocol = FORW_TCP; + ++p; /* eat this '@', too */ + } else { + pData->protocol = FORW_UDP; + } + /* we are now after the protocol indicator. Now check if we should + * use compression. We begin to use a new option format for this: + * @(option,option)host:port + * The first option defined is "z[0..9]" where the digit indicates + * the compression level. If it is not given, 9 (best compression) is + * assumed. An example action statement might be: + * @@(z5,o)127.0.0.1:1400 + * Which means send via TCP with medium (5) compresion (z) to the local + * host on port 1400. The '0' option means that octet-couting (as in + * IETF I-D syslog-transport-tls) is to be used for framing (this option + * applies to TCP-based syslog only and is ignored when specified with UDP). + * That is not yet implemented. + * rgerhards, 2006-12-07 + * In order to support IPv6 addresses, we must introduce an extension to + * the hostname. If it is in square brackets, whatever is in them is treated as + * the hostname - without any exceptions ;) -- rgerhards, 2008-08-05 + */ + if(*p == '(') { + /* at this position, it *must* be an option indicator */ + do { + ++p; /* eat '(' or ',' (depending on when called) */ + /* check options */ + if(*p == 'z') { /* compression */ + ++p; /* eat */ + if(isdigit((int) *p)) { + int iLevel; + iLevel = *p - '0'; + ++p; /* eat */ + pData->compressionLevel = iLevel; + pData->compressionMode = COMPRESS_SINGLE_MSG; + } else { + LogError(0, NO_ERRCODE, "Invalid compression level '%c' specified in " + "forwarding action - NOT turning on compression.", + *p); + } + } else if(*p == 'o') { /* octet-couting based TCP framing? */ + ++p; /* eat */ + /* no further options settable */ + tcp_framing = TCP_FRAMING_OCTET_COUNTING; + } else { /* invalid option! Just skip it... */ + LogError(0, NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p); + ++p; /* eat invalid option */ + } + /* the option processing is done. We now do a generic skip + * to either the next option or the end of the option + * block. + */ + while(*p && *p != ')' && *p != ',') + ++p; /* just skip it */ + } while(*p && *p == ','); /* Attention: do.. while() */ + if(*p == ')') + ++p; /* eat terminator, on to next */ + else + /* we probably have end of string - leave it for the rest + * of the code to handle it (but warn the user) + */ + LogError(0, NO_ERRCODE, "Option block not terminated in forwarding action."); + } + + /* extract the host first (we do a trick - we replace the ';' or ':' with a '\0') + * now skip to port and then template name. rgerhards 2005-07-06 + */ + if(*p == '[') { /* everything is hostname upto ']' */ + ++p; /* skip '[' */ + for(q = p ; *p && *p != ']' ; ++p) + /* JUST SKIP */; + if(*p == ']') { + *p = '\0'; /* trick to obtain hostname (later)! */ + ++p; /* eat it */ + } + } else { /* traditional view of hostname */ + for(q = p ; *p && *p != ';' && *p != ':' && *p != '#' ; ++p) + /* JUST SKIP */; + } + + pData->tcp_framing = tcp_framing; + pData->port = NULL; + pData->networkNamespace = NULL; + if(*p == ':') { /* process port */ + uchar * tmp; + + *p = '\0'; /* trick to obtain hostname (later)! */ + tmp = ++p; + for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i) + /* SKIP AND COUNT */; + pData->port = malloc(i + 1); + if(pData->port == NULL) { + LogError(0, NO_ERRCODE, "Could not get memory to store syslog forwarding port, " + "using default port, results may not be what you intend"); + /* we leave f_forw.port set to NULL, this is then handled below */ + } else { + memcpy(pData->port, tmp, i); + *(pData->port + i) = '\0'; + } + } + /* check if no port is set. If so, we use the IANA-assigned port of 514 */ + if(pData->port == NULL) { + CHKmalloc(pData->port = strdup("514")); + } + + /* now skip to template */ + while(*p && *p != ';' && *p != '#' && !isspace((int) *p)) + ++p; /*JUST SKIP*/ + + if(*p == ';' || *p == '#' || isspace(*p)) { + uchar cTmp = *p; + *p = '\0'; /* trick to obtain hostname (later)! */ + CHKmalloc(pData->target = strdup((char*) q)); + *p = cTmp; + } else { + CHKmalloc(pData->target = strdup((char*) q)); + } + + /* copy over config data as needed */ + pData->iRebindInterval = (pData->protocol == FORW_TCP) ? + cs.iTCPRebindInterval : cs.iUDPRebindInterval; + + pData->bKeepAlive = cs.bKeepAlive; + pData->iKeepAliveProbes = cs.iKeepAliveProbes; + pData->iKeepAliveIntvl = cs.iKeepAliveIntvl; + pData->iKeepAliveTime = cs.iKeepAliveTime; + pData->iConErrSkip = cs.iConErrSkip; + + /* process template */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, getDfltTpl())); + + if(pData->protocol == FORW_TCP) { + pData->bResendLastOnRecon = cs.bResendLastOnRecon; + pData->iStrmDrvrMode = cs.iStrmDrvrMode; + if(cs.pPermPeers != NULL) { + pData->pPermPeers = cs.pPermPeers; + cs.pPermPeers = NULL; + } + } +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +/* a common function to free our configuration variables - used both on exit + * and on $ResetConfig processing. -- rgerhards, 2008-05-16 + */ +static void +freeConfigVars(void) +{ + free(cs.pszStrmDrvr); + cs.pszStrmDrvr = NULL; + free(cs.pszStrmDrvrAuthMode); + cs.pszStrmDrvrAuthMode = NULL; + free(cs.pPermPeers); + cs.pPermPeers = NULL; /* TODO: fix in older builds! */ +} + + +BEGINmodExit +CODESTARTmodExit + /* release what we no longer need */ + objRelease(glbl, CORE_COMPONENT); + objRelease(net, LM_NET_FILENAME); + objRelease(netstrm, LM_NETSTRMS_FILENAME); + objRelease(netstrms, LM_NETSTRMS_FILENAME); + objRelease(tcpclt, LM_TCPCLT_FILENAME); + objRelease(statsobj, CORE_COMPONENT); + freeConfigVars(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMODTX_QUERIES +CODEqueryEtryPt_STD_OMOD8_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +ENDqueryEtryPt + + +/* Reset config variables for this module to default values. + * rgerhards, 2008-03-28 + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + freeConfigVars(); + + /* we now must reset all non-string values */ + cs.iStrmDrvrMode = 0; + cs.bResendLastOnRecon = 0; + cs.iUDPRebindInterval = 0; + cs.iTCPRebindInterval = 0; + cs.bKeepAlive = 0; + cs.iKeepAliveProbes = 0; + cs.iKeepAliveIntvl = 0; + cs.iKeepAliveTime = 0; + cs.iConErrSkip = 0; + + return RS_RET_OK; +} + + +BEGINmodInit(Fwd) +CODESTARTmodInit +INITLegCnfVars + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(net,LM_NET_FILENAME)); + CHKiRet(objUse(statsobj, CORE_COMPONENT)); + + CHKiRet(regCfSysLineHdlr((uchar *)"actionforwarddefaulttemplate", 0, eCmdHdlrGetWord, + setLegacyDfltTpl, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcprebindinterval", 0, eCmdHdlrInt, + NULL, &cs.iTCPRebindInterval, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendudprebindinterval", 0, eCmdHdlrInt, + NULL, &cs.iUDPRebindInterval, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcpkeepalive", 0, eCmdHdlrBinary, + NULL, &cs.bKeepAlive, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcpkeepalive_probes", 0, eCmdHdlrInt, + NULL, &cs.iKeepAliveProbes, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcpkeepalive_intvl", 0, eCmdHdlrInt, + NULL, &cs.iKeepAliveIntvl, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendtcpkeepalive_time", 0, eCmdHdlrInt, + NULL, &cs.iKeepAliveTime, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriver", 0, eCmdHdlrGetWord, + NULL, &cs.pszStrmDrvr, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdrivermode", 0, eCmdHdlrInt, + NULL, &cs.iStrmDrvrMode, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverauthmode", 0, eCmdHdlrGetWord, + NULL, &cs.pszStrmDrvrAuthMode, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendstreamdriverpermittedpeer", 0, eCmdHdlrGetWord, + setPermittedPeer, NULL, NULL)); + CHKiRet(regCfSysLineHdlr((uchar *)"actionsendresendlastmsgonreconnect", 0, eCmdHdlrBinary, + NULL, &cs.bResendLastOnRecon, NULL)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, + resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit diff --git a/tools/omfwd.h b/tools/omfwd.h new file mode 100644 index 0000000..c4f9074 --- /dev/null +++ b/tools/omfwd.h @@ -0,0 +1,34 @@ +/* omfwd.h + * These are the definitions for the build-in forwarding output module. + * + * File begun on 2007-07-13 by RGerhards + * + * Copyright 2007-2012 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. + */ +#ifndef OMFWD_H_INCLUDED +#define OMFWD_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitFwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), +rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef OMFWD_H_INCLUDED */ +/* + * vi:set ai: + */ diff --git a/tools/ompipe.c b/tools/ompipe.c new file mode 100644 index 0000000..a3c460f --- /dev/null +++ b/tools/ompipe.c @@ -0,0 +1,448 @@ +/* ompipe.c + * This is the implementation of the build-in pipe output module. + * Note that this module stems back to the "old" (4.4.2 and below) + * omfile. There were some issues with the new omfile code and pipes + * (namely in regard to xconsole), so we took out the pipe code and moved + * that to a separate module. That a) immediately solves the issue for a + * less common use case and probably makes it much easier to enhance + * file and pipe support (now independently) in the future (we always + * needed to think about pipes in omfile so far, what we now no longer + * need to, hopefully resulting in reduction of complexity). + * + * NOTE: read comments in module-template.h to understand how this pipe + * works! + * + * Copyright 2007-2018 Rainer Gerhards and 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 <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/file.h> + +#include "rsyslog.h" +#include "syslogd.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "template.h" +#include "ompipe.h" +#include "omfile.h" /* for dirty trick: access to $ActionFileDefaultTemplate value */ +#include "cfsysline.h" +#include "module-template.h" +#include "conf.h" +#include "errmsg.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("ompipe") + +/* internal structures + */ +DEF_OMOD_STATIC_DATA + + +typedef struct _instanceData { + uchar *pipe; /* pipe or template name (display only) */ + uchar *tplName; /* format template to use */ + short fd; /* pipe descriptor for (current) pipe */ + pthread_mutex_t mutWrite; /* guard against multiple instances writing to same pipe */ + sbool bHadError; /* did we already have/report an error on this pipe? */ + sbool bTryResumeReopen; /* should we attempt to reopen the pipe on action resume? */ +} instanceData; + +typedef struct wrkrInstanceData { + instanceData *pData; +} wrkrInstanceData_t; + +typedef struct configSettings_s { + EMPTY_STRUCT +} configSettings_t; +static configSettings_t __attribute__((unused)) cs; + +/* tables for interfacing with the v6 config system */ +/* module-global parameters */ +static struct cnfparamdescr modpdescr[] = { + { "template", eCmdHdlrGetWord, 0 }, +}; +static struct cnfparamblk modpblk = + { CNFPARAMBLK_VERSION, + sizeof(modpdescr)/sizeof(struct cnfparamdescr), + modpdescr + }; + +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "pipe", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "template", eCmdHdlrGetWord, 0 }, + { "tryResumeReopen", eCmdHdlrBinary, 0 }, +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + uchar *tplName; /* default template */ +}; + +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 */ + +/* this function gets the default template */ +static uchar* +getDfltTpl(void) +{ + if(loadModConf != NULL && loadModConf->tplName != NULL) + return loadModConf->tplName; + else + return (uchar*)"RSYSLOG_FileFormat"; +} + + +BEGINinitConfVars /* (re)set config variables to default values */ +CODESTARTinitConfVars +ENDinitConfVars + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + dbgprintf("pipe %s", pData->pipe); + if (pData->fd == -1) + dbgprintf(" (unused)"); +ENDdbgPrintInstInfo + + +/* This is now shared code for all types of files. It simply prepares + * pipe access, which, among others, means the the pipe wil be opened + * and any directories in between will be created (based on config, of + * course). -- rgerhards, 2008-10-22 + * changed to iRet interface - 2009-03-19 + */ +static rsRetVal +preparePipe(instanceData *pData) +{ + DEFiRet; + pData->fd = open((char*) pData->pipe, O_RDWR|O_NONBLOCK|O_CLOEXEC); + if(pData->fd < 0 ) { + pData->fd = -1; + if(!pData->bHadError) { + LogError(errno, RS_RET_NO_FILE_ACCESS, "Could not open output pipe '%s':", + pData->pipe); + pData->bHadError = 1; + } + DBGPRINTF("Error opening log pipe: %s\n", pData->pipe); + } + RETiRet; +} + + +/* rgerhards 2004-11-11: write to a pipe output. This + * will be called for all outputs using pipe semantics, + * for example also for pipes. + */ +static rsRetVal writePipe(uchar **ppString, instanceData *pData) +{ + int iLenWritten; + DEFiRet; + + assert(pData != NULL); + + if(pData->fd == -1) { + rsRetVal iRetLocal; + iRetLocal = preparePipe(pData); + if((iRetLocal != RS_RET_OK) || (pData->fd == -1)) + ABORT_FINALIZE(RS_RET_SUSPENDED); /* whatever the failure was, we need to retry */ + } + + /* create the message based on format specified */ + iLenWritten = write(pData->fd, ppString[0], strlen((char*)ppString[0])); + if(iLenWritten < 0) { + const int e = errno; + /* If a named pipe is full, we suspend this action for a while */ + if(e == EAGAIN) + ABORT_FINALIZE(RS_RET_SUSPENDED); + + close(pData->fd); + pData->fd = -1; /* tell that fd is no longer open! */ + iRet = RS_RET_SUSPENDED; + LogError(e, NO_ERRCODE, "write error on pipe %s", pData->pipe); + } + +finalize_it: + RETiRet; +} + + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + pModConf->tplName = NULL; +ENDbeginCnfLoad + +BEGINsetModCnf + struct cnfparamvals *pvals = NULL; + int i; +CODESTARTsetModCnf + pvals = nvlstGetParams(lst, &modpblk, NULL); + if(pvals == NULL) { + 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 ompipe:\n"); + cnfparamsPrint(&modpblk, pvals); + } + + for(i = 0 ; i < modpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(modpblk.descr[i].name, "template")) { + loadModConf->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + if(pszFileDfltTplName != NULL) { + LogError(0, RS_RET_DUP_PARAM, "ompipe: warning: default template " + "was already set via legacy directive - may lead to inconsistent " + "results."); + } + } else { + dbgprintf("ompipe: program error, non-handled " + "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); + } + } +finalize_it: + if(pvals != NULL) + cnfparamvalsDestruct(pvals, &modpblk); +ENDsetModCnf + +BEGINendCnfLoad +CODESTARTendCnfLoad + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(pszFileDfltTplName); + pszFileDfltTplName = NULL; +ENDendCnfLoad + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + +BEGINactivateCnf +CODESTARTactivateCnf + runModConf = pModConf; +ENDactivateCnf + +BEGINfreeCnf +CODESTARTfreeCnf + free(pModConf->tplName); +ENDfreeCnf + +BEGINcreateInstance +CODESTARTcreateInstance + pData->pipe = NULL; + pData->fd = -1; + pData->bHadError = 0; + pData->bTryResumeReopen = 0; + pthread_mutex_init(&pData->mutWrite, NULL); +ENDcreateInstance + + +BEGINcreateWrkrInstance +CODESTARTcreateWrkrInstance +ENDcreateWrkrInstance + + +BEGINfreeInstance +CODESTARTfreeInstance + pthread_mutex_destroy(&pData->mutWrite); + free(pData->pipe); + if(pData->fd != -1) + close(pData->fd); +ENDfreeInstance + + +BEGINfreeWrkrInstance +CODESTARTfreeWrkrInstance +ENDfreeWrkrInstance + + +BEGINtryResume + instanceData *__restrict__ const pData = pWrkrData->pData; + fd_set wrds; + struct timeval tv; + int ready; +CODESTARTtryResume + if(pData->fd == -1) { + rsRetVal iRetLocal; + iRetLocal = preparePipe(pData); + if((iRetLocal != RS_RET_OK) || (pData->fd == -1)) + ABORT_FINALIZE(RS_RET_SUSPENDED); + } else { + /* we can reach this if the pipe is full, so we need + * to check if we can write again. /dev/xconsole is the + * ugly example of why this is necessary. + */ + FD_ZERO(&wrds); + FD_SET(pData->fd, &wrds); + tv.tv_sec = 0; + tv.tv_usec = 0; + ready = select(pData->fd+1, NULL, &wrds, NULL, &tv); + DBGPRINTF("ompipe: tryResume: ready to write fd %d: %d\n", pData->fd, ready); + if(ready != 1) { + if(pData->bTryResumeReopen && pData->fd != -1) { + close(pData->fd); + pData->fd = -1; + } + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + } +finalize_it: +ENDtryResume + +BEGINdoAction + instanceData *pData; +CODESTARTdoAction + pData = pWrkrData->pData; + DBGPRINTF("ompipe: writing to %s\n", pData->pipe); + /* this module is single-threaded by nature */ + pthread_mutex_lock(&pData->mutWrite); + iRet = writePipe(ppString, pData); + pthread_mutex_unlock(&pData->mutWrite); +ENDdoAction + + +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->tplName = NULL; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTnewActInst(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "pipe")) { + pData->pipe = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "tryResumeReopen")) { + pData->bTryResumeReopen = (int) pvals[i].val.d.n; + } else { + dbgprintf("ompipe: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*)strdup((pData->tplName == NULL) ? + "RSYSLOG_FileFormat" : (char*)pData->tplName), + OMSR_NO_RQD_TPL_OPTS)); +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + +BEGINparseSelectorAct +CODESTARTparseSelectorAct + /* yes, the if below is redundant, but I need it now. Will go away as + * the code further changes. -- rgerhards, 2007-07-25 + */ + if(*p == '|') { + if((iRet = createInstance(&pData)) != RS_RET_OK) { + return iRet; /* this can not use RET_iRet! */ + } + } else { + /* this is not clean, but we need it for the time being + * TODO: remove when cleaning up modularization + */ + return RS_RET_CONFLINE_UNPROCESSED; + } + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + CHKmalloc(pData->pipe = malloc(512)); + ++p; + CHKiRet(cflineParseFileName(p, (uchar*) pData->pipe, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, + getDfltTpl())); + +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINdoHUP +CODESTARTdoHUP + pthread_mutex_lock(&pData->mutWrite); + if(pData->fd != -1) { + close(pData->fd); + pData->fd = -1; + } + pthread_mutex_unlock(&pData->mutWrite); +ENDdoHUP + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_OMOD8_QUERIES +CODEqueryEtryPt_doHUP +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES +CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(Pipe) +CODESTARTmodInit +INITLegCnfVars + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr +ENDmodInit +/* vi:set ai: + */ diff --git a/tools/ompipe.h b/tools/ompipe.h new file mode 100644 index 0000000..49a5221 --- /dev/null +++ b/tools/ompipe.h @@ -0,0 +1,31 @@ +/* ompipe.h + * These are the definitions for the build-in pipe output module. + * + * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH. + * + * This pipe 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. + */ +#ifndef OMPIPE_H_INCLUDED +#define OMPIPE_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitPipe(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), + rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef OMPIPE_H_INCLUDED */ +/* vi:set ai: + */ diff --git a/tools/omshell.c b/tools/omshell.c new file mode 100644 index 0000000..98f21f9 --- /dev/null +++ b/tools/omshell.c @@ -0,0 +1,163 @@ +/* omshell.c + * This is the implementation of the build-in shell output module. + * + * ************* DO NOT EXTEND THIS MODULE ************** + * This is pure legacy, omprog has much better and more + * secure functionality than this module. It is NOT + * recommended to base new work on it! + * 2012-01-19 rgerhards + * ****************************************************** + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * shell support was initially written by bkalkbrenner 2005-09-20 + * + * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c) + * This file is under development and has not yet arrived at being fully + * self-contained and a real object. So far, it is mostly an excerpt + * of the "old" message code without any modifications. However, it + * helps to have things at the right place one we go to the meat of it. + * + * Copyright 2007-2016 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 "conf.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "omshell.h" +#include "module-template.h" +#include "errmsg.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP + +/* internal structures + */ +DEF_OMOD_STATIC_DATA + +typedef struct _instanceData { + uchar progName[MAXFNAME]; /* program to execute */ +} instanceData; + +typedef struct wrkrInstanceData { + instanceData *pData; +} wrkrInstanceData_t; + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINcreateWrkrInstance +CODESTARTcreateWrkrInstance +ENDcreateWrkrInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance +ENDfreeInstance + + +BEGINfreeWrkrInstance +CODESTARTfreeWrkrInstance +ENDfreeWrkrInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + printf("%s", pData->progName); +ENDdbgPrintInstInfo + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +BEGINdoAction +CODESTARTdoAction + dbgprintf("\n"); + if(execProg((uchar*) pWrkrData->pData->progName, 1, ppString[0]) == 0) + LogError(0, NO_ERRCODE, "Executing program '%s' failed", (char*)pWrkrData->pData->progName); +ENDdoAction + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + /* yes, the if below is redundant, but I need it now. Will go away as + * the code further changes. -- rgerhards, 2007-07-25 + */ + if(*p == '^') { + if((iRet = createInstance(&pData)) != RS_RET_OK) + goto finalize_it; + } + + + switch (*p) + { + case '^': /* bkalkbrenner 2005-09-20: execute shell command */ + dbgprintf("exec\n"); + ++p; + iRet = cflineParseFileName(p, (uchar*) pData->progName, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, + (uchar*)"RSYSLOG_TraditionalFileFormat"); + break; + default: + iRet = RS_RET_CONFLINE_UNPROCESSED; + break; + } + +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_OMOD8_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(Shell) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr +ENDmodInit + +/* + * vi:set ai: + */ diff --git a/tools/omshell.h b/tools/omshell.h new file mode 100644 index 0000000..f0fd0b6 --- /dev/null +++ b/tools/omshell.h @@ -0,0 +1,34 @@ +/* omshell.c + * These are the definitions for the build-in shell output module. + * + * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c) + * + * Copyright 2007-2012 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. + */ +#ifndef ACTSHELL_H_INCLUDED +#define ACTSHELL_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitShell(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef ACTSHELL_H_INCLUDED */ +/* + * vi:set ai: + */ diff --git a/tools/omusrmsg.c b/tools/omusrmsg.c new file mode 100644 index 0000000..479db5b --- /dev/null +++ b/tools/omusrmsg.c @@ -0,0 +1,551 @@ +/* omusrmsg.c + * This is the implementation of the build-in output module for sending + * user messages. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c, which at the + * time of the fork from sysklogd was under BSD license) + * + * Copyright 2007-2018 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 <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <signal.h> +#include <ctype.h> +#include <sys/param.h> +#ifdef HAVE_UTMP_H +# include <utmp.h> +# define STRUCTUTMP struct utmp +# define UTNAME ut_name +#else +# include <utmpx.h> +# define STRUCTUTMP struct utmpx +# define UTNAME ut_user +#endif +#include <unistd.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <errno.h> +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#include <sys/msgbuf.h> +#endif +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif +#ifdef HAVE_LIBSYSTEMD +#include <systemd/sd-daemon.h> +#include <systemd/sd-login.h> +#include <pwd.h> +#endif +#include "rsyslog.h" +#include "srUtils.h" +#include "stringbuf.h" +#include "syslogd-types.h" +#include "conf.h" +#include "omusrmsg.h" +#include "module-template.h" +#include "errmsg.h" + + +/* portability: */ +#ifndef _PATH_DEV +# define _PATH_DEV "/dev/" +#endif + +#ifdef UT_NAMESIZE +# define UNAMESZ UT_NAMESIZE /* length of a login name */ +#else +# define UNAMESZ 32 /* length of a login name, 32 seems current (2018) good bet */ +#endif +#define MAXUNAMES 20 /* maximum number of user names */ + +#ifdef OS_SOLARIS +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omusrmsg") + +/* internal structures + */ +DEF_OMOD_STATIC_DATA + +typedef struct _instanceData { + int bIsWall; /* 1- is wall, 0 - individual users */ + char uname[MAXUNAMES][UNAMESZ+1]; + uchar *tplName; +} instanceData; + +typedef struct wrkrInstanceData { + instanceData *pData; +} wrkrInstanceData_t; + +typedef struct configSettings_s { + EMPTY_STRUCT +} configSettings_t; +static configSettings_t __attribute__((unused)) cs; + + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "users", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + +BEGINinitConfVars /* (re)set config variables to default values */ +CODESTARTinitConfVars +ENDinitConfVars + + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINcreateWrkrInstance +CODESTARTcreateWrkrInstance +ENDcreateWrkrInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + free(pData->tplName); +ENDfreeInstance + + +BEGINfreeWrkrInstance +CODESTARTfreeWrkrInstance +ENDfreeWrkrInstance + + +BEGINdbgPrintInstInfo + register int i; +CODESTARTdbgPrintInstInfo + for (i = 0; i < MAXUNAMES && *pData->uname[i]; i++) + dbgprintf("%s, ", pData->uname[i]); +ENDdbgPrintInstInfo + + +/** + * BSD setutent/getutent() replacement routines + * The following routines emulate setutent() and getutent() under + * BSD because they are not available there. We only emulate what we actually + * need! rgerhards 2005-03-18 + */ +#ifdef OS_BSD +/* Since version 900007, FreeBSD has a POSIX compliant <utmpx.h> */ +#if defined(__FreeBSD__) && (__FreeBSD_version >= 900007) +# define setutent(void) setutxent(void) +# define getutent(void) getutxent(void) +# define endutent(void) endutxent(void) +#else +static FILE *BSD_uf = NULL; +void setutent(void) +{ + assert(BSD_uf == NULL); + if ((BSD_uf = fopen(_PATH_UTMP, "r")) == NULL) { + LogError(errno, NO_ERRCODE, "error opening utmp %s", _PATH_UTMP); + return; + } +} + +STRUCTUTMP* getutent(void) +{ + static STRUCTUTMP st_utmp; + + if(fread((char *)&st_utmp, sizeof(st_utmp), 1, BSD_uf) != 1) + return NULL; + + return(&st_utmp); +} + +void endutent(void) +{ + fclose(BSD_uf); + BSD_uf = NULL; +} +#endif /* if defined(__FreeBSD__) */ +#endif /* #ifdef OS_BSD */ + + +static void sendwallmsg(const char *tty, uchar* pMsg) +{ + uchar szErr[512]; + int errnoSave; + char p[sizeof(_PATH_DEV) + UNAMESZ]; + int ttyf; + struct stat statb; + int wrRet; + + /* compute the device name */ + strcpy(p, _PATH_DEV); + strncat(p, tty, UNAMESZ); + + /* we must be careful when writing to the terminal. A terminal may block + * (for example, a user has pressed <ctl>-s). In that case, we can not + * wait indefinitely. So we need to use non-blocking I/O. In case we would + * block, we simply do not send the message, because that's the best we can + * do. -- rgerhards, 2008-07-04 + */ + + /* open the terminal */ + if((ttyf = open(p, O_WRONLY|O_NOCTTY|O_NONBLOCK)) >= 0) { + if(fstat(ttyf, &statb) == 0 && (statb.st_mode & S_IWRITE)) { + wrRet = write(ttyf, pMsg, strlen((char*)pMsg)); + if(Debug && wrRet == -1) { + /* we record the state to the debug log */ + errnoSave = errno; + rs_strerror_r(errno, (char*)szErr, sizeof(szErr)); + dbgprintf("write to terminal '%s' failed with [%d]:%s\n", + p, errnoSave, szErr); + } + } + close(ttyf); + } +} + +/* WALLMSG -- Write a message to the world at large + * + * Write the specified message to either the entire + * world, or a list of approved users. + * + * rgerhards, 2005-10-19: applying the following sysklogd patch: + * Tue May 4 16:52:01 CEST 2004: Solar Designer <solar@openwall.com> + * Adjust the size of a variable to prevent a buffer overflow + * should _PATH_DEV ever contain something different than "/dev/". + * rgerhards, 2008-07-04: changing the function to no longer use fork() but + * continue run on its thread instead. + */ +static rsRetVal wallmsg(uchar* pMsg, instanceData *pData) +{ + register int i; + STRUCTUTMP ut; + STRUCTUTMP *uptr; + DEFiRet; + + assert(pMsg != NULL); + +#ifdef HAVE_LIBSYSTEMD + if (sd_booted() > 0) { + register int j; + int sdRet; + char **sessions_list; + int sessions = sd_get_sessions(&sessions_list); + + for (j = 0; j < sessions; j++) { + uchar szErr[512]; + char *tty; + const char *user = NULL; + uid_t uid; + struct passwd *pws; + + sdRet = sd_session_get_uid(sessions_list[j], &uid); + if (sdRet >= 0) { + pws = getpwuid(uid); + user = pws->pw_name; /* DO NOT FREE, OS/LIB internal memory! */ + + if (user == NULL) { + dbgprintf("failed to get username for userid '%d'\n", uid); + continue; + } + } else { + /* we record the state to the debug log */ + rs_strerror_r(-sdRet, (char*)szErr, sizeof(szErr)); + dbgprintf("get userid for session '%s' failed with [%d]:%s\n", + sessions_list[j], -sdRet, szErr); + continue; /* try next session */ + } + /* should we send the message to this user? */ + if(pData->bIsWall == 0) { + for(i = 0; i < MAXUNAMES; i++) { + if(!pData->uname[i][0]) { + i = MAXUNAMES; + break; + } + if(strncmp(pData->uname[i], user, UNAMESZ) == 0) + break; + } + if(i == MAXUNAMES) { /* user not found? */ + free(sessions_list[j]); + continue; /* on to next user! */ + } + } + if ((sdRet = sd_session_get_tty(sessions_list[j], &tty)) < 0) { + /* we record the state to the debug log */ + rs_strerror_r(-sdRet, (char*)szErr, sizeof(szErr)); + dbgprintf("get tty for session '%s' failed with [%d]:%s\n", + sessions_list[j], -sdRet, szErr); + free(sessions_list[j]); + continue; /* try next session */ + } + + sendwallmsg(tty, pMsg); + + free(tty); + free(sessions_list[j]); + } + free(sessions_list); + } else { +#endif + + /* open the user login file */ + setutent(); + + /* scan the user login file */ + while((uptr = getutent())) { + memcpy(&ut, uptr, sizeof(ut)); + /* is this slot used? */ + if(ut.UTNAME[0] == '\0') + continue; +#ifndef OS_BSD + if(ut.ut_type != USER_PROCESS) + continue; +#endif + if(!(memcmp (ut.UTNAME,"LOGIN", 6))) /* paranoia */ + continue; + + /* should we send the message to this user? */ + if(pData->bIsWall == 0) { + for(i = 0; i < MAXUNAMES; i++) { + if(!pData->uname[i][0]) { + i = MAXUNAMES; + break; + } + if(strncmp(pData->uname[i], ut.UTNAME, UNAMESZ) == 0) + break; + } + if(i == MAXUNAMES) /* user not found? */ + continue; /* on to next user! */ + } + + sendwallmsg(ut.ut_line, pMsg); + } + + /* close the user login file */ + endutent(); +#ifdef HAVE_LIBSYSTEMD + } +#endif + RETiRet; +} + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +BEGINdoAction +CODESTARTdoAction + dbgprintf("\n"); + iRet = wallmsg(ppString[0], pWrkrData->pData); +ENDdoAction + + +static void +populateUsers(instanceData *pData, es_str_t *usrs) +{ + int i; + int iDst; + es_size_t iUsr; + es_size_t len; + uchar *c; + + len = es_strlen(usrs); + c = es_getBufAddr(usrs); + pData->bIsWall = 0; /* write to individual users */ + iUsr = 0; + for(i = 0 ; i < MAXUNAMES && iUsr < len ; ++i) { + for( iDst = 0 + ; iDst < UNAMESZ && iUsr < len && c[iUsr] != ',' + ; ++iDst, ++iUsr) { + pData->uname[i][iDst] = c[iUsr]; + } + pData->uname[i][iDst] = '\0'; + DBGPRINTF("omusrmsg: send to user '%s'\n", pData->uname[i]); + if(iUsr < len && c[iUsr] != ',') { + LogError(0, RS_RET_ERR, "user name '%s...' too long - " + "ignored", pData->uname[i]); + --i; + ++iUsr; + while(iUsr < len && c[iUsr] != ',') + ++iUsr; /* skip to next name */ + } else if(iDst == 0) { + LogError(0, RS_RET_ERR, "no user name given - " + "ignored"); + --i; + ++iUsr; + while(iUsr < len && c[iUsr] != ',') + ++iUsr; /* skip to next name */ + } + if(iUsr < len) { + ++iUsr; /* skip "," */ + while(iUsr < len && isspace(c[iUsr])) + ++iUsr; /* skip whitespace */ + } + } + if(i == MAXUNAMES && iUsr != len) { + LogError(0, RS_RET_ERR, "omusrmsg supports only up to %d " + "user names in a single action - all others have been ignored", + MAXUNAMES); + } +} + + +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->bIsWall = 0; + pData->tplName = NULL; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTnewActInst(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "users")) { + if(!es_strbufcmp(pvals[i].val.d.estr, (uchar*)"*", 1)) { + pData->bIsWall = 1; + } else { + populateUsers(pData, pvals[i].val.d.estr); + } + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("omusrmsg: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup(pData->bIsWall ? " WallFmt" : " StdUsrMsgFmt"), + OMSR_NO_RQD_TPL_OPTS)); + } else { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup((char*) pData->tplName), + OMSR_NO_RQD_TPL_OPTS)); + } +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + +BEGINparseSelectorAct + es_str_t *usrs; + int bHadWarning; +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + bHadWarning = 0; + if(!strncmp((char*) p, ":omusrmsg:", sizeof(":omusrmsg:") - 1)) { + p += sizeof(":omusrmsg:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ + } else { + if(!*p || !((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') + || (*p >= '0' && *p <= '9') || *p == '_' || *p == '.' || *p == '*')) { + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + } else { + LogMsg(0, RS_RET_OUTDATED_STMT, LOG_WARNING, + "action '%s' treated as ':omusrmsg:%s' - please " + "use ':omusrmsg:%s' syntax instead, '%s' will " + "not be supported in the future", + p, p, p, p); + bHadWarning = 1; + } + } + + CHKiRet(createInstance(&pData)); + + if(*p == '*') { /* wall */ + dbgprintf("write-all"); + ++p; /* eat '*' */ + pData->bIsWall = 1; /* write to all users */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " WallFmt")); + } else { + /* everything else is currently treated as a user name */ + usrs = es_newStr(128); + while(*p && *p != ';') { + es_addChar(&usrs, *p); + ++p; + } + populateUsers(pData, usrs); + es_deleteStr(usrs); + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*)" StdUsrMsgFmt")) + != RS_RET_OK) + goto finalize_it; + } + if(iRet == RS_RET_OK && bHadWarning) + iRet = RS_RET_OK_WARN; +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_OMOD8_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(UsrMsg) +CODESTARTmodInit +INITLegCnfVars + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr +ENDmodInit + +/* vim:set ai: + */ diff --git a/tools/omusrmsg.h b/tools/omusrmsg.h new file mode 100644 index 0000000..0e50c03 --- /dev/null +++ b/tools/omusrmsg.h @@ -0,0 +1,33 @@ +/* omusrmsg.c + * These are the definitions for the build-in user message output module. + * + * File begun on 2007-07-13 by RGerhards + * + * Copyright 20072-2012 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. + */ +#ifndef OMUSRMSG_H_INCLUDED +#define OMUSRMSG_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitUsrMsg(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef OMUSRMSG_H_INCLUDED */ +/* vi:set ai: + */ diff --git a/tools/pmrfc3164.c b/tools/pmrfc3164.c new file mode 100644 index 0000000..a1e136b --- /dev/null +++ b/tools/pmrfc3164.c @@ -0,0 +1,419 @@ +/* pmrfc3164.c + * This is a parser module for RFC3164(legacy syslog)-formatted messages. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2009-11-04 by RGerhards + * + * Copyright 2007-2017 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "parser.h" +#include "datetime.h" +#include "unicode-helper.h" +#include "rsconf.h" +MODULE_TYPE_PARSER +MODULE_TYPE_NOKEEP +PARSER_NAME("rsyslog.rfc3164") +MODULE_CNFNAME("pmrfc3164") + +/* internal structures + */ +DEF_PMOD_STATIC_DATA +DEFobjCurrIf(glbl) +DEFobjCurrIf(parser) +DEFobjCurrIf(datetime) + + +/* static data */ +static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */ + + +/* parser instance parameters */ +static struct cnfparamdescr parserpdescr[] = { + { "detect.yearaftertimestamp", eCmdHdlrBinary, 0 }, + { "permit.squarebracketsinhostname", eCmdHdlrBinary, 0 }, + { "permit.slashesinhostname", eCmdHdlrBinary, 0 }, + { "permit.atsignsinhostname", eCmdHdlrBinary, 0 }, + { "force.tagendingbycolon", eCmdHdlrBinary, 0}, + { "remove.msgfirstspace", eCmdHdlrBinary, 0}, +}; +static struct cnfparamblk parserpblk = + { CNFPARAMBLK_VERSION, + sizeof(parserpdescr)/sizeof(struct cnfparamdescr), + parserpdescr + }; + +struct instanceConf_s { + int bDetectYearAfterTimestamp; + int bPermitSquareBracketsInHostname; + int bPermitSlashesInHostname; + int bPermitAtSignsInHostname; + int bForceTagEndingByColon; + int bRemoveMsgFirstSpace; +}; + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATUREAutomaticSanitazion) + iRet = RS_RET_OK; + if(eFeat == sFEATUREAutomaticPRIParsing) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +/* create input instance, set default parameters, and + * add it to the list of instances. + */ +static rsRetVal +createInstance(instanceConf_t **pinst) +{ + instanceConf_t *inst; + DEFiRet; + CHKmalloc(inst = malloc(sizeof(instanceConf_t))); + inst->bDetectYearAfterTimestamp = 0; + inst->bPermitSquareBracketsInHostname = 0; + inst->bPermitSlashesInHostname = 0; + inst->bPermitAtSignsInHostname = 0; + inst->bForceTagEndingByColon = 0; + inst->bRemoveMsgFirstSpace = 0; + bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(loadConf); + *pinst = inst; +finalize_it: + RETiRet; +} + +BEGINnewParserInst + struct cnfparamvals *pvals = NULL; + int i; +CODESTARTnewParserInst + DBGPRINTF("newParserInst (pmrfc3164)\n"); + + inst = NULL; + CHKiRet(createInstance(&inst)); + + if(lst == NULL) + FINALIZE; /* just set defaults, no param block! */ + + if((pvals = nvlstGetParams(lst, &parserpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("parser param blk in pmrfc3164:\n"); + cnfparamsPrint(&parserpblk, pvals); + } + + for(i = 0 ; i < parserpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(parserpblk.descr[i].name, "detect.yearaftertimestamp")) { + inst->bDetectYearAfterTimestamp = (int) pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "permit.squarebracketsinhostname")) { + inst->bPermitSquareBracketsInHostname = (int) pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "permit.slashesinhostname")) { + inst->bPermitSlashesInHostname = (int) pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "permit.atsignsinhostname")) { + inst->bPermitAtSignsInHostname = (int) pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "force.tagendingbycolon")) { + inst->bForceTagEndingByColon = (int) pvals[i].val.d.n; + } else if(!strcmp(parserpblk.descr[i].name, "remove.msgfirstspace")) { + inst->bRemoveMsgFirstSpace = (int) pvals[i].val.d.n; + } else { + dbgprintf("pmrfc3164: program error, non-handled " + "param '%s'\n", parserpblk.descr[i].name); + } + } +finalize_it: +CODE_STD_FINALIZERnewParserInst + if(lst != NULL) + cnfparamvalsDestruct(pvals, &parserpblk); + if(iRet != RS_RET_OK) + free(inst); +ENDnewParserInst + + +BEGINfreeParserInst +CODESTARTfreeParserInst + dbgprintf("pmrfc3164: free parser instance %p\n", pInst); +ENDfreeParserInst + + +/* parse a legay-formatted syslog message. + */ +BEGINparse2 + uchar *p2parse; + int lenMsg; + int i; /* general index for parsing */ + uchar bufParseTAG[CONF_TAG_MAXSIZE]; + uchar bufParseHOSTNAME[CONF_HOSTNAME_MAXSIZE]; +CODESTARTparse + assert(pMsg != NULL); + assert(pMsg->pszRawMsg != NULL); + lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; + DBGPRINTF("Message will now be parsed by the legacy syslog parser (offAfterPRI=%d, lenMsg=%d.\n", + pMsg->offAfterPRI, lenMsg); + /* note: offAfterPRI is already the number of PRI chars (do not add one!) */ + p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */ + setProtocolVersion(pMsg, MSG_LEGACY_PROTOCOL); + if(pMsg->iFacility == (LOG_INVLD>>3)) { + DBGPRINTF("facility LOG_INVLD, do not parse\n"); + FINALIZE; + } + + /* now check if we have a completely headerless message. This is indicated + * by spaces or tabs followed '{' or '['. + */ + i = 0; + while(i < lenMsg && (p2parse[i] == ' ' || p2parse[i] == '\t')) { + ++i; + } + if(i < lenMsg && (p2parse[i] == '{' || p2parse[i] == '[')) { + DBGPRINTF("msg seems to be headerless, treating it as such\n"); + FINALIZE; + } + + + /* Check to see if msg contains a timestamp. We start by assuming + * that the message timestamp is the time of reception (which we + * generated ourselfs and then try to actually find one inside the + * message. There we go from high-to low precison and are done + * when we find a matching one. -- rgerhards, 2008-09-16 + */ + if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) { + /* we are done - parse pointer is moved by ParseTIMESTAMP3339 */; + } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg, + NO_PARSE3164_TZSTRING, pInst->bDetectYearAfterTimestamp) == RS_RET_OK) { + if(pMsg->dfltTZ[0] != '\0') + applyDfltTZ(&pMsg->tTIMESTAMP, pMsg->dfltTZ); + /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */; + } else if(*p2parse == ' ' && lenMsg > 1) { + /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */ + ++p2parse; /* move over space */ + --lenMsg; + if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg, + NO_PARSE3164_TZSTRING, pInst->bDetectYearAfterTimestamp) == RS_RET_OK) { + /* indeed, we got it! */ + /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */; + } else {/* parse pointer needs to be restored, as we moved it off-by-one + * for this try. + */ + --p2parse; + ++lenMsg; + } + } + + if(pMsg->msgFlags & IGNDATE) { + /* we need to ignore the msg data, so simply copy over reception date */ + memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime)); + } + + /* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we + * do this only when the user has not forbidden this. I now introduce some + * code that allows a user to configure rsyslogd to treat the rest of the + * message as MSG part completely. In this case, the hostname will be the + * machine that we received the message from and the tag will be empty. This + * is meant to be an interim solution, but for now it is in the code. + */ + if(bParseHOSTNAMEandTAG && !(pMsg->msgFlags & INTERNAL_MSG)) { + /* parse HOSTNAME - but only if this is network-received! + * rger, 2005-11-14: we still have a problem with BSD messages. These messages + * do NOT include a host name. In most cases, this leads to the TAG to be treated + * as hostname and the first word of the message as the TAG. Clearly, this is not + * of advantage ;) I think I have now found a way to handle this situation: there + * are certain characters which are frequently used in TAG (e.g. ':'), which are + * *invalid* in host names. So while parsing the hostname, I check for these characters. + * If I find them, I set a simple flag but continue. After parsing, I check the flag. + * If it was set, then we most probably do not have a hostname but a TAG. Thus, I change + * the fields. I think this logic shall work with any type of syslog message. + * rgerhards, 2009-06-23: and I now have extended this logic to every character + * that is not a valid hostname. + * A "hostname" can validly include "[]" at the beginning and end. This sometimes + * happens with IP address (e.g. "[192.168.0.1]"). This must be turned on via + * an option as it may interfere with non-hostnames in some message formats. + * rgerhards, 2015-04-20 + */ + if(lenMsg > 0 && pMsg->msgFlags & PARSE_HOSTNAME) { + i = 0; + int bHadSBracket = 0; + if(pInst->bPermitSquareBracketsInHostname) { + assert(i < lenMsg); + if(p2parse[i] == '[') { + bHadSBracket = 1; + bufParseHOSTNAME[0] = '['; + ++i; + } + } + while(i < lenMsg + && (isalnum(p2parse[i]) || p2parse[i] == '.' + || p2parse[i] == '_' || p2parse[i] == '-' + || (p2parse[i] == ']' && bHadSBracket) + || (p2parse[i] == '@' && pInst->bPermitAtSignsInHostname) + || (p2parse[i] == '/' && pInst->bPermitSlashesInHostname) ) + && i < (CONF_HOSTNAME_MAXSIZE - 1)) { + bufParseHOSTNAME[i] = p2parse[i]; + ++i; + if(p2parse[i] == ']') + break; /* must be closing bracket */ + } + + if(i == lenMsg) { + /* we have a message that is empty immediately after the hostname, + * but the hostname thus is valid! -- rgerhards, 2010-02-22 + */ + p2parse += i; + lenMsg -= i; + bufParseHOSTNAME[i] = '\0'; + MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i); + } else { + int isHostName = 0; + if(i > 0) { + if(bHadSBracket) { + if(p2parse[i] == ']') { + bufParseHOSTNAME[i] = ']'; + ++i; + isHostName = 1; + } + } else { + if(isalnum(p2parse[i-1])) { + isHostName = 1; + } + } + if(p2parse[i] != ' ') + isHostName = 0; + } + + if(isHostName) { + /* we got a hostname! */ + p2parse += i + 1; /* "eat" it (including SP delimiter) */ + lenMsg -= i + 1; + bufParseHOSTNAME[i] = '\0'; + MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i); + } + } + } + + /* now parse TAG - that should be present in message from all sources. + * This code is somewhat not compliant with RFC 3164. As of 3164, + * the TAG field is ended by any non-alphanumeric character. In + * practice, however, the TAG often contains dashes and other things, + * which would end the TAG. So it is not desirable. As such, we only + * accept colon and SP to be terminators. Even there is a slight difference: + * a colon is PART of the TAG, while a SP is NOT part of the tag + * (it is CONTENT). Starting 2008-04-04, we have removed the 32 character + * size limit (from RFC3164) on the tag. This had bad effects on existing + * envrionments, as sysklogd didn't obey it either (probably another bug + * in RFC3164...). We now receive the full size, but will modify the + * outputs so that only 32 characters max are used by default. + */ + i = 0; + while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE - 2) { + bufParseTAG[i++] = *p2parse++; + --lenMsg; + } + if(lenMsg > 0 && *p2parse == ':') { + ++p2parse; + --lenMsg; + bufParseTAG[i++] = ':'; + } + else if (pInst->bForceTagEndingByColon) { + /* Tag need to be ended by a colon or it's not a tag but the + * begin of the message + */ + p2parse -= ( i + 1 ); + lenMsg += ( i + 1 ); + i = 0; + /* Default TAG is dash (without ':') + */ + bufParseTAG[i++] = '-'; + } + + /* no TAG can only be detected if the message immediatly ends, in which case an empty TAG + * is considered OK. So we do not need to check for empty TAG. -- rgerhards, 2009-06-23 + */ + bufParseTAG[i] = '\0'; /* terminate string */ + MsgSetTAG(pMsg, bufParseTAG, i); + } else {/* we enter this code area when the user has instructed rsyslog NOT + * to parse HOSTNAME and TAG - rgerhards, 2006-03-13 + */ + if(!(pMsg->msgFlags & INTERNAL_MSG)) { + DBGPRINTF("HOSTNAME and TAG not parsed by user configuration.\n"); + } + } + +finalize_it: + if (pInst->bRemoveMsgFirstSpace && *p2parse == ' ') { + /* Bypass first space found in MSG part */ + p2parse++; + lenMsg--; + } + MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg); +ENDparse2 + + +BEGINmodExit +CODESTARTmodExit + /* release what we no longer need */ + objRelease(glbl, CORE_COMPONENT); + objRelease(parser, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_PMOD2_QUERIES +CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(pmrfc3164) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; + /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(parser, CORE_COMPONENT)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + + DBGPRINTF("rfc3164 parser init called\n"); + bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(loadConf); + /* cache value, is set only during rsyslogd option processing */ + + +ENDmodInit + +/* vim:set ai: + */ diff --git a/tools/pmrfc3164.h b/tools/pmrfc3164.h new file mode 100644 index 0000000..9ea3c81 --- /dev/null +++ b/tools/pmrfc3164.h @@ -0,0 +1,33 @@ +/* pmrfc3164.h + * These are the definitions for the RFC3164 parser module. + * + * File begun on 2009-11-04 by RGerhards + * + * Copyright 2009 Rainer Gerhards and 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. + */ +#ifndef PMRFC3164_H_INCLUDED +#define PMRFC3164_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitpmrfc3164(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef PMRFC3164_H_INCLUDED */ +/* vi:set ai: + */ diff --git a/tools/pmrfc5424.c b/tools/pmrfc5424.c new file mode 100644 index 0000000..f682b3e --- /dev/null +++ b/tools/pmrfc5424.c @@ -0,0 +1,329 @@ +/* pmrfc5424.c + * This is a parser module for RFC5424-formatted messages. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2009-11-03 by RGerhards + * + * Copyright 2007-2015 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "parser.h" +#include "datetime.h" +#include "unicode-helper.h" + +MODULE_TYPE_PARSER +MODULE_TYPE_NOKEEP +PARSER_NAME("rsyslog.rfc5424") + +/* internal structures + */ +DEF_PMOD_STATIC_DATA +DEFobjCurrIf(glbl) +DEFobjCurrIf(parser) +DEFobjCurrIf(datetime) + + +/* config data */ + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATUREAutomaticSanitazion) + iRet = RS_RET_OK; + if(eFeat == sFEATUREAutomaticPRIParsing) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +/* Helper to parseRFCSyslogMsg. This function parses a field up to + * (and including) the SP character after it. The field contents is + * returned in a caller-provided buffer. The parsepointer is advanced + * to after the terminating SP. The caller must ensure that the + * provided buffer is large enough to hold the to be extracted value. + * Returns 0 if everything is fine or 1 if either the field is not + * SP-terminated or any other error occurs. -- rger, 2005-11-24 + * The function now receives the size of the string and makes sure + * that it does not process more than that. The *pLenStr counter is + * updated on exit. -- rgerhards, 2009-09-23 + */ +static int parseRFCField(uchar **pp2parse, uchar *pResult, int *pLenStr) +{ + uchar *p2parse; + int iRet = 0; + + assert(pp2parse != NULL); + assert(*pp2parse != NULL); + assert(pResult != NULL); + + p2parse = *pp2parse; + + /* this is the actual parsing loop */ + while(*pLenStr > 0 && *p2parse != ' ') { + *pResult++ = *p2parse++; + --(*pLenStr); + } + + if(*pLenStr > 0 && *p2parse == ' ') { + ++p2parse; /* eat SP, but only if not at end of string */ + --(*pLenStr); + } else { + iRet = 1; /* there MUST be an SP! */ + } + *pResult = '\0'; + + /* set the new parse pointer */ + *pp2parse = p2parse; + return iRet; +} + + +/* Helper to parseRFCSyslogMsg. This function parses the structured + * data field of a message. It does NOT parse inside structured data, + * just gets the field as whole. Parsing the single entities is left + * to other functions. The parsepointer is advanced + * to after the terminating SP. The caller must ensure that the + * provided buffer is large enough to hold the to be extracted value. + * Returns 0 if everything is fine or 1 if either the field is not + * SP-terminated or any other error occurs. -- rger, 2005-11-24 + * The function now receives the size of the string and makes sure + * that it does not process more than that. The *pLenStr counter is + * updated on exit. -- rgerhards, 2009-09-23 + */ +static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult, int *pLenStr) +{ + uchar *p2parse; + int bCont = 1; + int iRet = 0; + int lenStr; + + assert(pp2parse != NULL); + assert(*pp2parse != NULL); + assert(pResult != NULL); + + p2parse = *pp2parse; + lenStr = *pLenStr; + + /* this is the actual parsing loop + * Remeber: structured data starts with [ and includes any characters + * until the first ] followed by a SP. There may be spaces inside + * structured data. There may also be \] inside the structured data, which + * do NOT terminate an element. + */ + if(lenStr == 0 || (*p2parse != '[' && *p2parse != '-')) + return 1; /* this is NOT structured data! */ + + if(*p2parse == '-') { /* empty structured data? */ + *pResult++ = '-'; + ++p2parse; + --lenStr; + } else { + while(bCont) { + if(lenStr < 2) { + /* we now need to check if we have only structured data */ + if(lenStr > 0 && *p2parse == ']') { + *pResult++ = *p2parse; + p2parse++; + lenStr--; + bCont = 0; + } else { + iRet = 1; /* this is not valid! */ + bCont = 0; + } + } else if(*p2parse == '\\' && *(p2parse+1) == ']') { + /* this is escaped, need to copy both */ + *pResult++ = *p2parse++; + *pResult++ = *p2parse++; + lenStr -= 2; + } else if(*p2parse == ']' && *(p2parse+1) == ' ') { + /* found end, just need to copy the ] and eat the SP */ + *pResult++ = *p2parse; + p2parse += 1; + lenStr -= 1; + bCont = 0; + } else { + *pResult++ = *p2parse++; + --lenStr; + } + } + } + + if(lenStr > 0 && *p2parse == ' ') { + ++p2parse; /* eat SP, but only if not at end of string */ + --lenStr; + } else { + iRet = 1; /* there MUST be an SP! */ + } + *pResult = '\0'; + + /* set the new parse pointer */ + *pp2parse = p2parse; + *pLenStr = lenStr; + return iRet; +} + +/* parse a RFC5424-formatted syslog message. This function returns + * 0 if processing of the message shall continue and 1 if something + * went wrong and this messe should be ignored. This function has been + * implemented in the effort to support syslog-protocol. Please note that + * the name (parse *RFC*) stems from the hope that syslog-protocol will + * some time become an RFC. Do not confuse this with informational + * RFC 3164 (which is legacy syslog). + * + * currently supported format: + * + * <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP [SD-ID]s SP MSG + * + * <PRI> is already stripped when this function is entered. VERSION already + * has been confirmed to be "1", but has NOT been stripped from the message. + * + * rger, 2005-11-24 + */ +BEGINparse + uchar *p2parse; + uchar *pBuf = NULL; + int lenMsg; + int bContParse = 1; +CODESTARTparse + assert(pMsg != NULL); + assert(pMsg->pszRawMsg != NULL); + p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */ + lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; + + /* check if we are the right parser */ + if(lenMsg < 2 || p2parse[0] != '1' || p2parse[1] != ' ') { + ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE); + } + DBGPRINTF("Message has RFC5424/syslog-protocol format.\n"); + setProtocolVersion(pMsg, MSG_RFC5424_PROTOCOL); + p2parse += 2; + lenMsg -= 2; + + /* Now get us some memory we can use as a work buffer while parsing. + * We simply allocated a buffer sufficiently large to hold all of the + * message, so we can not run into any troubles. I think this is + * wiser than to use individual buffers. + */ + CHKmalloc(pBuf = malloc(lenMsg + 1)); + + /* IMPORTANT NOTE: + * Validation is not actually done below nor are any errors handled. I have + * NOT included this for the current proof of concept. However, it is strongly + * advisable to add it when this code actually goes into production. + * rgerhards, 2005-11-24 + */ + + /* TIMESTAMP */ + if(lenMsg >= 2 && p2parse[0] == '-' && p2parse[1] == ' ') { + memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime)); + p2parse += 2; + lenMsg -= 2; + } else if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) { + if(pMsg->msgFlags & IGNDATE) { + /* we need to ignore the msg data, so simply copy over reception date */ + memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime)); + } + } else { + DBGPRINTF("no TIMESTAMP detected!\n"); + bContParse = 0; + } + + /* HOSTNAME */ + if(bContParse) { + parseRFCField(&p2parse, pBuf, &lenMsg); + MsgSetHOSTNAME(pMsg, pBuf, ustrlen(pBuf)); + } + + /* APP-NAME */ + if(bContParse) { + parseRFCField(&p2parse, pBuf, &lenMsg); + MsgSetAPPNAME(pMsg, (char*)pBuf); + } + + /* PROCID */ + if(bContParse) { + parseRFCField(&p2parse, pBuf, &lenMsg); + MsgSetPROCID(pMsg, (char*)pBuf); + } + + /* MSGID */ + if(bContParse) { + parseRFCField(&p2parse, pBuf, &lenMsg); + MsgSetMSGID(pMsg, (char*)pBuf); + } + + /* STRUCTURED-DATA */ + if(bContParse) { + parseRFCStructuredData(&p2parse, pBuf, &lenMsg); + MsgSetStructuredData(pMsg, (char*)pBuf); + } + + /* MSG */ + MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg); + +finalize_it: + if(pBuf != NULL) + free(pBuf); +ENDparse + + +BEGINmodExit +CODESTARTmodExit + /* release what we no longer need */ + objRelease(glbl, CORE_COMPONENT); + objRelease(parser, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_PMOD_QUERIES +CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(pmrfc5424) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(parser, CORE_COMPONENT)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + + dbgprintf("rfc5424 parser init called\n"); + dbgprintf("GetParserName addr %p\n", GetParserName); +ENDmodInit + +/* vim:set ai: + */ diff --git a/tools/pmrfc5424.h b/tools/pmrfc5424.h new file mode 100644 index 0000000..17f9306 --- /dev/null +++ b/tools/pmrfc5424.h @@ -0,0 +1,33 @@ +/* pmrfc5424.h + * These are the definitions for the RFCC5424 parser module. + * + * File begun on 2009-11-03 by RGerhards + * + * Copyright 2009-2014 Rainer Gerhards and 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. + */ +#ifndef PMRFC54254_H_INCLUDED +#define PMRFC54254_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitpmrfc5424(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef PMRFC54254_H_INCLUDED */ +/* vi:set ai: + */ diff --git a/tools/recover_qi.pl b/tools/recover_qi.pl new file mode 100755 index 0000000..eb8de55 --- /dev/null +++ b/tools/recover_qi.pl @@ -0,0 +1,207 @@ +#!/usr/bin/perl -w +# recover rsyslog disk queue index (.qi) from queue files (.nnnnnnnn). +# +# See: +# runtime/queue.c: qqueuePersist() +# runtime/queue.c: qqueueTryLoadPersistedInfo() +# +# kaiwang.chen@gmail.com 2012-03-14 +# +use strict; +use Getopt::Long; + +my %opt = (); +GetOptions(\%opt,"spool|w=s","basename|f=s","digits|d=i","help!"); +if ($opt{help}) { + print "Usage: +\t$0 -w WorkDirectory -f QueueFileName -d 8 > QueueFileName.qi +"; + exit; +} + +# runtime/queue.c: qConstructDisk() +my $iMaxFiles = 10000000; # 0+"1".( "0"x($opt{digits} - 1)); + +# get the list of queue files, spool directory excluded +my $re = qr/^\Q$opt{basename}\E\.\d{$opt{digits}}$/; +opendir(DIR, $opt{spool}) or die "can’t open spool: $!"; +my @qf = grep { /$re/ && -f "$opt{spool}/$_" } readdir(DIR); +closedir DIR; + +# ensure order and continuity +@qf = sort @qf; +my ($head) = ($qf[0] =~ /(\d+)$/); +my ($tail) = ($qf[-1] =~ /(\d+)$/); +$head += 0; +$tail += 0; +if ($tail-$head+1 != @qf || $tail > $iMaxFiles) { + die "broken queue: missing file(s) or wrong tail\n"; +} + +# collect some counters about the queue, assuming all are unprocessed entries. +my $sizeOnDisk = 0; +my $iQueueSize = 0; +chdir($opt{spool}) or die "can't chdir to spool: $!"; +print STDERR "traversing ". @qf ." files, please wait...\n"; +for (@qf) { + open FH, "<", $_ or die "can't read queue file $_\n"; + $sizeOnDisk += (stat FH)[7]; + while (<FH>) { + $iQueueSize++ if /^<Obj/; # runtime/msg.c: MsgSerialize() + } + close FH; +} +# happen to reuse last stat +my $iCurrOffs_Write = (stat(_))[7]; + +# runtime/queue.c: qqueuePersist() +my $qqueue = Rsyslog::OPB->new("qqueue",1); +$qqueue->property("iQueueSize", "INT", $iQueueSize); +$qqueue->property("tVars.disk.sizeOnDisk", "INT64", $sizeOnDisk); +$qqueue->property("tVars.disk.bytesRead", "INT64", 0); + +# runtime/stream.h: strmType_t +my $STREAMTYPE_FILE_CIRCULAR = 1; +# runtime/stream.h: strmMode_t +my $STREAMMODE_READ = 1; +my $STREAMMODE_WRITE_APPEND = 4; + +# runtime/stream.c: strmSerialize() +# write to end +my $strm_Write = Rsyslog::Obj->new("strm",1); +$strm_Write->property( "iCurrFNum", "INT", $tail); +$strm_Write->property( "pszFName", "PSZ", $opt{basename}); +$strm_Write->property( "iMaxFiles", "INT", $iMaxFiles); +$strm_Write->property( "bDeleteOnClose", "INT", 0); +$strm_Write->property( "sType", "INT", $STREAMTYPE_FILE_CIRCULAR); +$strm_Write->property("tOperationsMode", "INT", $STREAMMODE_WRITE_APPEND); +$strm_Write->property( "tOpenMode", "INT", 0600); +$strm_Write->property( "iCurrOffs","INT64", $iCurrOffs_Write); +# read from head +my $strm_ReadDel = Rsyslog::Obj->new("strm",1); +$strm_ReadDel->property( "iCurrFNum", "INT", $head); +$strm_ReadDel->property( "pszFName", "PSZ", $opt{basename}); +$strm_ReadDel->property( "iMaxFiles", "INT", $iMaxFiles); +$strm_ReadDel->property( "bDeleteOnClose", "INT", 1); +$strm_ReadDel->property( "sType", "INT", $STREAMTYPE_FILE_CIRCULAR); +$strm_ReadDel->property("tOperationsMode", "INT", $STREAMMODE_READ); +$strm_ReadDel->property( "tOpenMode", "INT", 0600); +$strm_ReadDel->property( "iCurrOffs","INT64", 0); + +# .qi +print $qqueue->serialize(); +print $strm_Write->serialize(); +print $strm_ReadDel->serialize(); + +exit; +#----------------------------------------------------------------------------- + +package Rsyslog::Serializable; +# runtime/obj.c +sub COOKIE_OBJLINE { '<' } +sub COOKIE_PROPLINE { '+' } +sub COOKIE_ENDLINE { '>' } +sub COOKIE_BLANKLINE { '.' } +# VARTYPE(short_ptype) +sub VARTYPE { + my ($t) = @_; + # runtime/obj-types.h: propType_t + my $ptype = "PROPTYPE_".$t; + # runtime/var.h: varType_t + my %vm = ( + VARTYPE_NONE => 0, + VARTYPE_STR => 1, + VARTYPE_NUMBER => 2, + VARTYPE_SYSLOGTIME => 3, + ); + # runtime/obj.c: SerializeProp() + my %p2v = ( + #PROPTYPE_NONE => "", + PROPTYPE_PSZ => "VARTYPE_STR", + PROPTYPE_SHORT => "VARTYPE_NUMBER", + PROPTYPE_INT => "VARTYPE_NUMBER", + PROPTYPE_LONG => "VARTYPE_NUMBER", + PROPTYPE_INT64 => "VARTYPE_NUMBER", + PROPTYPE_CSTR => "VARTYPE_STR", + #PROPTYPE_SYSLOGTIME => "VARTYPE_SYSLOGTIME", + ); + my $vtype = $p2v{$ptype}; + unless ($vtype) { + die "property type $t is not supported!\n"; + } + return $vm{$vtype}; +} +sub serialize { + my $self = shift; + # runtime/obj.c: objSerializeHeader() + my $x = COOKIE_OBJLINE(); + $x .= join(":", $self->type(), $self->cver(), $self->id(), $self->version()); + $x .= ":\n"; + for ( values %{$self->{props}} ) { + # runtime/obj.c: SerializeProp() + $x .= COOKIE_PROPLINE(); + $x .= join(":", + $_->{name}, + VARTYPE($_->{type}), + length($_->{value}), + $_->{value}); + $x .= ":\n"; + } + # runtime/obj.c: EndSerialize() + $x .= COOKIE_ENDLINE() . "End\n"; + $x .= COOKIE_BLANKLINE() . "\n"; +} +# constructor: new(id,version) +sub new { + my ($class, $id, $version) = @_; + $class = ref $class if ref $class; + bless { + id => $id, + version => $version, + props => {}, + }, $class; +} +sub id { + my $self = shift; + if (@_) { + my $x = $self->{id}; + $self->{id} = shift; + return $x; + } + return $self->{id}; +} +sub version { + my $self = shift; + if (@_) { + my $x = $self->{version}; + $self->{version} = shift; + return $x; + } + return $self->{version}; +} +# property(name, type, value) +sub property { + my $self = shift; + my $name = shift; + if (@_) { + my $x = $self->{props}{$name}; + $self->{props}{$name}{name} = $name; + $self->{props}{$name}{type} = shift; + $self->{props}{$name}{value} = shift; + return $x; + } + return $self->{props}{$name}; +} +1; +package Rsyslog::OPB; +use base qw(Rsyslog::Serializable); +sub type { 'OPB' } +sub cver { 1 } +sub new { shift->SUPER::new(@_) } +1; +package Rsyslog::Obj; +use base qw(Rsyslog::Serializable); +sub type { 'Obj' } +sub cver { 1 } +sub new { shift->SUPER::new(@_) } +1; diff --git a/tools/rscryutil.c b/tools/rscryutil.c new file mode 100644 index 0000000..6ff0724 --- /dev/null +++ b/tools/rscryutil.c @@ -0,0 +1,552 @@ +/* This is a tool for processing rsyslog encrypted log files. + * + * Copyright 2013-2019 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 exprs or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <getopt.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <gcrypt.h> + +#include "rsyslog.h" +#include "libgcry.h" + + +static enum { MD_DECRYPT, MD_WRITE_KEYFILE +} mode = MD_DECRYPT; +static int verbose = 0; +static gcry_cipher_hd_t gcry_chd; +static size_t blkLength; + +static char *keyfile = NULL; +static char *keyprog = NULL; +static int randomKeyLen = -1; +static char *cry_key = NULL; +static unsigned cry_keylen = 0; +static int cry_algo = GCRY_CIPHER_AES128; +static int cry_mode = GCRY_CIPHER_MODE_CBC; +static int optionForce = 0; + +/* We use some common code which expects rsyslog runtime to be + * present, most importantly for debug output. As a stand-alone + * tool, we do not really have this. So we do some dummy defines + * in order to satisfy the needs of the common code. + */ +int Debug = 0; +#ifndef DEBUGLESS +void r_dbgprintf(const char *srcname __attribute__((unused)), const char *fmt __attribute__((unused)), ...) {}; +#endif +void srSleep(int a __attribute__((unused)), int b __attribute__((unused))); +/* prototype (avoid compiler warning) */ +void srSleep(int a __attribute__((unused)), int b __attribute__((unused))) {} +/* this is not really needed by any of our code */ +long randomNumber(void); +/* prototype (avoid compiler warning) */ +long randomNumber(void) {return 0l;} +/* this is not really needed by any of our code */ + +/* rectype/value must be EIF_MAX_*_LEN+1 long! + * returns 0 on success or something else on error/EOF + */ +static int +eiGetRecord(FILE *eifp, char *rectype, char *value) +{ + int r; + unsigned short i, j; + char buf[EIF_MAX_RECTYPE_LEN+EIF_MAX_VALUE_LEN+128]; + /* large enough for any valid record */ + + if(fgets(buf, sizeof(buf), eifp) == NULL) { + r = 1; goto done; + } + + for(i = 0 ; i < EIF_MAX_RECTYPE_LEN && buf[i] != ':' ; ++i) + if(buf[i] == '\0') { + r = 2; goto done; + } else + rectype[i] = buf[i]; + rectype[i] = '\0'; + j = 0; + for(++i ; i < EIF_MAX_VALUE_LEN && buf[i] != '\n' ; ++i, ++j) + if(buf[i] == '\0') { + r = 3; goto done; + } else + value[j] = buf[i]; + value[j] = '\0'; + r = 0; +done: return r; +} + +static int +eiCheckFiletype(FILE *eifp) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + int r; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "FILETYPE") || strcmp(value, RSGCRY_FILETYPE_NAME)) { + fprintf(stderr, "invalid filetype \"cookie\" in encryption " + "info file\n"); + fprintf(stderr, "\trectype: '%s', value: '%s'\n", rectype, value); + r = 1; goto done; + } + r = 0; +done: return r; +} + +static int +eiGetIV(FILE *eifp, char *iv, size_t leniv) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + size_t valueLen; + unsigned short i, j; + int r; + unsigned char nibble; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "IV")) { + fprintf(stderr, "no IV record found when expected, record type " + "seen is '%s'\n", rectype); + r = 1; goto done; + } + valueLen = strlen(value); + if(valueLen/2 != leniv) { + fprintf(stderr, "length of IV is %lld, expected %lld\n", + (long long) valueLen/2, (long long) leniv); + r = 1; goto done; + } + + for(i = j = 0 ; i < valueLen ; ++i) { + if(value[i] >= '0' && value[i] <= '9') + nibble = value[i] - '0'; + else if(value[i] >= 'a' && value[i] <= 'f') + nibble = value[i] - 'a' + 10; + else { + fprintf(stderr, "invalid IV '%s'\n", value); + r = 1; goto done; + } + if(i % 2 == 0) + iv[j] = nibble << 4; + else + iv[j++] |= nibble; + } + r = 0; +done: return r; +} + +static int +eiGetEND(FILE *eifp, off64_t *offs) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1] = ""; + char value[EIF_MAX_VALUE_LEN+1]; + int r; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "END")) { + fprintf(stderr, "no END record found when expected, record type " + "seen is '%s'\n", rectype); + r = 1; goto done; + } + *offs = atoll(value); + r = 0; +done: return r; +} + +static int +initCrypt(FILE *eifp) +{ + int r = 0; + gcry_error_t gcryError; + char iv[4096]; + + blkLength = gcry_cipher_get_algo_blklen(cry_algo); + if(blkLength > sizeof(iv)) { + fprintf(stderr, "internal error[%s:%d]: block length %lld too large for " + "iv buffer\n", __FILE__, __LINE__, (long long) blkLength); + r = 1; goto done; + } + if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done; + + size_t keyLength = gcry_cipher_get_algo_keylen(cry_algo); + assert(cry_key != NULL); /* "fix" clang 10 static analyzer false positive */ + if(strlen(cry_key) != keyLength) { + fprintf(stderr, "invalid key length; key is %u characters, but " + "exactly %llu characters are required\n", cry_keylen, + (long long unsigned) keyLength); + r = 1; goto done; + } + + gcryError = gcry_cipher_open(&gcry_chd, cry_algo, cry_mode, 0); + if (gcryError) { + printf("gcry_cipher_open failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } + + gcryError = gcry_cipher_setkey(gcry_chd, cry_key, keyLength); + if (gcryError) { + printf("gcry_cipher_setkey failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } + + gcryError = gcry_cipher_setiv(gcry_chd, iv, blkLength); + if (gcryError) { + printf("gcry_cipher_setiv failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } +done: return r; +} + +static void +removePadding(char *buf, size_t *plen) +{ + unsigned len = (unsigned) *plen; + unsigned iSrc, iDst; + char *frstNUL; + + frstNUL = memchr(buf, 0x00, *plen); + if(frstNUL == NULL) + goto done; + iDst = iSrc = frstNUL - buf; + + while(iSrc < len) { + if(buf[iSrc] != 0x00) + buf[iDst++] = buf[iSrc]; + ++iSrc; + } + + *plen = iDst; +done: return; +} + +static void +decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) +{ + gcry_error_t gcryError; + size_t nRead, nWritten; + size_t toRead; + size_t leftTillBlkEnd; + char buf[64*1024]; + + leftTillBlkEnd = blkEnd - *pCurrOffs; + while(1) { + toRead = sizeof(buf) <= leftTillBlkEnd ? sizeof(buf) : leftTillBlkEnd; + toRead = toRead - toRead % blkLength; + nRead = fread(buf, 1, toRead, fpin); + if(nRead == 0) + break; + leftTillBlkEnd -= nRead, *pCurrOffs += nRead; + gcryError = gcry_cipher_decrypt( + gcry_chd, // gcry_cipher_hd_t + buf, // void * + nRead, // size_t + NULL, // const void * + 0); // size_t + if (gcryError) { + fprintf(stderr, "gcry_cipher_decrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + return; + } + removePadding(buf, &nRead); + nWritten = fwrite(buf, 1, nRead, fpout); + if(nWritten != nRead) { + perror("fpout"); + return; + } + } +} + + +static int +doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp) +{ + off64_t blkEnd; + off64_t currOffs = 0; + int r = 1; + int fd; + struct stat buf; + + while(1) { + /* process block */ + if(initCrypt(eifp) != 0) + goto done; + /* set blkEnd to size of logfp and proceed. */ + if((fd = fileno(logfp)) == -1) { + r = -1; + goto done; + } + if((r = fstat(fd, &buf)) != 0) goto done; + blkEnd = buf.st_size; + r = eiGetEND(eifp, &blkEnd); + if(r != 0 && r != 1) goto done; + decryptBlock(logfp, outfp, blkEnd, &currOffs); + gcry_cipher_close(gcry_chd); + } + r = 0; +done: return r; +} + +static void +decrypt(const char *name) +{ + FILE *logfp = NULL, *eifp = NULL; + int r = 0; + char eifname[4096]; + + if(!strcmp(name, "-")) { + fprintf(stderr, "decrypt mode cannot work on stdin\n"); + goto err; + } else { + if((logfp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } + snprintf(eifname, sizeof(eifname), "%s%s", name, ENCINFO_SUFFIX); + eifname[sizeof(eifname)-1] = '\0'; + if((eifp = fopen(eifname, "r")) == NULL) { + perror(eifname); + goto err; + } + if(eiCheckFiletype(eifp) != 0) + goto err; + } + + if((r = doDecrypt(logfp, eifp, stdout)) != 0) + goto err; + + fclose(logfp); logfp = NULL; + fclose(eifp); eifp = NULL; + return; + +err: + fprintf(stderr, "error %d processing file %s\n", r, name); + if(eifp != NULL) + fclose(eifp); + if(logfp != NULL) + fclose(logfp); +} + +static void +write_keyfile(char *fn) +{ + int fd; + int r; + mode_t fmode; + + fmode = O_WRONLY|O_CREAT; + if(!optionForce) + fmode |= O_EXCL; + if(fn == NULL) { + fprintf(stderr, "program error: keyfile is NULL"); + exit(1); + } + if((fd = open(fn, fmode, S_IRUSR)) == -1) { + fprintf(stderr, "error opening keyfile "); + perror(fn); + exit(1); + } + if((r = write(fd, cry_key, cry_keylen)) != (ssize_t)cry_keylen) { + fprintf(stderr, "error writing keyfile (ret=%d) ", r); + perror(fn); + exit(1); + } + close(fd); +} + +static void +getKeyFromFile(const char *fn) +{ + const int r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); + if(r != 0) { + perror(fn); + exit(1); + } +} + +static void +getRandomKey(void) +{ + int fd; + cry_keylen = randomKeyLen; + cry_key = malloc(randomKeyLen); /* do NOT zero-out! */ + /* if we cannot obtain data from /dev/urandom, we use whatever + * is present at the current memory location as random data. Of + * course, this is very weak and we should consider a different + * option, especially when not running under Linux (for Linux, + * unavailability of /dev/urandom is just a theoretic thing, it + * will always work...). -- TODO -- rgerhards, 2013-03-06 + */ + if((fd = open("/dev/urandom", O_RDONLY)) >= 0) { + if(read(fd, cry_key, randomKeyLen) != randomKeyLen) { + fprintf(stderr, "warning: could not read sufficient data " + "from /dev/urandom - key may be weak\n"); + }; + close(fd); + } +} + + +static void +setKey(void) +{ + if(randomKeyLen != -1) + getRandomKey(); + else if(keyfile != NULL) + getKeyFromFile(keyfile); + else if(keyprog != NULL) + gcryGetKeyFromProg(keyprog, &cry_key, &cry_keylen); + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); + exit(1); + } +} + +static struct option long_options[] = +{ + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"decrypt", no_argument, NULL, 'd'}, + {"force", no_argument, NULL, 'f'}, + {"write-keyfile", required_argument, NULL, 'W'}, + {"key", required_argument, NULL, 'K'}, + {"generate-random-key", required_argument, NULL, 'r'}, + {"keyfile", required_argument, NULL, 'k'}, + {"key-program", required_argument, NULL, 'p'}, + {"algo", required_argument, NULL, 'a'}, + {"mode", required_argument, NULL, 'm'}, + {NULL, 0, NULL, 0} +}; + +int +main(int argc, char *argv[]) +{ + int i; + int opt; + int temp; + char *newKeyFile = NULL; + + while(1) { + opt = getopt_long(argc, argv, "a:dfk:K:m:p:r:vVW:", long_options, NULL); + if(opt == -1) + break; + switch(opt) { + case 'd': + mode = MD_DECRYPT; + break; + case 'W': + mode = MD_WRITE_KEYFILE; + newKeyFile = optarg; + break; + case 'k': + keyfile = optarg; + break; + case 'p': + keyprog = optarg; + break; + case 'f': + optionForce = 1; + break; + case 'r': + randomKeyLen = atoi(optarg); + if(randomKeyLen > 64*1024) { + fprintf(stderr, "ERROR: keys larger than 64KiB are " + "not supported\n"); + exit(1); + } + break; + case 'K': + fprintf(stderr, "WARNING: specifying the actual key " + "via the command line is highly insecure\n" + "Do NOT use this for PRODUCTION use.\n"); + cry_key = optarg; + cry_keylen = strlen(cry_key); + break; + case 'a': + temp = rsgcryAlgoname2Algo(optarg); + if(temp == GCRY_CIPHER_NONE) { + fprintf(stderr, "ERROR: algorithm \"%s\" is not " + "kown/supported\n", optarg); + exit(1); + } + cry_algo = temp; + break; + case 'm': + temp = rsgcryModename2Mode(optarg); + if(temp == GCRY_CIPHER_MODE_NONE) { + fprintf(stderr, "ERROR: cipher mode \"%s\" is not " + "kown/supported\n", optarg); + exit(1); + } + cry_mode = temp; + break; + case 'v': + verbose = 1; + break; + case 'V': + fprintf(stderr, "rsgtutil " VERSION "\n"); + exit(0); + break; + case '?': + break; + default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt); + return 1; + } + } + + setKey(); + assert(cry_key != NULL); + + if(mode == MD_WRITE_KEYFILE) { + if(optind != argc) { + fprintf(stderr, "ERROR: no file parameters permitted in " + "--write-keyfile mode\n"); + exit(1); + } + write_keyfile(newKeyFile); + } else { + if(optind == argc) + decrypt("-"); + else { + for(i = optind ; i < argc ; ++i) + decrypt(argv[i]); + } + } + + assert(cry_key != NULL); + memset(cry_key, 0, cry_keylen); /* zero-out key store */ + cry_keylen = 0; + return 0; +} diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst new file mode 100644 index 0000000..dfd447d --- /dev/null +++ b/tools/rscryutil.rst @@ -0,0 +1,199 @@ +========= +rscryutil +========= + +-------------------------- +Manage Encrypted Log Files +-------------------------- + +:Author: Rainer Gerhards <rgerhards@adiscon.com> +:Date: 2013-04-15 +:Manual section: 1 + +SYNOPSIS +======== + +:: + + rscryutil [OPTIONS] [FILE] ... + + +DESCRIPTION +=========== + +This tool performs various operations on encrypted log files. +Most importantly, it provides the ability to decrypt them. + + +OPTIONS +======= + +-d, --decrypt + Select decryption mode. This is the default mode. + +-W, --write-keyfile <file> + Utility function to write a key to a keyfile. The key can be obtained + via any method. + +-v, --verbose + Select verbose mode. + +-f, --force + Forces operations that otherwise would fail. + +-k, --keyfile <file> + Reads the key from <file>. File _must_ contain the key, only, no headers + or other meta information. Keyfiles can be generated via the + *--write-keyfile* option. + +-p, --key-program <path-to-program> + In this mode, the key is provided by a so-called "key program". This program + is executed and must return the key to (as well as some meta information) + via stdout. The core idea of key programs is that using this interface the + user can implement as complex (and secure) method to obtain keys as + desired, all without the need to make modifications to rsyslog. + +-K, --key <KEY> + TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified + on the command line. This is the actual key, and as such this mode + is highly insecure. However, it can be useful for intial testing + steps. This option may be removed in the future. + +-a, --algo <algo> + Sets the encryption algorightm (cipher) to be used. See below + for supported algorithms. The default is "AES128". + +-m, --mode <mode> + Sets the ciphermode to be used. See below for supported modes. + The default is "CBC". + +-r, --generate-random-key <bytes> + Generates a random key of length <bytes>. This option is + meant to be used together with *--write-keyfile* (and it is hard + to envision any other valid use for it). + +OPERATION MODES +=============== + +The operation mode specifies what exactly the tool does with the provided +files. The default operation mode is "dump", but this may change in the future. +Thus, it is recommended to always set the operations mode explicitely. If +multiple operations mode are set on the command line, results are +unpredictable. + +decrypt +------- + +The provided log files are decrypted. Note that the *.encinfo* side files +must exist and be accessible in order for decryption to to work. + +write-keyfile +------------- + +In this mode no log files are processed; thus it is an error to specify +any on the command line. The specified keyfile is written. The key itself +is obtained via the usual key commands. If *--keyfile* is used, that +file is effectively copied. + +For security reasons, existing key files are _not_ overwritten. To permit +this, specify the *--force* option. When doing so, keep in mind that lost +keys cannot be recovered and data encrypted with them may also be considered +lost. + +Keyfiles are always created with 0400 permission, that is read access for only +the user. An exception is when an existing file is overwritten via the +*--force* option, in which case the former permissions still apply. + +EXIT CODES +========== + +The command returns an exit code of 0 if everything went fine, and some +other code in case of failures. + + +SUPPORTED ALGORITHMS +==================== + +We basically support what libgcrypt supports. This is: + + 3DES + CAST5 + BLOWFISH + AES128 + AES192 + AES256 + TWOFISH + TWOFISH128 + ARCFOUR + DES + SERPENT128 + SERPENT192 + SERPENT256 + RFC2268_40 + SEED + CAMELLIA128 + CAMELLIA192 + CAMELLIA256 + + +SUPPORTED CIPHER MODES +====================== + +We basically support what libgcrypt supports. This is: + + ECB + CFB + CBC + STREAM + OFB + CTR + AESWRAP + +EXAMPLES +======== + +**rscryutil logfile** + +Decrypts "logfile" and sends data to stdout. + + +**rscryutil --generate-random-key 16 --keyfile /some/secured/path/keyfile** + +Generates random key and stores it in the specified keyfile. + +LOG SIGNATURES +============== + +Encrypted log files can be used together with signing. To verify such a file, +it must be decrypted first, and the verification tool **rsgtutil(1)** must be +run on the decrypted file. + +SECURITY CONSIDERATIONS +======================= + +Specifying keys directly on the command line (*--key* option) is very +insecure and should +not be done, except for testing purposes with test keys. Even then it is +recommended to use keyfiles, which are also easy to handle during testing. +Keep in mind that command history is usally be kept by bash and can also +easily be monitored. + +Local keyfiles are also a security risk. At a minimum, they should be +used with very restrictive file permissions. For this reason, +the *rscryutil* tool creates them with read permissions for the user, +only, no matter what umask is set to. + +When selecting cipher algorithms and modes, care needs to be taken. The +defaults should be reasonable safe to use, but this tends to change over +time. Keep up with the most current crypto recommendations. + + +SEE ALSO +======== +**rsgtutil(1)**, **rsyslogd(8)** + +COPYRIGHT +========= + +This page is part of the *rsyslog* project, and is available under +LGPLv2. diff --git a/tools/rsyslog.conf.5 b/tools/rsyslog.conf.5 new file mode 100644 index 0000000..ee0b3d0 --- /dev/null +++ b/tools/rsyslog.conf.5 @@ -0,0 +1,815 @@ +.\" rsyslog.conf - rsyslogd(8) configuration file +.\" Copyright 2003-2008 Rainer Gerhards and Adiscon GmbH. +.\" +.\" This file is part of the rsyslog package, an enhanced system log daemon. +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. +.\" +.TH RSYSLOG.CONF 5 "22 October 2012" "Version 7.2.0" "Linux System Administration" +.SH NAME +rsyslog.conf \- rsyslogd(8) configuration file +.SH DESCRIPTION +The +.I rsyslog.conf +file is the main configuration file for the +.BR rsyslogd (8) +which logs system messages on *nix systems. This file specifies rules +for logging. For special features see the +.BR rsyslogd (8) +manpage. Rsyslog.conf is backward-compatible with sysklogd's syslog.conf file. So if you migrate +from sysklogd you can rename it and it should work. + +.B Note that this version of rsyslog ships with extensive documentation in HTML format. +This is provided in the ./doc subdirectory and probably +in a separate package if you installed rsyslog via a packaging system. +To use rsyslog's advanced features, you +.B need +to look at the HTML documentation, because the man pages only cover +basic aspects of operation. + + +.SH MODULES + +Rsyslog has a modular design. Consequently, there is a growing number +of modules. See the HTML documentation for their full description. + +.TP +.I omsnmp +SNMP trap output module +.TP +.I omgssapi +Output module for GSS-enabled syslog +.TP +.I ommysql +Output module for MySQL +.TP +.I omrelp +Output module for the reliable RELP protocol (prevents message loss). +For details, see below at imrelp and the HTML documentation. +It can be used like this: +.IP +*.* :omrelp:server:port +.IP +*.* :omrelp:192.168.0.1:2514 # actual sample +.TP +.I ompgsql +Output module for PostgreSQL +.TP +.I omlibdbi +Generic database output module (Firebird/Interbase, MS SQL, Sybase, +SQLite, Ingres, Oracle, mSQL) +.TP +.I imfile +Input module for text files +.TP +.I imudp +Input plugin for UDP syslog. Replaces the deprecated -r option. Can be +used like this: +.IP +$ModLoad imudp +.IP +$UDPServerRun 514 +.TP +.I imtcp +Input plugin for plain TCP syslog. Replaces the deprecated -t +option. Can be used like this: +.IP +$ModLoad imtcp +.IP +$InputTCPServerRun 514 +.TP +.TP +.I imrelp +Input plugin for the RELP protocol. RELP can be used instead +of UDP or plain TCP syslog to provide reliable delivery of +syslog messages. Please note that plain TCP syslog does NOT +provide truly reliable delivery, with it messages may be lost +when there is a connection problem or the server shuts down. +RELP prevents message loss in those cases. +It can be used like this: +.IP +$ModLoad imrelp +.IP +$InputRELPServerRun 2514 +.TP +.I imgssapi +Input plugin for plain TCP and GSS-enable syslog +.TP +.I immark +Support for mark messages +.TP +.I imklog +Kernel logging. To include kernel log messages, you need to do +.IP +$ModLoad imklog + +Please note that the klogd daemon is no longer necessary and consequently +no longer provided by the rsyslog package. +.TP +.I imuxsock +Unix sockets, including the system log socket. You need to specify +.IP +$ModLoad imuxsock + +in order to receive log messages from local system processes. This +config directive should only left out if you know exactly what you +are doing. + + +.SH BASIC STRUCTURE + +Lines starting with a hash mark ('#') and empty lines are ignored. +Rsyslog.conf should contain following sections (sorted by recommended order in file): + +.TP +Global directives +Global directives set some global properties of whole rsyslog daemon, for example size of main +message queue ($MainMessageQueueSize), loading external modules ($ModLoad) and so on. +All global directives need to be specified on a line by their own and must start with +a dollar-sign. The complete list of global directives can be found in HTML documentation in doc +directory or online on web pages. + +.TP +Templates +Templates allow you to specify format of the logged message. They are also used for dynamic +file name generation. They have to be defined before they are used in rules. For more info +about templates see TEMPLATES section of this manpage. + +.TP +Output channels +Output channels provide an umbrella for any type of output that the user might want. +They have to be defined before they are used in rules. For more info about output channels +see OUTPUT CHANNELS section of this manpage. + +.TP +Rules (selector + action) +Every rule line consists of two fields, a selector field and an action field. These +two fields are separated by one or more spaces or tabs. The selector field specifies +a pattern of facilities and priorities belonging to the specified action. + +.SH SELECTORS + +The selector field itself again consists of two parts, a facility and a +priority, separated by a period ('.'). Both parts are case insensitive and can +also be specified as decimal numbers, but don't do that, you have been warned. +Both facilities and priorities are described in syslog(3). The names mentioned +below correspond to the similar LOG_-values in /usr/include/syslog.h. + +The facility is one of the following keywords: auth, authpriv, cron, daemon, +kern, lpr, mail, mark, news, security (same as auth), syslog, user, uucp and +local0 through local7. The keyword security should not be used anymore and mark +is only for internal use and therefore should not be used in applications. +Anyway, you may want to specify and redirect these messages here. The facility +specifies the subsystem that produced the message, i.e. all mail programs log +with the mail facility (LOG_MAIL) if they log using syslog. + +The priority is one of the following keywords, in ascending order: debug, info, +notice, warning, warn (same as warning), err, error (same as err), crit, alert, +emerg, panic (same as emerg). The keywords error, warn and panic are deprecated +and should not be used anymore. The priority defines the severity of the message. + +The behavior of the original BSD syslogd is that all messages of the specified +priority and higher are logged according to the given action. Rsyslogd behaves +the same, but has some extensions. + +In addition to the above mentioned names the rsyslogd(8) understands the +following extensions: An asterisk ('*') stands for all facilities or all +priorities, depending on where it is used (before or after the period). The +keyword none stands for no priority of the given facility. + +You can specify multiple facilities with the same priority pattern in one +statement using the comma (',') operator. You may specify as much facilities as +you want. Remember that only the facility part from such a statement is taken, a +priority part would be skipped. + +Multiple selectors may be specified for a single action using the semicolon +(';') separator. Remember that each selector in the selector field is capable +to overwrite the preceding ones. Using this behavior you can exclude some +priorities from the pattern. + +Rsyslogd has a syntax extension to the original BSD source, that makes its use +more intuitively. You may precede every priority with an equals sign ('=') to +specify only this single priority and not any of the above. You may also (both +is valid, too) precede the priority with an exclamation mark ('!') to ignore +all that priorities, either exact this one or this and any higher priority. If +you use both extensions then the exclamation mark must occur before the equals +sign, just use it intuitively. + +However, please note that there are some restrictions over the traditional +BSD syslog behaviour. These restrictions stem back to sysklogd, exist +probably since at least the 1990's and as such have always been in +rsyslog. + +Namely, in BSD syslogd you can craft a selector like this: + +*.debug;local6.err + +The intent is to log all facilities at debug or higher, except for local6, +which should only log at err or higher. + +Unfortunately, local6.err will permit error severity and higher, but will +not exclude lower severity messages from facility local6. + +As an alternative, you can explicitly exclude all severities that you do +not want to match. For the above case, this selector is equivalent to the +BSD syslog selector: + +*.debug;local6.!=info;local6.!=notice;local6.!=warn + +An easier approach is probably to do if ... then based matching in script. + +.SH ACTIONS +The action field of a rule describes what to do with the message. In general, message content +is written to a kind of "logfile". But also other actions might be done, like writing to a +database table or forwarding to another host. + +.SS Regular file +Typically messages are logged to real files. The file has to be specified with full pathname, +beginning with a slash ('/'). + +.B Example: +.RS +*.* /var/log/traditionalfile.log;RSYSLOG_TraditionalFileFormat # log to a file in the traditional format +.RE + +Note: if you would like to use high-precision timestamps in your log files, +just remove the ";RSYSLOG_TraditionalFormat". That will select the default +template, which, if not changed, uses RFC 3339 timestamps. + +.B Example: +.RS +*.* /var/log/file.log # log to a file with RFC3339 timestamps +.RE + +By default, files are not synced after each write. To enable syncing +of log files globally, use either the "$ActionFileEnableSync" +directive or the "sync" parameter to omfile. Enabling this option +degrades performance and it is advised not to enable syncing unless +you know what you are doing. +To selectively disable syncing for certain files, you may prefix the +file path with a minus sign ("-"). + +.SS Named pipes +This version of rsyslogd(8) has support for logging output to named pipes (fifos). A fifo or +named pipe can be used as a destination for log messages by prepending a pipe symbol ('|') +to the name of the file. This is handy for debugging. Note that the fifo must be created with +the mkfifo(1) command before rsyslogd(8) is started. + +.SS Terminal and console +If the file you specified is a tty, special tty-handling is done, same with /dev/console. + +.SS Remote machine +There are three ways to forward message: the traditional UDP transport, which is extremely +lossy but standard, the plain TCP based transport which loses messages only during certain +situations but is widely available and the RELP transport which does not lose messages +but is currently available only as part of rsyslogd 3.15.0 and above. + +To forward messages to another host via UDP, prepend the hostname with the at sign ("@"). +To forward it via plain tcp, prepend two at signs ("@@"). To forward via RELP, prepend the +string ":omrelp:" in front of the hostname. + +.B Example: +.RS +*.* @192.168.0.1 +.RE +.sp +In the example above, messages are forwarded via UDP to the machine 192.168.0.1, the destination +port defaults to 514. Due to the nature of UDP, you will probably lose some messages in transit. +If you expect high traffic volume, you can expect to lose a quite noticeable number of messages +(the higher the traffic, the more likely and severe is message loss). + +Sockets for forwarded messages can be bound to a specific device using the "device" option for +the omfwd module. + +.B Example: +.RS +action(type="omfwd" Target="192.168.0.1" Device="eth0" Port=514 Protocol="udp") +.RE +.sp +In the example above, messages are forwarded via UDP to the machine 192.168.0.1 at port 514 over +the device eth0. TCP can be used by setting Protocol to "tcp" in the above example. + +For Linux with VRF support, the device option is used to specify the VRF to send messages. + +.B If you would like to prevent message loss, use RELP: +.RS +*.* :omrelp:192.168.0.1:2514 +.RE +.sp +Note that a port number was given as there is no standard port for relp. + +Keep in mind that you need to load the correct input and output plugins (see "Modules" above). + +Please note that rsyslogd offers a variety of options in regarding to remote +forwarding. For full details, please see the HTML documentation. + +.SS List of users +Usually critical messages are also directed to ``root'' on that machine. You +can specify a list +of users that shall get the message by simply writing ":omusrmsg:" followed +by the login name. You may specify more than one +user by separating them with commas (','). If they're logged in they +get the message (for example: ":omusrmsg:root,user1,user2"). + +.SS Everyone logged on +Emergency messages often go to all users currently online to notify them that something strange +is happening with the system. To specify this wall(1)-feature use an ":omusrmsg:*". + +.SS Database table +This allows logging of the message to a database table. +By default, a MonitorWare-compatible schema is required for this to work. You can +create that schema with the createDB.SQL file that came with the rsyslog package. You can also +use any other schema of your liking - you just need to define a proper template and assign this +template to the action. + +See the HTML documentation for further details on database logging. + +.SS Discard +If the discard action is carried out, the received message is immediately discarded. Discard +can be highly effective if you want to filter out some annoying messages that otherwise would +fill your log files. To do that, place the discard actions early in your log files. +This often plays well with property-based filters, giving you great freedom in specifying +what you do not want. + +Discard is just the single 'stop' command with no further parameters. +.sp +.B Example: +.RS +*.* stop # discards everything. +.RE + + +.SS Output channel +Binds an output channel definition (see there for details) to this action. Output channel actions +must start with a $-sign, e.g. if you would like to bind your output channel definition "mychannel" +to the action, use "$mychannel". Output channels support template definitions like all all other +actions. + +.SS Shell execute +This executes a program in a subshell. The program is passed the template-generated message as the +only command line parameter. Rsyslog waits until the program terminates and only then continues to run. + +.B Example: +.RS +^program-to-execute;template +.RE + +The program-to-execute can be any valid executable. It receives the template string as a single parameter +(argv[1]). + +.SH FILTER CONDITIONS +Rsyslog offers three different types "filter conditions": +.sp 0 + * "traditional" severity and facility based selectors +.sp 0 + * property-based filters +.sp 0 + * expression-based filters +.RE + +.SS Selectors +.B Selectors are the traditional way of filtering syslog messages. +They have been kept in rsyslog with their original syntax, because it is well-known, highly +effective and also needed for compatibility with stock syslogd configuration files. If you just +need to filter based on priority and facility, you should do this with selector lines. They are +not second-class citizens in rsyslog and offer the best performance for this job. + +.SS Property-Based Filters +Property-based filters are unique to rsyslogd. They allow one to filter on any property, like HOSTNAME, +syslogtag and msg. + +A property-based filter must start with a colon in column 0. This tells rsyslogd that it is the new +filter type. The colon must be followed by the property name, a comma, the name of the compare +operation to carry out, another comma and then the value to compare against. This value must be quoted. +There can be spaces and tabs between the commas. Property names and compare operations are +case-sensitive, so "msg" works, while "MSG" is an invalid property name. In brief, the syntax is as follows: +.sp +.RS +:property, [!]compare-operation, "value" +.RE + +The following compare-operations are currently supported: +.sp +.RS +.B contains +.RS +Checks if the string provided in value is contained in the property +.RE +.sp +.B isequal +.RS +Compares the "value" string provided and the property contents. These two values must be exactly equal to match. +.RE +.sp +.B startswith +.RS +Checks if the value is found exactly at the beginning of the property value +.RE +.sp +.B regex +.RS +Compares the property against the provided regular expression. +.RE + +.SS Expression-Based Filters +See the HTML documentation for this feature. + + +.SH TEMPLATES + +Every output in rsyslog uses templates - this holds true for files, user +messages and so on. Templates compatible with the stock syslogd +formats are hardcoded into rsyslogd. If no template is specified, we use +one of these hardcoded templates. Search for "template_" in syslogd.c and +you will find the hardcoded ones. + +A template consists of a template directive, a name, the actual template text +and optional options. A sample is: + +.RS +.B $template MyTemplateName,"\\\\7Text %property% some more text\\\\n",<options> +.RE + +The "$template" is the template directive. It tells rsyslog that this line +contains a template. The backslash is an escape character. For example, \\7 rings the +bell (this is an ASCII value), \\n is a new line. The set in rsyslog is a bit restricted +currently. + +All text in the template is used literally, except for things within percent +signs. These are properties and allow you access to the contents of the syslog +message. Properties are accessed via the property replacer and it can for example +pick a substring or do date-specific formatting. More on this is the PROPERTY REPLACER +section of this manpage. + +To escape: +.sp 0 + % = \\% +.sp 0 + \\ = \\\\ --> '\\' is used to escape (as in C) +.sp 0 +$template TraditionalFormat,"%timegenerated% %HOSTNAME% %syslogtag%%msg%\\n" + +Properties can be accessed by the property replacer (see there for details). + +.B Please note that templates can also by used to generate selector lines with dynamic file names. +For example, if you would like to split syslog messages from different hosts +to different files (one per host), you can define the following template: + +.RS +.B $template DynFile,"/var/log/system-%HOSTNAME%.log" +.RE + +This template can then be used when defining an output selector line. It will +result in something like "/var/log/system-localhost.log" + +.SS Template options +The <options> part is optional. It carries options influencing the template as whole. +See details below. Be sure NOT to mistake template options with property options - the +later ones are processed by the property replacer and apply to a SINGLE property, only +(and not the whole template). + +Template options are case-insensitive. Currently defined are: + +.RS +.TP +sql +format the string suitable for a SQL statement in MySQL format. This will replace single +quotes ("'") and the backslash character by their backslash-escaped counterpart +("\'" and "\\") inside each field. Please note that in MySQL configuration, the NO_BACKSLASH_ESCAPES +mode must be turned off for this format to work (this is the default). + +.TP +stdsql +format the string suitable for a SQL statement that is to be sent to a standards-compliant +sql server. This will replace single quotes ("'") by two single quotes ("''") inside each field. +You must use stdsql together with MySQL if in MySQL configuration the NO_BACKSLASH_ESCAPES +is turned on. +.RE + +Either the +.B sql +or +.B stdsql +option +.B MUST +be specified when a template is used for writing to a database, +otherwise injection might occur. Please note that due to the unfortunate fact +that several vendors have violated the sql standard and introduced their own +escape methods, it is impossible to have a single option doing all the work. +So you yourself must make sure you are using the right format. +.B If you choose the wrong one, you are still vulnerable to sql injection. + +Please note that the database writer *checks* that the sql option is present +in the template. If it is not present, the write database action is disabled. +This is to guard you against accidental forgetting it and then becoming +vulnerable to SQL injection. The sql option can also be useful with files - +especially if you want to import them into a database on another machine for +performance reasons. However, do NOT use it if you do not have a real need for +it - among others, it takes some toll on the processing time. Not much, but on +a really busy system you might notice it ;) + +The default template for the write to database action has the sql option set. + +.SS Template examples +Please note that the samples are split across multiple lines. A template MUST +NOT actually be split across multiple lines. + +A template that resembles traditional syslogd file output: +.sp +.RS +$template TraditionalFormat,"%timegenerated% %HOSTNAME% +.sp 0 +%syslogtag%%msg:::drop-last-lf%\\n" +.RE + +A template that tells you a little more about the message: +.sp +.RS +$template precise,"%syslogpriority%,%syslogfacility%,%timegenerated%,%HOSTNAME%, +.sp 0 +%syslogtag%,%msg%\\n" +.RE + +A template for RFC 3164 format: +.sp +.RS +$template RFC3164fmt,"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%" +.RE + +A template for the format traditionally used for user messages: +.sp +.RS +$template usermsg," XXXX%syslogtag%%msg%\\n\\r" +.RE + +And a template with the traditional wall-message format: +.sp +.RS +$template wallmsg,"\\r\\n\\7Message from syslogd@%HOSTNAME% at %timegenerated%" +.RE + +.B A template that can be used for writing to a database (please note the SQL template option) +.sp +.RS +.ad l +$template MySQLInsert,"insert iut, message, receivedat values +('%iut%', '%msg:::UPPERCASE%', '%timegenerated:::date-mysql%') +into systemevents\\r\\n", SQL + +NOTE 1: This template is embedded into core application under name +.B StdDBFmt +, so you don't need to define it. +.sp +NOTE 2: You have to have MySQL module installed to use this template. +.ad +.RE + +.SH OUTPUT CHANNELS + +Output Channels are a new concept first introduced in rsyslog 0.9.0. As of this writing, +it is most likely that they will be replaced by something different in the future. +So if you use them, be prepared to change you configuration file syntax when you upgrade +to a later release. + +Output channels are defined via an $outchannel directive. It's syntax is as follows: +.sp +.RS +.B $outchannel name,file-name,max-size,action-on-max-size +.RE + +name is the name of the output channel (not the file), file-name is the file name to be +written to, max-size the maximum allowed size and action-on-max-size a command to be issued +when the max size is reached. This command always has exactly one parameter. The binary is +that part of action-on-max-size before the first space, its parameter is everything behind +that space. + +Keep in mind that $outchannel just defines a channel with "name". It does not activate it. +To do so, you must use a selector line (see below). That selector line includes the channel +name plus ":omfile:$" in front of it. A sample might be: +.sp +.RS +*.* :omfile:$mychannel +.RE + +.SH PROPERTY REPLACER +The property replacer is a core component in rsyslogd's output system. A syslog message has +a number of well-defined properties (see below). Each of this properties can be accessed and +manipulated by the property replacer. With it, it is easy to use only part of a property value +or manipulate the value, e.g. by converting all characters to lower case. + +.SS Accessing Properties +Syslog message properties are used inside templates. They are accessed by putting them between +percent signs. Properties can be modified by the property replacer. The full syntax is as follows: +.sp +.RS +.B %propname:fromChar:toChar:options% +.RE + +propname is the name of the property to access. +.B It is case-sensitive. + +.SS Available Properties +.TP +.B msg +the MSG part of the message (aka "the message" ;)) +.TP +.B rawmsg +the message exactly as it was received from the socket. Should be useful for debugging. +.TP +.B HOSTNAME +hostname from the message +.TP +.B FROMHOST +hostname of the system the message was received from (in a relay chain, this is the system immediately +in front of us and not necessarily the original sender) +.TP +.B syslogtag +TAG from the message +.TP +.B programname +the "static" part of the tag, as defined by BSD syslogd. For example, when TAG is "named[12345]", +programname is "named". +.TP +.B PRI +PRI part of the message - undecoded (single value) +.TP +.B PRI-text +the PRI part of the message in a textual form (e.g. "syslog.info") +.TP +.B IUT +the monitorware InfoUnitType - used when talking to a MonitorWare backend (also for phpLogCon) +.TP +.B syslogfacility +the facility from the message - in numerical form +.TP +.B syslogfacility-text +the facility from the message - in text form +.TP +.B syslogseverity +severity from the message - in numerical form +.TP +.B syslogseverity-text +severity from the message - in text form +.TP +.B timegenerated +timestamp when the message was RECEIVED. Always in high resolution +.TP +.B timereported +timestamp from the message. Resolution depends on what was provided in the message (in most cases, only seconds) +.TP +.B TIMESTAMP +alias for timereported +.TP +.B PROTOCOL-VERSION +The contents of the PROTOCOL-VERSION field from IETF draft draft-ietf-syslog-protocol +.TP +.B STRUCTURED-DATA +The contents of the STRUCTURED-DATA field from IETF draft draft-ietf-syslog-protocol +.TP +.B APP-NAME +The contents of the APP-NAME field from IETF draft draft-ietf-syslog-protocol +.TP +.B PROCID +The contents of the PROCID field from IETF draft draft-ietf-syslog-protocol +.TP +.B MSGID +The contents of the MSGID field from IETF draft draft-ietf-syslog-protocol +.TP +.B $NOW +The current date stamp in the format YYYY-MM-DD +.TP +.B $YEAR +The current year (4-digit) +.TP +.B $MONTH +The current month (2-digit) +.TP +.B $DAY +The current day of the month (2-digit) +.TP +.B $HOUR +The current hour in military (24 hour) time (2-digit) +.TP +.B $MINUTE +The current minute (2-digit) + +.P +Properties starting with a $-sign are so-called system properties. These do NOT stem from the +message but are rather internally-generated. + +.SS Character Positions +FromChar and toChar are used to build substrings. They specify the offset within the string that +should be copied. Offset counting starts at 1, so if you need to obtain the first 2 characters of +the message text, you can use this syntax: "%msg:1:2%". If you do not wish to specify from and to, +but you want to specify options, you still need to include the colons. For example, if you would +like to convert the full message text to lower case, use "%msg:::lowercase%". If you would like to +extract from a position until the end of the string, you can place a dollar-sign ("$") in toChar +(e.g. %msg:10:$%, which will extract from position 10 to the end of the string). + +There is also support for +.B regular expressions. +To use them, you need to place a "R" into FromChar. +This tells rsyslog that a regular expression instead of position-based extraction is desired. The +actual regular expression +.B must +then be provided in toChar. The regular expression must be followed +by the string "--end". It denotes the end of the regular expression and will not become part of it. +If you are using regular expressions, the property replacer will return the part of the property text +that matches the regular expression. An example for a property replacer sequence with a regular +expression is: "%msg:R:.*Sev:. \\(.*\\) \\[.*--end%" + +Also, extraction can be done based on so-called "fields". To do so, place a "F" into FromChar. A field +in its current definition is anything that is delimited by a delimiter character. The delimiter by +default is TAB (US-ASCII value 9). However, if can be changed to any other US-ASCII character by +specifying a comma and the decimal US-ASCII value of the delimiter immediately after the "F". For example, +to use comma (",") as a delimiter, use this field specifier: "F,44". If your syslog data is delimited, +this is a quicker way to extract than via regular expressions (actually, a *much* quicker way). Field +counting starts at 1. Field zero is accepted, but will always lead to a "field not found" error. The same +happens if a field number higher than the number of fields in the property is requested. The field number +must be placed in the "ToChar" parameter. An example where the 3rd field (delimited by TAB) from the msg +property is extracted is as follows: "%msg:F:3%". The same example with semicolon as delimiter is +"%msg:F,59:3%". + +Please note that the special characters "F" and "R" are case-sensitive. Only upper case works, lower case +will return an error. There are no white spaces permitted inside the sequence (that will lead to error +messages and will NOT provide the intended result). + +.SS Property Options +Property options are case-insensitive. Currently, the following options are defined: +.TP +uppercase +convert property to lowercase only +.TP +lowercase +convert property text to uppercase only +.TP +drop-last-lf +The last LF in the message (if any), is dropped. Especially useful for PIX. +.TP +date-mysql +format as mysql date +.TP +date-rfc3164 +format as RFC 3164 date +.TP +date-rfc3339 +format as RFC 3339 date +.TP +escape-cc +replace control characters (ASCII value 127 and values less then 32) with an escape sequence. The sequence is "#<charval>" where charval is the 3-digit decimal value of the control character. For example, a tabulator would be replaced by "#009". +.TP +space-cc +replace control characters by spaces +.TP +drop-cc +drop control characters - the resulting string will neither contain control characters, escape sequences nor any other replacement character like space. + +.SH QUEUED OPERATIONS +Rsyslogd supports queued operations to handle offline outputs +(like remote syslogd's or database servers being down). When running in +queued mode, rsyslogd buffers messages to memory and optionally to disk +(on an as-needed basis). Queues survive rsyslogd restarts. + +It is highly suggested to use remote forwarding and database writing +in queued mode, only. + +To learn more about queued operations, see the HTML documentation. + +.SH FILES +.PD 0 +.TP +.I /etc/rsyslog.conf +Configuration file for +.B rsyslogd + +.SH SEE ALSO +.BR rsyslogd (8), +.BR logger (1), +.BR syslog (3) + +The complete documentation can be found in the doc folder of the rsyslog distribution or online at + +.RS +.B https://www.rsyslog.com/doc/ + +.RE +Please note that the man page reflects only a subset of the configuration options. Be sure to read +the HTML documentation for all features and details. This is especially vital if you plan to set +up a more-then-extremely-simple system. + +.SH AUTHORS +.B rsyslogd +is taken from sysklogd sources, which have been heavily modified +by Rainer Gerhards (rgerhards@adiscon.com) and others. diff --git a/tools/rsyslogd.8 b/tools/rsyslogd.8 new file mode 100644 index 0000000..ce69c36 --- /dev/null +++ b/tools/rsyslogd.8 @@ -0,0 +1,320 @@ +.\" Copyright 2004-2014 Rainer Gerhards and Adiscon for the rsyslog modifications +.\" May be distributed under the GNU General Public License +.\" +.TH RSYSLOGD 8 "28 May 2014" "Version 8.1905.0" "Linux System Administration" +.SH NAME +rsyslogd \- reliable and extended syslogd +.SH SYNOPSIS +.B rsyslogd +.RB [ " \-d " ] +.RB [ " \-D " ] +.RB [ " \-f " +.I config file +] +.RB [ " \-i " +.I pid file +] +.RB [ " \-n " ] +.RB [ " \-N " +.I level +] +.RB [ " \-o " +.I fullconf +] +.RB [ " \-C " ] +.RB [ " \-v " ] +.LP +.SH DESCRIPTION +.B Rsyslogd +is a system utility providing support for message logging. +Support of both internet and +unix domain sockets enables this utility to support both local +and remote logging. + +.B Note that this version of rsyslog ships with extensive documentation in HTML format. +This is provided in the ./doc subdirectory and probably +in a separate package if you installed rsyslog via a packaging system. +To use rsyslog's advanced features, you +.B need +to look at the HTML documentation, because the man pages only covers +basic aspects of operation. +.B For details and configuration examples, see the rsyslog.conf (5) +.B man page and the online documentation at https://www.rsyslog.com/doc/ + +.BR Rsyslogd (8) +is derived from the sysklogd package which in turn is derived from the +stock BSD sources. + +.B Rsyslogd +provides a kind of logging that many modern programs use. Every logged +message contains at least a time and a hostname field, normally a +program name field, too, but that depends on how trusty the logging +program is. The rsyslog package supports free definition of output formats +via templates. It also supports precise timestamps and writing directly +to databases. If the database option is used, tools like phpLogCon can +be used to view the log data. + +While the +.B rsyslogd +sources have been heavily modified a couple of notes +are in order. First of all there has been a systematic attempt to +ensure that rsyslogd follows its default, standard BSD behavior. Of course, +some configuration file changes are necessary in order to support the +template system. However, rsyslogd should be able to use a standard +syslog.conf and act like the original syslogd. However, an original syslogd +will not work correctly with a rsyslog-enhanced configuration file. At +best, it will generate funny looking file names. +The second important concept to note is that this version of rsyslogd +interacts transparently with the version of syslog found in the +standard libraries. If a binary linked to the standard shared +libraries fails to function correctly we would like an example of the +anomalous behavior. + +The main configuration file +.I /etc/rsyslog.conf +or an alternative file, given with the +.B "\-f" +option, is read at startup. Any lines that begin with the hash mark +(``#'') and empty lines are ignored. If an error occurs during parsing +the error element is ignored. It is tried to parse the rest of the line. + +.LP +.SH OPTIONS +.TP +.B "\-D" +Runs the Bison config parser in debug mode. This may help when hard to find +syntax errors are reported. Please note that the output generated is deeply +technical and originally targeted towards developers. +.TP +.B "\-d" +Turns on debug mode. See the DEBUGGING section for more information. +.TP +.BI "\-f " "config file" +Specify an alternative configuration file instead of +.IR /etc/rsyslog.conf "," +which is the default. +.TP +.BI "\-i " "pid file" +Specify an alternative pid file instead of the default one. +This option must be used if multiple instances of rsyslogd should +run on a single machine. To disable writing a pid file, use +the reserved name "NONE" (all upper case!), so "-iNONE". +.TP +.B "\-n" +Avoid auto-backgrounding. This is needed especially if the +.B rsyslogd +is started and controlled by +.BR init (8). +.TP +.B "\-N " "level" +Do a config check. Do NOT run in regular mode, just check configuration +file correctness. +This option is meant to verify a config file. To do so, run rsyslogd +interactively in foreground, specifying -f <config-file> and -N level. +The level argument modifies behaviour. Currently, 0 is the same as +not specifying the -N option at all (so this makes limited sense) and +1 actually activates the code. Later, higher levels will mean more +verbosity (this is a forward-compatibility option). +.TP +.B "\-o " "fullconf" +Generates a consolidated config file +.I fullconf +that contains all of rsyslog's configuration in a single file. Include +files are exploded into that file in exactly the way rsyslog sees them. +This option is useful for troubleshooting, especially if problems with +the order of action processing is suspected. It may also be used to +check for "unexepectedly" included config content. +.TP +.BI "\-C" +This prevents rsyslogd from changing to the root directory. This +is almost never a good idea in production use. This option was introduced +in support of the internal testbed. +.TP +.B "\-v" +Print version and exit. +.LP +.SH SIGNALS +.B Rsyslogd +reacts to a set of signals. You may easily send a signal to +.B rsyslogd +using the following: +.IP +.nf +kill -SIGNAL $(cat /var/run/rsyslogd.pid) +.fi +.PP +Note that -SIGNAL must be replaced with the actual signal +you are trying to send, e.g. with HUP. So it then becomes: +.IP +.nf +kill -HUP $(cat /var/run/rsyslogd.pid) +.fi +.PP +.TP +.B HUP +This lets +.B rsyslogd +perform close all open files. +.TP +.B TERM ", " INT ", " QUIT +.B Rsyslogd +will die. +.TP +.B USR1 +Switch debugging on/off. This option can only be used if +.B rsyslogd +is started with the +.B "\-d" +debug option. +.TP +.B CHLD +Wait for children if some were born, because of wall'ing messages. +.LP +.SH SECURITY THREATS +There is the potential for the rsyslogd daemon to be +used as a conduit for a denial of service attack. +A rogue program(mer) could very easily flood the rsyslogd daemon with +syslog messages resulting in the log files consuming all the remaining +space on the filesystem. Activating logging over the inet domain +sockets will of course expose a system to risks outside of programs or +individuals on the local machine. + +There are a number of methods of protecting a machine: +.IP 1. +Implement kernel firewalling to limit which hosts or networks have +access to the 514/UDP socket. +.IP 2. +Logging can be directed to an isolated or non-root filesystem which, +if filled, will not impair the machine. +.IP 3. +The ext2 filesystem can be used which can be configured to limit a +certain percentage of a filesystem to usage by root only. \fBNOTE\fP +that this will require rsyslogd to be run as a non-root process. +\fBALSO NOTE\fP that this will prevent usage of remote logging on the default port since +rsyslogd will be unable to bind to the 514/UDP socket. +.IP 4. +Disabling inet domain sockets will limit risk to the local machine. +.SS Message replay and spoofing +If remote logging is enabled, messages can easily be spoofed and replayed. +As the messages are transmitted in clear-text, an attacker might use +the information obtained from the packets for malicious things. Also, an +attacker might replay recorded messages or spoof a sender's IP address, +which could lead to a wrong perception of system activity. These can +be prevented by using GSS-API authentication and encryption. Be sure +to think about syslog network security before enabling it. +.LP +.SH DEBUGGING +When debugging is turned on using the +.B "\-d" +option, +.B rsyslogd +produces debugging information according to the +.B RSYSLOG_DEBUG +environment variable and the signals received. When run in foreground, +the information is written to stdout. An additional output file can be +specified using the +.B RSYSLOG_DEBUGLOG +environment variable. +.SH FILES +.PD 0 +.TP +.I /etc/rsyslog.conf +Configuration file for +.BR rsyslogd . +See +.BR rsyslog.conf (5) +for exact information. +.TP +.I /dev/log +The Unix domain socket to from where local syslog messages are read. +.TP +.I /var/run/rsyslogd.pid +The file containing the process id of +.BR rsyslogd . +.TP +.I prefix/lib/rsyslog +Default directory for +.B rsyslogd +modules. The +.I prefix +is specified during compilation (e.g. /usr/local). +.SH ENVIRONMENT +.TP +.B RSYSLOG_DEBUG +Controls runtime debug support. It contains an option string with the +following options possible (all are case insensitive): + +.RS +.IP Debug +Turns on debugging and prevents forking. This is processed earlier +in the startup than command line options (i.e. -d) and as such +enables earlier debugging output. Mutually exclusive with DebugOnDemand. +.IP DebugOnDemand +Enables debugging but turns off debug output. The output can be toggled +by sending SIGUSR1. Mutually exclusive with Debug. +.IP LogFuncFlow +Print out the logical flow of functions (entering and exiting them) +.IP FileTrace +Specifies which files to trace LogFuncFlow. If not set (the +default), a LogFuncFlow trace is provided for all files. Set to +limit it to the files specified.FileTrace may be specified multiple +times, one file each (e.g. export RSYSLOG_DEBUG="LogFuncFlow +FileTrace=vm.c FileTrace=expr.c" +.IP PrintFuncDB +Print the content of the debug function database whenever debug +information is printed (e.g. abort case)! +.IP PrintAllDebugInfoOnExit +Print all debug information immediately before rsyslogd exits +(currently not implemented!) +.IP PrintMutexAction +Print mutex action as it happens. Useful for finding deadlocks and +such. +.IP NoLogTimeStamp +Do not prefix log lines with a timestamp (default is to do that). +.IP NoStdOut +Do not emit debug messages to stdout. If RSYSLOG_DEBUGLOG is not +set, this means no messages will be displayed at all. +.IP Help +Display a very short list of commands - hopefully a life saver if +you can't access the documentation... +.RE + +.TP +.B RSYSLOG_DEBUGLOG +If set, writes (almost) all debug message to the specified log file +in addition to stdout. +.TP +.B RSYSLOG_MODDIR +Provides the default directory in which loadable modules reside. +.PD +.SH BUGS +Please review the file BUGS for up-to-date information on known +bugs and annoyances. +.SH Further Information +Please visit +.BR https://www.rsyslog.com/doc/ +for additional information, tutorials and a support forum. +.SH SEE ALSO +.BR rsyslog.conf (5), +.BR logger (1), +.BR syslog (2), +.BR syslog (3), +.BR services (5), +.BR savelog (8) +.LP +.SH COLLABORATORS +.B rsyslogd +is derived from sysklogd sources, which in turn was taken from +the BSD sources. Special thanks to Greg Wettstein (greg@wind.enjellic.com) +and Martin Schulze (joey@linux.de) for the fine sysklogd package. + +.PD 0 +.TP +Rainer Gerhards +.TP +Adiscon GmbH +.TP +Grossrinderfeld, Germany +.TP +rgerhards@adiscon.com +.PD diff --git a/tools/rsyslogd.c b/tools/rsyslogd.c new file mode 100644 index 0000000..d27a2a7 --- /dev/null +++ b/tools/rsyslogd.c @@ -0,0 +1,2362 @@ +/* This is the main rsyslogd file. + * It contains code * that is known to be validly under ASL 2.0, + * because it was either written from scratch by me (rgerhards) or + * contributors who agreed to ASL 2.0. + * + * Copyright 2004-2023 Rainer Gerhards and Adiscon + * + * 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 <signal.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <dirent.h> +#include <unistd.h> +#include <errno.h> +#ifdef ENABLE_LIBLOGGING_STDLOG +# include <liblogging/stdlog.h> +#else +# include <syslog.h> +#endif +#ifdef HAVE_LIBSYSTEMD +# include <systemd/sd-daemon.h> +#endif +#ifdef ENABLE_LIBCAPNG + #include <cap-ng.h> +#endif +#if defined(HAVE_LINUX_CLOSE_RANGE_H) +# include <linux/close_range.h> +#endif + +#include "rsyslog.h" +#include "wti.h" +#include "ratelimit.h" +#include "parser.h" +#include "linkedlist.h" +#include "ruleset.h" +#include "action.h" +#include "iminternal.h" +#include "errmsg.h" +#include "threads.h" +#include "dnscache.h" +#include "prop.h" +#include "unicode-helper.h" +#include "net.h" +#include "glbl.h" +#include "debug.h" +#include "srUtils.h" +#include "rsconf.h" +#include "cfsysline.h" +#include "datetime.h" +#include "operatingstate.h" +#include "dirty.h" +#include "janitor.h" +#include "parserif.h" + +/* some global vars we need to differentiate between environments, + * for TZ-related things see + * https://github.com/rsyslog/rsyslog/issues/2994 + */ +static int runningInContainer = 0; +#ifdef OS_LINUX +static int emitTZWarning = 0; +#else +static int emitTZWarning = 1; +#endif +static pthread_t mainthread = 0; + +#if defined(_AIX) +/* AIXPORT : start + * The following includes and declarations are for support of the System + * Resource Controller (SRC) . + */ +#include <sys/select.h> +/* AIXPORT : start*/ +#define SRC_FD 13 +#define SRCMSG (sizeof(srcpacket)) + +static void deinitAll(void); +#include <spc.h> +static struct srcreq srcpacket; +int cont; +struct srchdr *srchdr; +char progname[128]; + + +/* Normally defined as locals in main + * But here since the functionality is split + * across multiple functions, we make it global + */ +static int rc; +static socklen_t addrsz; +static struct sockaddr srcaddr; +int src_exists = TRUE; +/* src end */ + +/* + * SRC packet processing - . + */ +#define SRCMIN(a, b) (a < b) ? a : b +void +dosrcpacket(msgno, txt, len) + int msgno; + char *txt; + int len; +{ + struct srcrep reply; + + reply.svrreply.rtncode = msgno; +/* AIXPORT : srv was corrected to syslogd */ + strcpy(reply.svrreply.objname, "syslogd"); + snprintf(reply.svrreply.rtnmsg, + SRCMIN(sizeof(reply.svrreply.rtnmsg)-1, strlen(txt)), "%s", txt); + srchdr = srcrrqs((char *)&srcpacket); + srcsrpy(srchdr, (char *)&reply, len, cont); +} + +#define AIX_SRC_EXISTS_IF if(!src_exists) { +#define AIX_SRC_EXISTS_FI } + +static void aix_close_it(int i) +{ + if(src_exists) { + if(i != SRC_FD) + (void)close(i); + } else + close(i); +} + + +#else + +#define AIX_SRC_EXISTS_IF +#define AIX_SRC_EXISTS_FI +#define aix_close_it(x) close(x) +#endif + +/* AIXPORT : end */ + + +DEFobjCurrIf(obj) +DEFobjCurrIf(prop) +DEFobjCurrIf(parser) +DEFobjCurrIf(ruleset) +DEFobjCurrIf(net) +DEFobjCurrIf(rsconf) +DEFobjCurrIf(module) +DEFobjCurrIf(datetime) +DEFobjCurrIf(glbl) + +extern int yydebug; /* interface to flex */ + + +/* forward definitions */ +void rsyslogd_submitErrMsg(const int severity, const int iErr, const uchar *msg); +void rsyslogdDoDie(int sig); + + +#ifndef PATH_PIDFILE +#if defined(_AIX) /* AIXPORT : Add _AIX */ +# define PATH_PIDFILE "/etc/rsyslogd.pid" +#else +# define PATH_PIDFILE "/var/run/rsyslogd.pid" +#endif /*_AIX*/ +#endif + +#ifndef PATH_CONFFILE +# define PATH_CONFFILE "/etc/rsyslog.conf" +#endif + +/* global data items */ +static pthread_mutex_t mutChildDied; +static int bChildDied = 0; +static pthread_mutex_t mutHadHUP; +static int bHadHUP; +static int doFork = 1; /* fork - run in daemon mode - read-only after startup */ +int bFinished = 0; /* used by termination signal handler, read-only except there + * is either 0 or the number of the signal that requested the + * termination. + */ +const char *PidFile = NULL; +#define NO_PIDFILE "NONE" +int iConfigVerify = 0; /* is this just a config verify run? */ +rsconf_t *ourConf = NULL; /* our config object */ +int MarkInterval = 20 * 60; /* interval between marks in seconds - read-only after startup */ +ratelimit_t *dflt_ratelimiter = NULL; /* ratelimiter for submits without explicit one */ +uchar *ConfFile = (uchar*) PATH_CONFFILE; +int bHaveMainQueue = 0;/* set to 1 if the main queue - in queueing mode - is available + * If the main queue is either not yet ready or not running in + * queueing mode (mode DIRECT!), then this is set to 0. + */ +prop_t *pInternalInputName = NULL; /* there is only one global inputName for all internally-generated messages */ +ratelimit_t *internalMsg_ratelimiter = NULL; /* ratelimiter for rsyslog-own messages */ +int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */ + +static struct queuefilenames_s { + struct queuefilenames_s *next; + uchar *name; +} *queuefilenames = NULL; + + +static __attribute__((noreturn)) void +rsyslogd_usage(void) +{ + fprintf(stderr, "usage: rsyslogd [options]\n" + "use \"man rsyslogd\" for details. To run rsyslog " + "interactively, use \"rsyslogd -n\"\n" + "to run it in debug mode use \"rsyslogd -dn\"\n" + "For further information see https://www.rsyslog.com/doc/\n"); + exit(1); /* "good" exit - done to terminate usage() */ +} + +#ifndef HAVE_SETSID +extern void untty(void); /* in syslogd.c, GPLv3 */ +static int +setsid(void) +{ + untty(); + return 0; +} +#endif + +/* helper for imdiag. Returns if HUP processing has been requested or + * is not yet finished. We know this is racy, but imdiag handles this + * part by repeating operations. The mutex look is primarily to force + * a memory barrier, so that we have a change to see changes already + * written, but not present in the core's cache. + * 2023-07-26 Rainer Gerhards + */ +int +get_bHadHUP(void) +{ + pthread_mutex_lock(&mutHadHUP); + const int ret = bHadHUP; + pthread_mutex_unlock(&mutHadHUP); + /* note: at this point ret can already be invalid */ + return ret; +} + +/* we need a pointer to the conf, because in early startup stage we + * need to use loadConf, later on runConf. + */ +rsRetVal +queryLocalHostname(rsconf_t *const pConf) +{ + uchar *LocalHostName = NULL; + uchar *LocalDomain = NULL; + uchar *LocalFQDNName; + DEFiRet; + + CHKiRet(net.getLocalHostname(pConf, &LocalFQDNName)); + uchar *dot = (uchar*) strstr((char*)LocalFQDNName, "."); + if(dot == NULL) { + CHKmalloc(LocalHostName = (uchar*) strdup((char*)LocalFQDNName)); + CHKmalloc(LocalDomain = (uchar*)strdup("")); + } else { + const size_t lenhn = dot - LocalFQDNName; + CHKmalloc(LocalHostName = (uchar*) strndup((char*) LocalFQDNName, lenhn)); + CHKmalloc(LocalDomain = (uchar*) strdup((char*) dot+1)); + } + + glbl.SetLocalFQDNName(LocalFQDNName); + glbl.SetLocalHostName(LocalHostName); + glbl.SetLocalDomain(LocalDomain); + glbl.GenerateLocalHostNameProperty(); + LocalHostName = NULL; /* handed over */ + LocalDomain = NULL; /* handed over */ + +finalize_it: + free(LocalHostName); + free(LocalDomain); + RETiRet; +} + +static rsRetVal +writePidFile(void) +{ + FILE *fp; + DEFiRet; + + const char *tmpPidFile; + + if(!strcmp(PidFile, NO_PIDFILE)) { + FINALIZE; + } + if(asprintf((char **)&tmpPidFile, "%s.tmp", PidFile) == -1) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + if(tmpPidFile == NULL) + tmpPidFile = PidFile; + DBGPRINTF("rsyslogd: writing pidfile '%s'.\n", tmpPidFile); + if((fp = fopen((char*) tmpPidFile, "w")) == NULL) { + perror("rsyslogd: error writing pid file (creation stage)\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + if(fprintf(fp, "%d", (int) glblGetOurPid()) < 0) { + LogError(errno, iRet, "rsyslog: error writing pid file"); + } + fclose(fp); + if(tmpPidFile != PidFile) { + if(rename(tmpPidFile, PidFile) != 0) { + perror("rsyslogd: error writing pid file (rename stage)"); + } + free((void*)tmpPidFile); + } +finalize_it: + RETiRet; +} + +static void +clearPidFile(void) +{ + if(PidFile != NULL) { + if(strcmp(PidFile, NO_PIDFILE)) { + unlink(PidFile); + } + } +} + +/* duplicate startup protection: check, based on pid file, if our instance + * is already running. This MUST be called before we write our own pid file. + */ +static rsRetVal +checkStartupOK(void) +{ + FILE *fp = NULL; + DEFiRet; + + DBGPRINTF("rsyslogd: checking if startup is ok, pidfile '%s'.\n", PidFile); + + if(!strcmp(PidFile, NO_PIDFILE)) { + dbgprintf("no pid file shall be written, skipping check\n"); + FINALIZE; + } + + if((fp = fopen((char*) PidFile, "r")) == NULL) + FINALIZE; /* all well, no pid file yet */ + + int pf_pid; + if(fscanf(fp, "%d", &pf_pid) != 1) { + fprintf(stderr, "rsyslogd: error reading pid file, cannot start up\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + + /* ok, we got a pid, let's check if the process is running */ + const pid_t pid = (pid_t) pf_pid; + if(kill(pid, 0) == 0 || errno != ESRCH) { + fprintf(stderr, "rsyslogd: pidfile '%s' and pid %d already exist.\n" + "If you want to run multiple instances of rsyslog, you need " + "to specify\n" + "different pid files for them (-i option).\n", + PidFile, (int) getpid()); + ABORT_FINALIZE(RS_RET_ERR); + } + +finalize_it: + if(fp != NULL) + fclose(fp); + RETiRet; +} + + + +/* note: this function is specific to OS'es which provide + * the ability to read open file descriptors via /proc. + * returns 0 - success, something else otherwise + */ +static int +close_unneeded_open_files(const char *const procdir, + const int beginClose, const int parentPipeFD) +{ + DIR *dir; + struct dirent *entry; + + dir = opendir(procdir); + if (dir == NULL) { + dbgprintf("closes unneeded files: opendir failed for %s\n", procdir); + return 1; + } + + while ((entry = readdir(dir)) != NULL) { + const int fd = atoi(entry->d_name); + if(fd >= beginClose && (((fd != dbgGetDbglogFd()) && (fd != parentPipeFD)))) { + close(fd); + } + } + + closedir(dir); + return 0; +} + +/* prepares the background processes (if auto-backbrounding) for + * operation. + */ +static void +prepareBackground(const int parentPipeFD) +{ + DBGPRINTF("rsyslogd: in child, finalizing initialization\n"); + + dbgTimeoutToStderr = 0; /* we loose stderr when backgrounding! */ + int r = setsid(); + if(r == -1) { + char err[1024]; + char em[2048]; + rs_strerror_r(errno, err, sizeof(err)); + snprintf(em, sizeof(em)-1, "rsyslog: error " + "auto-backgrounding: %s\n", err); + dbgprintf("%s\n", em); + fprintf(stderr, "%s", em); + } + + int beginClose = 3; + +#ifdef HAVE_LIBSYSTEMD + /* running under systemd? Then we must make sure we "forward" any + * fds passed by it (adjust the pid). + */ + if(sd_booted()) { + const char *lstnPid = getenv("LISTEN_PID"); + if(lstnPid != NULL) { + char szBuf[64]; + const int lstnPidI = atoi(lstnPid); + snprintf(szBuf, sizeof(szBuf), "%d", lstnPidI); + if(!strcmp(szBuf, lstnPid) && lstnPidI == getppid()) { + snprintf(szBuf, sizeof(szBuf), "%d", (int) getpid()); + setenv("LISTEN_PID", szBuf, 1); + /* ensure we do not close what systemd provided */ + const int nFds = sd_listen_fds(0); + if(nFds > 0) { + beginClose = SD_LISTEN_FDS_START + nFds; + } + } + } + } +#endif + + /* close unnecessary open files - first try to use /proc file system, + * if that is not possible iterate through all potentially open file + * descriptors. This can be lenghty, but in practice /proc should work + * for almost all current systems, and the fallback is primarily for + * Solaris and AIX, where we do expect a decent max numbers of fds. + */ + close(0); /* always close stdin, we do not need it */ + + /* try Linux, Cygwin, NetBSD */ + if(close_unneeded_open_files("/proc/self/fd", beginClose, parentPipeFD) != 0) { + /* try MacOS, FreeBSD */ + if(close_unneeded_open_files("/proc/fd", beginClose, parentPipeFD) != 0) { + /* did not work out, so let's close everything... */ + int endClose = (parentPipeFD > dbgGetDbglogFd()) ? parentPipeFD : dbgGetDbglogFd(); + for(int i = beginClose ; i <= endClose ; ++i) { + if((i != dbgGetDbglogFd()) && (i != parentPipeFD)) { + aix_close_it(i); /* AIXPORT */ + } + } + beginClose = endClose + 1; + endClose = getdtablesize(); +#if defined(HAVE_CLOSE_RANGE) + if(close_range(beginClose, endClose, 0) !=0) { + dbgprintf("errno %d after close_range(), fallback to loop\n", errno); +#endif + for(int i = beginClose ; i <= endClose ; ++i) { + aix_close_it(i); /* AIXPORT */ + } +#if defined(HAVE_CLOSE_RANGE) + } +#endif + } + } + seedRandomNumberForChild(); +} + +/* This is called when rsyslog is set to auto-background itself. If so, a child + * is forked and the parent waits until it is initialized. + * The parent never returns from this function, only this happens for the child. + * So if it returns, you know you are in the child. + * return: file descriptor to which the child needs to write an "OK" or error + * message. + */ +static int +forkRsyslog(void) +{ + int pipefd[2]; + pid_t cpid; + char err[1024]; + char msgBuf[4096]; + + dbgprintf("rsyslogd: parent ready for forking\n"); + if(pipe(pipefd) == -1) { + perror("error creating rsyslog \"fork pipe\" - terminating"); + exit(1); + } + AIX_SRC_EXISTS_IF /* AIXPORT */ + cpid = fork(); + if(cpid == -1) { + perror("error forking rsyslogd process - terminating"); + exit(1); + } + AIX_SRC_EXISTS_FI /* AIXPORT */ + + if(cpid == 0) { + prepareBackground(pipefd[1]); + close(pipefd[0]); + return pipefd[1]; + } + + /* we are now in the parent. All we need to do here is wait for the + * startup message, emit it (if necessary) and then terminate. + */ + close(pipefd[1]); + dbgprintf("rsyslogd: parent waiting up to 60 seconds to read startup message\n"); + + fd_set rfds; + struct timeval tv; + int retval; + + FD_ZERO(&rfds); + FD_SET(pipefd[0], &rfds); + tv.tv_sec = 60; + tv.tv_usec = 0; + + retval = select(pipefd[0]+1, &rfds, NULL, NULL, &tv); + if(retval == -1) + rs_strerror_r(errno, err, sizeof(err)); + else + strcpy(err, "OK"); + dbgprintf("rsyslogd: select() returns %d: %s\n", retval, err); + if(retval == -1) { + fprintf(stderr,"rsyslog startup failure, select() failed: %s\n", err); + exit(1); + } else if(retval == 0) { + fprintf(stderr,"rsyslog startup failure, child did not " + "respond within startup timeout (60 seconds)\n"); + exit(1); + } + + int nRead = read(pipefd[0], msgBuf, sizeof(msgBuf)); + if(nRead > 0) { + msgBuf[nRead] = '\0'; + } else { + rs_strerror_r(errno, err, sizeof(err)); + snprintf(msgBuf, sizeof(msgBuf)-1, "error reading \"fork pipe\": %s", + err); + } + if(strcmp(msgBuf, "OK")) { + dbgprintf("rsyslog parent startup failure: %s\n", msgBuf); + fprintf(stderr,"rsyslog startup failure: %s\n", msgBuf); + exit(1); + } + close(pipefd[0]); + dbgprintf("rsyslogd: parent terminates after successful child startup\n"); + exit(0); +} + +/* startup processing: this signals the waiting parent that the child is ready + * and the parent may terminate. + */ +static void +tellChildReady(const int pipefd, const char *const msg) +{ + dbgprintf("rsyslogd: child signaling OK\n"); + const int nWritten = write(pipefd, msg, strlen(msg)); + dbgprintf("rsyslogd: child signalled OK, nWritten %d\n", (int) nWritten); + close(pipefd); + sleep(1); +} + +/* print version and compile-time setting information */ +static void +printVersion(void) +{ + printf("rsyslogd " VERSION " (aka %4d.%2.2d) compiled with:\n", + 2000 + VERSION_YEAR, VERSION_MONTH); + printf("\tPLATFORM:\t\t\t\t%s\n", PLATFORM_ID); + printf("\tPLATFORM (lsb_release -d):\t\t%s\n", PLATFORM_ID_LSB); +#ifdef FEATURE_REGEXP + printf("\tFEATURE_REGEXP:\t\t\t\tYes\n"); +#else + printf("\tFEATURE_REGEXP:\t\t\t\tNo\n"); +#endif +#if defined(SYSLOG_INET) && defined(USE_GSSAPI) + printf("\tGSSAPI Kerberos 5 support:\t\tYes\n"); +#else + printf("\tGSSAPI Kerberos 5 support:\t\tNo\n"); +#endif +#ifndef NDEBUG + printf("\tFEATURE_DEBUG (debug build, slow code):\tYes\n"); +#else + printf("\tFEATURE_DEBUG (debug build, slow code):\tNo\n"); +#endif +#ifdef HAVE_ATOMIC_BUILTINS + printf("\t32bit Atomic operations supported:\tYes\n"); +#else + printf("\t32bit Atomic operations supported:\tNo\n"); +#endif +#ifdef HAVE_ATOMIC_BUILTINS64 + printf("\t64bit Atomic operations supported:\tYes\n"); +#else + printf("\t64bit Atomic operations supported:\tNo\n"); +#endif +#ifdef HAVE_JEMALLOC + printf("\tmemory allocator:\t\t\tjemalloc\n"); +#else + printf("\tmemory allocator:\t\t\tsystem default\n"); +#endif +#ifdef RTINST + printf("\tRuntime Instrumentation (slow code):\tYes\n"); +#else + printf("\tRuntime Instrumentation (slow code):\tNo\n"); +#endif +#ifdef USE_LIBUUID + printf("\tuuid support:\t\t\t\tYes\n"); +#else + printf("\tuuid support:\t\t\t\tNo\n"); +#endif +#ifdef HAVE_LIBSYSTEMD + printf("\tsystemd support:\t\t\tYes\n"); +#else + printf("\tsystemd support:\t\t\tNo\n"); +#endif + /* we keep the following message to so that users don't need + * to wonder. + */ + printf("\tConfig file:\t\t\t\t" PATH_CONFFILE "\n"); + printf("\tPID file:\t\t\t\t" PATH_PIDFILE "%s\n", PATH_PIDFILE[0]!='/'? + "(relative to global workingdirectory)":""); + printf("\tNumber of Bits in RainerScript integers: 64\n"); + printf("\nSee https://www.rsyslog.com for more information.\n"); +} + +static rsRetVal +rsyslogd_InitStdRatelimiters(void) +{ + DEFiRet; + CHKiRet(ratelimitNew(&dflt_ratelimiter, "rsyslogd", "dflt")); + CHKiRet(ratelimitNew(&internalMsg_ratelimiter, "rsyslogd", "internal_messages")); + ratelimitSetThreadSafe(internalMsg_ratelimiter); + ratelimitSetLinuxLike(internalMsg_ratelimiter, + loadConf->globals.intMsgRateLimitItv, loadConf->globals.intMsgRateLimitBurst); + /* TODO: make internalMsg ratelimit settings configurable */ +finalize_it: + RETiRet; +} + + +/* Method to initialize all global classes and use the objects that we need. + * rgerhards, 2008-01-04 + * rgerhards, 2008-04-16: the actual initialization is now carried out by the runtime + */ +static rsRetVal +rsyslogd_InitGlobalClasses(void) +{ + DEFiRet; + const char *pErrObj; /* tells us which object failed if that happens (useful for troubleshooting!) */ + + /* Intialize the runtime system */ + pErrObj = "rsyslog runtime"; /* set in case the runtime errors before setting an object */ + CHKiRet(rsrtInit(&pErrObj, &obj)); + rsrtSetErrLogger(rsyslogd_submitErrMsg); + + /* Now tell the system which classes we need ourselfs */ + pErrObj = "glbl"; + CHKiRet(objUse(glbl, CORE_COMPONENT)); + pErrObj = "module"; + CHKiRet(objUse(module, CORE_COMPONENT)); + pErrObj = "datetime"; + CHKiRet(objUse(datetime, CORE_COMPONENT)); + pErrObj = "ruleset"; + CHKiRet(objUse(ruleset, CORE_COMPONENT)); + pErrObj = "prop"; + CHKiRet(objUse(prop, CORE_COMPONENT)); + pErrObj = "parser"; + CHKiRet(objUse(parser, CORE_COMPONENT)); + pErrObj = "rsconf"; + CHKiRet(objUse(rsconf, CORE_COMPONENT)); + + /* intialize some dummy classes that are not part of the runtime */ + pErrObj = "action"; + CHKiRet(actionClassInit()); + pErrObj = "template"; + CHKiRet(templateInit()); + + /* TODO: the dependency on net shall go away! -- rgerhards, 2008-03-07 */ + pErrObj = "net"; + CHKiRet(objUse(net, LM_NET_FILENAME)); + + dnscacheInit(); + initRainerscript(); + ratelimitModInit(); + + /* we need to create the inputName property (only once during our lifetime) */ + CHKiRet(prop.Construct(&pInternalInputName)); + CHKiRet(prop.SetString(pInternalInputName, UCHAR_CONSTANT("rsyslogd"), sizeof("rsyslogd") - 1)); + CHKiRet(prop.ConstructFinalize(pInternalInputName)); + +finalize_it: + if(iRet != RS_RET_OK) { + /* we know we are inside the init sequence, so we can safely emit + * messages to stderr. -- rgerhards, 2008-04-02 + */ + fprintf(stderr, "Error during class init for object '%s' - failing...\n", pErrObj); + fprintf(stderr, "rsyslogd initialization failed - global classes could not be initialized.\n" + "Did you do a \"make install\"?\n" + "Suggested action: run rsyslogd with -d -n options to see what exactly " + "fails.\n"); + } + + RETiRet; +} + +/* preprocess a batch of messages, that is ready them for actual processing. This is done + * as a first stage and totally in parallel to any other worker active in the system. So + * it helps us keep up the overall concurrency level. + * rgerhards, 2010-06-09 + */ +static rsRetVal +preprocessBatch(batch_t *pBatch, int *pbShutdownImmediate) { + prop_t *ip; + prop_t *fqdn; + prop_t *localName; + int bIsPermitted; + smsg_t *pMsg; + int i; + rsRetVal localRet; + DEFiRet; + + for(i = 0 ; i < pBatch->nElem && !*pbShutdownImmediate ; i++) { + pMsg = pBatch->pElem[i].pMsg; + if((pMsg->msgFlags & NEEDS_ACLCHK_U) != 0) { + DBGPRINTF("msgConsumer: UDP ACL must be checked for message (hostname-based)\n"); + if(net.cvthname(pMsg->rcvFrom.pfrominet, &localName, &fqdn, &ip) != RS_RET_OK) + continue; + bIsPermitted = net.isAllowedSender2((uchar*)"UDP", + (struct sockaddr *)pMsg->rcvFrom.pfrominet, (char*)propGetSzStr(fqdn), 1); + if(!bIsPermitted) { + DBGPRINTF("Message from '%s' discarded, not a permitted sender host\n", + propGetSzStr(fqdn)); + pBatch->eltState[i] = BATCH_STATE_DISC; + } else { + /* save some of the info we obtained */ + MsgSetRcvFrom(pMsg, localName); + CHKiRet(MsgSetRcvFromIP(pMsg, ip)); + pMsg->msgFlags &= ~NEEDS_ACLCHK_U; + } + } + if((pMsg->msgFlags & NEEDS_PARSING) != 0) { + if((localRet = parser.ParseMsg(pMsg)) != RS_RET_OK) { + DBGPRINTF("Message discarded, parsing error %d\n", localRet); + pBatch->eltState[i] = BATCH_STATE_DISC; + } + } + } + +finalize_it: + RETiRet; +} + + +/* The consumer of dequeued messages. This function is called by the + * queue engine on dequeueing of a message. It runs on a SEPARATE + * THREAD. It receives an array of pointers, which it must iterate + * over. We do not do any further batching, as this is of no benefit + * for the main queue. + */ +static rsRetVal +msgConsumer(void __attribute__((unused)) *notNeeded, batch_t *pBatch, wti_t *pWti) +{ + DEFiRet; + assert(pBatch != NULL); + preprocessBatch(pBatch, pWti->pbShutdownImmediate); + ruleset.ProcessBatch(pBatch, pWti); +//TODO: the BATCH_STATE_COMM must be set somewhere down the road, but we +//do not have this yet and so we emulate -- 2010-06-10 +int i; + for(i = 0 ; i < pBatch->nElem && !*pWti->pbShutdownImmediate ; i++) { + pBatch->eltState[i] = BATCH_STATE_COMM; + } + RETiRet; +} + + +/* create a main message queue, now also used for ruleset queues. This function + * needs to be moved to some other module, but it is considered acceptable for + * the time being (remember that we want to restructure config processing at large!). + * rgerhards, 2009-10-27 + */ +rsRetVal createMainQueue(qqueue_t **ppQueue, uchar *pszQueueName, struct nvlst *lst) +{ + struct queuefilenames_s *qfn; + uchar *qfname = NULL; + static int qfn_renamenum = 0; + uchar qfrenamebuf[1024]; + DEFiRet; + + /* create message queue */ + CHKiRet_Hdlr(qqueueConstruct(ppQueue, ourConf->globals.mainQ.MainMsgQueType, + ourConf->globals.mainQ.iMainMsgQueueNumWorkers, ourConf->globals.mainQ.iMainMsgQueueSize, msgConsumer)) { + /* no queue is fatal, we need to give up in that case... */ + LogError(0, iRet, "could not create (ruleset) main message queue"); \ + } + /* name our main queue object (it's not fatal if it fails...) */ + obj.SetName((obj_t*) (*ppQueue), pszQueueName); + + if(lst == NULL) { /* use legacy parameters? */ + /* ... set some properties ... */ + # define setQPROP(func, directive, data) \ + CHKiRet_Hdlr(func(*ppQueue, data)) { \ + LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, " \ + "running with default setting", iRet); \ + } + # define setQPROPstr(func, directive, data) \ + CHKiRet_Hdlr(func(*ppQueue, data, (data == NULL)? 0 : strlen((char*) data))) { \ + LogError(0, NO_ERRCODE, "Invalid " #directive ", error %d. Ignored, " \ + "running with default setting", iRet); \ + } + + if(ourConf->globals.mainQ.pszMainMsgQFName != NULL) { + /* check if the queue file name is unique, else emit an error */ + for(qfn = queuefilenames ; qfn != NULL ; qfn = qfn->next) { + dbgprintf("check queue file name '%s' vs '%s'\n", qfn->name, + ourConf->globals.mainQ.pszMainMsgQFName ); + if(!ustrcmp(qfn->name, ourConf->globals.mainQ.pszMainMsgQFName)) { + snprintf((char*)qfrenamebuf, sizeof(qfrenamebuf), "%d-%s-%s", + ++qfn_renamenum, ourConf->globals.mainQ.pszMainMsgQFName, + (pszQueueName == NULL) ? "NONAME" : (char*)pszQueueName); + qfname = ustrdup(qfrenamebuf); + LogError(0, NO_ERRCODE, "Error: queue file name '%s' already in use " + " - using '%s' instead", ourConf->globals.mainQ.pszMainMsgQFName, + qfname); + break; + } + } + if(qfname == NULL) + qfname = ustrdup(ourConf->globals.mainQ.pszMainMsgQFName); + qfn = malloc(sizeof(struct queuefilenames_s)); + qfn->name = qfname; + qfn->next = queuefilenames; + queuefilenames = qfn; + } + + setQPROP(qqueueSetMaxFileSize, "$MainMsgQueueFileSize", + ourConf->globals.mainQ.iMainMsgQueMaxFileSize); + setQPROP(qqueueSetsizeOnDiskMax, "$MainMsgQueueMaxDiskSpace", + ourConf->globals.mainQ.iMainMsgQueMaxDiskSpace); + setQPROP(qqueueSetiDeqBatchSize, "$MainMsgQueueDequeueBatchSize", + ourConf->globals.mainQ.iMainMsgQueDeqBatchSize); + setQPROPstr(qqueueSetFilePrefix, "$MainMsgQueueFileName", qfname); + setQPROP(qqueueSetiPersistUpdCnt, "$MainMsgQueueCheckpointInterval", + ourConf->globals.mainQ.iMainMsgQPersistUpdCnt); + setQPROP(qqueueSetbSyncQueueFiles, "$MainMsgQueueSyncQueueFiles", + ourConf->globals.mainQ.bMainMsgQSyncQeueFiles); + setQPROP(qqueueSettoQShutdown, "$MainMsgQueueTimeoutShutdown", + ourConf->globals.mainQ.iMainMsgQtoQShutdown ); + setQPROP(qqueueSettoActShutdown, "$MainMsgQueueTimeoutActionCompletion", + ourConf->globals.mainQ.iMainMsgQtoActShutdown); + setQPROP(qqueueSettoWrkShutdown, "$MainMsgQueueWorkerTimeoutThreadShutdown", + ourConf->globals.mainQ.iMainMsgQtoWrkShutdown); + setQPROP(qqueueSettoEnq, "$MainMsgQueueTimeoutEnqueue", ourConf->globals.mainQ.iMainMsgQtoEnq); + setQPROP(qqueueSetiHighWtrMrk, "$MainMsgQueueHighWaterMark", + ourConf->globals.mainQ.iMainMsgQHighWtrMark); + setQPROP(qqueueSetiLowWtrMrk, "$MainMsgQueueLowWaterMark", + ourConf->globals.mainQ.iMainMsgQLowWtrMark); + setQPROP(qqueueSetiDiscardMrk, "$MainMsgQueueDiscardMark", + ourConf->globals.mainQ.iMainMsgQDiscardMark); + setQPROP(qqueueSetiDiscardSeverity, "$MainMsgQueueDiscardSeverity", + ourConf->globals.mainQ.iMainMsgQDiscardSeverity); + setQPROP(qqueueSetiMinMsgsPerWrkr, "$MainMsgQueueWorkerThreadMinimumMessages", + ourConf->globals.mainQ.iMainMsgQWrkMinMsgs); + setQPROP(qqueueSetbSaveOnShutdown, "$MainMsgQueueSaveOnShutdown", + ourConf->globals.mainQ.bMainMsgQSaveOnShutdown); + setQPROP(qqueueSetiDeqSlowdown, "$MainMsgQueueDequeueSlowdown", + ourConf->globals.mainQ.iMainMsgQDeqSlowdown); + setQPROP(qqueueSetiDeqtWinFromHr, "$MainMsgQueueDequeueTimeBegin", + ourConf->globals.mainQ.iMainMsgQueueDeqtWinFromHr); + setQPROP(qqueueSetiDeqtWinToHr, "$MainMsgQueueDequeueTimeEnd", + ourConf->globals.mainQ.iMainMsgQueueDeqtWinToHr); + + # undef setQPROP + # undef setQPROPstr + } else { /* use new style config! */ + qqueueSetDefaultsRulesetQueue(*ppQueue); + qqueueApplyCnfParam(*ppQueue, lst); + } + qqueueCorrectParams(*ppQueue); + + RETiRet; +} + +rsRetVal +startMainQueue(rsconf_t *cnf, qqueue_t *const pQueue) +{ + DEFiRet; + CHKiRet_Hdlr(qqueueStart(cnf, pQueue)) { + /* no queue is fatal, we need to give up in that case... */ + LogError(0, iRet, "could not start (ruleset) main message queue"); + if(runConf->globals.bAbortOnFailedQueueStartup) { + fprintf(stderr, "rsyslogd: could not start (ruleset) main message queue, " + "abortOnFailedQueueStartup is set, so we abort rsyslog now.\n"); + fflush(stderr); + clearPidFile(); + exit(1); /* "good" exit, this is intended here */ + } + pQueue->qType = QUEUETYPE_DIRECT; + CHKiRet_Hdlr(qqueueStart(cnf, pQueue)) { + /* no queue is fatal, we need to give up in that case... */ + LogError(0, iRet, "fatal error: could not even start queue in direct mode"); + } + } + RETiRet; +} + + +/* this is a special function used to submit an error message. This + * function is also passed to the runtime library as the generic error + * message handler. -- rgerhards, 2008-04-17 + */ +void +rsyslogd_submitErrMsg(const int severity, const int iErr, const uchar *msg) +{ + if (glbl.GetGlobalInputTermState() == 1) { + /* After fork the stderr is unusable (dfltErrLogger uses is internally) */ + if(!doFork) + dfltErrLogger(severity, iErr, msg); + } else { + logmsgInternal(iErr, LOG_SYSLOG|(severity & 0x07), msg, 0); + } +} + +static inline rsRetVal +submitMsgWithDfltRatelimiter(smsg_t *pMsg) +{ + return ratelimitAddMsg(dflt_ratelimiter, NULL, pMsg); +} + + +static void +logmsgInternal_doWrite(smsg_t *pMsg) +{ + const int pri = getPRIi(pMsg); + if(pri % 8 <= runConf->globals.intMsgsSeverityFilter) { + if(runConf->globals.bProcessInternalMessages) { + submitMsg2(pMsg); + pMsg = NULL; /* msg obj handed over; do not destruct */ + } else { + uchar *const msg = getMSG(pMsg); + #ifdef ENABLE_LIBLOGGING_STDLOG + /* the "emit only once" rate limiter is quick and dirty and not + * thread safe. However, that's no problem for the current intend + * and it is not justified to create more robust code for the + * functionality. -- rgerhards, 2018-05-14 + */ + static warnmsg_emitted = 0; + if(warnmsg_emitted == 0) { + stdlog_log(runConf->globals.stdlog_hdl, LOG_WARNING, "%s", + "RSYSLOG WARNING: liblogging-stdlog " + "functionality will go away soon. For details see " + "https://github.com/rsyslog/rsyslog/issues/2706"); + warnmsg_emitted = 1; + } + stdlog_log(runConf->globals.stdlog_hdl, pri2sev(pri), "%s", (char*)msg); + #else + syslog(pri, "%s", msg); + #endif + } + } + if(pMsg != NULL) { + msgDestruct(&pMsg); + } +} + +/* This function creates a log message object out of the provided + * message text and forwards it for logging. + */ +static rsRetVal +logmsgInternalSubmit(const int iErr, const syslog_pri_t pri, const size_t lenMsg, + const char *__restrict__ const msg, int flags) +{ + uchar pszTag[33]; + smsg_t *pMsg; + DEFiRet; + + CHKiRet(msgConstruct(&pMsg)); + MsgSetInputName(pMsg, pInternalInputName); + MsgSetRawMsg(pMsg, (char*)msg, lenMsg); + MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())); + MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp()); + MsgSetRcvFromIP(pMsg, glbl.GetLocalHostIP()); + MsgSetMSGoffs(pMsg, 0); + /* check if we have an error code associated and, if so, + * adjust the tag. -- rgerhards, 2008-06-27 + */ + if(iErr == NO_ERRCODE) { + MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd:"), sizeof("rsyslogd:") - 1); + } else { + size_t len = snprintf((char*)pszTag, sizeof(pszTag), "rsyslogd%d:", iErr); + pszTag[32] = '\0'; /* just to make sure... */ + MsgSetTAG(pMsg, pszTag, len); + } + flags |= INTERNAL_MSG; + pMsg->msgFlags = flags; + msgSetPRI(pMsg, pri); + + iminternalAddMsg(pMsg); +finalize_it: + RETiRet; +} + + + +/* rgerhards 2004-11-09: the following is a function that can be used + * to log a message orginating from the syslogd itself. + */ +rsRetVal +logmsgInternal(int iErr, const syslog_pri_t pri, const uchar *const msg, int flags) +{ + size_t lenMsg; + unsigned i; + char *bufModMsg = NULL; /* buffer for modified message, should we need to modify */ + DEFiRet; + + /* we first do a path the remove control characters that may have accidently + * introduced (program error!). This costs performance, but we do not expect + * to be called very frequently in any case ;) -- rgerhards, 2013-12-19. + */ + lenMsg = ustrlen(msg); + for(i = 0 ; i < lenMsg ; ++i) { + if(msg[i] < 0x20 || msg[i] == 0x7f) { + if(bufModMsg == NULL) { + CHKmalloc(bufModMsg = strdup((char*) msg)); + } + bufModMsg[i] = ' '; + } + } + + CHKiRet(logmsgInternalSubmit(iErr, pri, lenMsg, + (bufModMsg == NULL) ? (char*)msg : bufModMsg, + flags)); + + /* we now check if we should print internal messages out to stderr. This was + * suggested by HKS as a way to help people troubleshoot rsyslog configuration + * (by running it interactively. This makes an awful lot of sense, so I add + * it here. -- rgerhards, 2008-07-28 + * Note that error messages can not be disabled during a config verify. This + * permits us to process unmodified config files which otherwise contain a + * supressor statement. + */ + int emit_to_stderr = (ourConf == NULL) ? 1 : ourConf->globals.bErrMsgToStderr; + int emit_supress_msg = 0; + if(Debug == DEBUG_FULL || !doFork) { + emit_to_stderr = 1; + } + if(ourConf != NULL && ourConf->globals.maxErrMsgToStderr != -1) { + if(emit_to_stderr && ourConf->globals.maxErrMsgToStderr != -1 && ourConf->globals.maxErrMsgToStderr) { + --ourConf->globals.maxErrMsgToStderr; + if(ourConf->globals.maxErrMsgToStderr == 0) + emit_supress_msg = 1; + } else { + emit_to_stderr = 0; + } + } + if(emit_to_stderr || iConfigVerify) { + if(pri2sev(pri) == LOG_ERR) + fprintf(stderr, "rsyslogd: %s\n", + (bufModMsg == NULL) ? (char*)msg : bufModMsg); + } + if(emit_supress_msg) { + fprintf(stderr, "rsyslogd: configured max number of error messages " + "to stderr reached, further messages will not be output\n" + "Consider adjusting\n" + " global(errorMessagesToStderr.maxNumber=\"xx\")\n" + "if you want more.\n"); + } + +finalize_it: + free(bufModMsg); + RETiRet; +} + +rsRetVal +submitMsg(smsg_t *pMsg) +{ + return submitMsgWithDfltRatelimiter(pMsg); +} + + +static rsRetVal ATTR_NONNULL() +splitOversizeMessage(smsg_t *const pMsg) +{ + DEFiRet; + const char *rawmsg; + int nsegments; + int len_rawmsg; + const int maxlen = glblGetMaxLine(runConf); + ISOBJ_TYPE_assert(pMsg, msg); + + getRawMsg(pMsg, (uchar**) &rawmsg, &len_rawmsg); + nsegments = len_rawmsg / maxlen; + const int len_last_segment = len_rawmsg % maxlen; + DBGPRINTF("splitting oversize message, size %d, segment size %d, " + "nsegments %d, bytes in last fragment %d\n", + len_rawmsg, maxlen, nsegments, len_last_segment); + + smsg_t *pMsg_seg; + + /* process full segments */ + for(int i = 0 ; i < nsegments ; ++i) { + CHKmalloc(pMsg_seg = MsgDup(pMsg)); + MsgSetRawMsg(pMsg_seg, rawmsg + (i * maxlen), maxlen); + submitMsg2(pMsg_seg); + } + + /* if necessary, write partial last segment */ + if(len_last_segment != 0) { + CHKmalloc(pMsg_seg = MsgDup(pMsg)); + MsgSetRawMsg(pMsg_seg, rawmsg + (nsegments * maxlen), len_last_segment); + submitMsg2(pMsg_seg); + } + +finalize_it: + RETiRet; +} + + +/* submit a message to the main message queue. This is primarily + * a hook to prevent the need for callers to know about the main message queue + * rgerhards, 2008-02-13 + */ +rsRetVal +submitMsg2(smsg_t *pMsg) +{ + qqueue_t *pQueue; + ruleset_t *pRuleset; + DEFiRet; + + ISOBJ_TYPE_assert(pMsg, msg); + + if(getRawMsgLen(pMsg) > glblGetMaxLine(runConf)){ + uchar *rawmsg; + int dummy; + getRawMsg(pMsg, &rawmsg, &dummy); + if(glblReportOversizeMessage(runConf)) { + LogMsg(0, RS_RET_OVERSIZE_MSG, LOG_WARNING, + "message too long (%d) with configured size %d, begin of " + "message is: %.80s", + getRawMsgLen(pMsg), glblGetMaxLine(runConf), rawmsg); + } + writeOversizeMessageLog(pMsg); + if(glblGetOversizeMsgInputMode(runConf) == glblOversizeMsgInputMode_Split) { + splitOversizeMessage(pMsg); + /* we have submitted the message segments recursively, so we + * can just deleted the original msg object and terminate. + */ + msgDestruct(&pMsg); + FINALIZE; + } else if(glblGetOversizeMsgInputMode(runConf) == glblOversizeMsgInputMode_Truncate) { + MsgTruncateToMaxSize(pMsg); + } else { + /* in "accept" mode, we do nothing, simply because "accept" means + * to use as-is. + */ + assert(glblGetOversizeMsgInputMode(runConf) == glblOversizeMsgInputMode_Accept); + } + } + + pRuleset = MsgGetRuleset(pMsg); + pQueue = (pRuleset == NULL) ? runConf->pMsgQueue : ruleset.GetRulesetQueue(pRuleset); + + /* if a plugin logs a message during shutdown, the queue may no longer exist */ + if(pQueue == NULL) { + DBGPRINTF("submitMsg2() could not submit message - " + "queue does (no longer?) exist - ignored\n"); + FINALIZE; + } + + qqueueEnqMsg(pQueue, pMsg->flowCtlType, pMsg); + +finalize_it: + RETiRet; +} + +/* submit multiple messages at once, very similar to submitMsg, just + * for multi_submit_t. All messages need to go into the SAME queue! + * rgerhards, 2009-06-16 + */ +rsRetVal ATTR_NONNULL() +multiSubmitMsg2(multi_submit_t *const pMultiSub) +{ + qqueue_t *pQueue; + ruleset_t *pRuleset; + DEFiRet; + + if(pMultiSub->nElem == 0) + FINALIZE; + + pRuleset = MsgGetRuleset(pMultiSub->ppMsgs[0]); + pQueue = (pRuleset == NULL) ? runConf->pMsgQueue : ruleset.GetRulesetQueue(pRuleset); + + /* if a plugin logs a message during shutdown, the queue may no longer exist */ + if(pQueue == NULL) { + DBGPRINTF("multiSubmitMsg() could not submit message - " + "queue does (no longer?) exist - ignored\n"); + FINALIZE; + } + + iRet = pQueue->MultiEnq(pQueue, pMultiSub); + pMultiSub->nElem = 0; + +finalize_it: + RETiRet; +} +rsRetVal +multiSubmitMsg(multi_submit_t *pMultiSub) /* backward compat. level */ +{ + return multiSubmitMsg2(pMultiSub); +} + + +/* flush multiSubmit, e.g. at end of read records */ +rsRetVal +multiSubmitFlush(multi_submit_t *pMultiSub) +{ + DEFiRet; + if(pMultiSub->nElem > 0) { + iRet = multiSubmitMsg2(pMultiSub); + } + RETiRet; +} + + +/* some support for command line option parsing. Any non-trivial options must be + * buffered until the complete command line has been parsed. This is necessary to + * prevent dependencies between the options. That, in turn, means we need to have + * something that is capable of buffering options and there values. The follwing + * functions handle that. + * rgerhards, 2008-04-04 + */ +typedef struct bufOpt { + struct bufOpt *pNext; + char optchar; + char *arg; +} bufOpt_t; +static bufOpt_t *bufOptRoot = NULL; +static bufOpt_t *bufOptLast = NULL; + +/* add option buffer */ +static rsRetVal +bufOptAdd(char opt, char *arg) +{ + DEFiRet; + bufOpt_t *pBuf; + + if((pBuf = malloc(sizeof(bufOpt_t))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + pBuf->optchar = opt; + pBuf->arg = arg; + pBuf->pNext = NULL; + + if(bufOptLast == NULL) { + bufOptRoot = pBuf; /* then there is also no root! */ + } else { + bufOptLast->pNext = pBuf; + } + bufOptLast = pBuf; + +finalize_it: + RETiRet; +} + + +/* remove option buffer from top of list, return values and destruct buffer itself. + * returns RS_RET_END_OF_LINKEDLIST when no more options are present. + * (we use int *opt instead of char *opt to keep consistent with getopt()) + */ +static rsRetVal +bufOptRemove(int *opt, char **arg) +{ + DEFiRet; + bufOpt_t *pBuf; + + if(bufOptRoot == NULL) + ABORT_FINALIZE(RS_RET_END_OF_LINKEDLIST); + pBuf = bufOptRoot; + + *opt = pBuf->optchar; + *arg = pBuf->arg; + + bufOptRoot = pBuf->pNext; + free(pBuf); + +finalize_it: + RETiRet; +} + + +static void +hdlr_sigttin_ou(void) +{ + /* this is just a dummy to care for our sigttin input + * module cancel interface and sigttou internal message + * notificaton/mainloop wakeup mechanism. The important + * point is that it actually does *NOTHING*. + */ +} + +static void +hdlr_enable(int sig, void (*hdlr)()) +{ + struct sigaction sigAct; + memset(&sigAct, 0, sizeof (sigAct)); + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = hdlr; + sigaction(sig, &sigAct, NULL); +} + +static void +hdlr_sighup(void) +{ + pthread_mutex_lock(&mutHadHUP); + bHadHUP = 1; + pthread_mutex_unlock(&mutHadHUP); + /* at least on FreeBSD we seem not to necessarily awake the main thread. + * So let's do it explicitely. + */ + dbgprintf("awaking mainthread on HUP\n"); + pthread_kill(mainthread, SIGTTIN); +} + +static void +hdlr_sigchld(void) +{ + pthread_mutex_lock(&mutChildDied); + bChildDied = 1; + pthread_mutex_unlock(&mutChildDied); +} + +static void +rsyslogdDebugSwitch(void) +{ + time_t tTime; + struct tm tp; + + datetime.GetTime(&tTime); + localtime_r(&tTime, &tp); + if(debugging_on == 0) { + debugging_on = 1; + dbgprintf("\n"); + dbgprintf("\n"); + dbgprintf("********************************************************************************\n"); + dbgprintf("Switching debugging_on to true at %2.2d:%2.2d:%2.2d\n", + tp.tm_hour, tp.tm_min, tp.tm_sec); + dbgprintf("********************************************************************************\n"); + } else { + dbgprintf("********************************************************************************\n"); + dbgprintf("Switching debugging_on to false at %2.2d:%2.2d:%2.2d\n", + tp.tm_hour, tp.tm_min, tp.tm_sec); + dbgprintf("********************************************************************************\n"); + dbgprintf("\n"); + dbgprintf("\n"); + debugging_on = 0; + } +} + + +/* This is the main entry point into rsyslogd. Over time, we should try to + * modularize it a bit more... + * + * NOTE on stderr and stdout: they are kept open during a fork. Note that this + * may introduce subtle security issues: if we are in a jail, one may break out of + * it via these descriptors. But if I close them earlier, error messages will (once + * again) not be emitted to the user that starts the daemon. Given that the risk + * of a break-in is very low in the startup phase, we decide it is more important + * to emit error messages. + */ +static void +initAll(int argc, char **argv) +{ + rsRetVal localRet; + int ch; + int iHelperUOpt; + int bChDirRoot = 1; /* change the current working directory to "/"? */ + char *arg; /* for command line option processing */ + char cwdbuf[128]; /* buffer to obtain/display current working directory */ + int parentPipeFD = 0; /* fd of pipe to parent, if auto-backgrounding */ + DEFiRet; + + /* prepare internal signaling */ + hdlr_enable(SIGTTIN, hdlr_sigttin_ou); + hdlr_enable(SIGTTOU, hdlr_sigttin_ou); + + /* first, parse the command line options. We do not carry out any actual work, just + * see what we should do. This relieves us from certain anomalies and we can process + * the parameters down below in the correct order. For example, we must know the + * value of -M before we can do the init, but at the same time we need to have + * the base classes init before we can process most of the options. Now, with the + * split of functionality, this is no longer a problem. Thanks to varmofekoj for + * suggesting this algo. + * Note: where we just need to set some flags and can do so without knowledge + * of other options, we do this during the inital option processing. + * rgerhards, 2008-04-04 + */ +#if defined(_AIX) + while((ch = getopt(argc, argv, "46ACDdf:hi:M:nN:o:qQS:T:u:vwxR")) != EOF) { +#else + while((ch = getopt(argc, argv, "46ACDdf:hi:M:nN:o:qQS:T:u:vwx")) != EOF) { +#endif + switch((char)ch) { + case '4': + case '6': + case 'A': + case 'f': /* configuration file */ + case 'i': /* pid file name */ + case 'n': /* don't fork */ + case 'N': /* enable config verify mode */ + case 'q': /* add hostname if DNS resolving has failed */ + case 'Q': /* dont resolve hostnames in ACL to IPs */ + case 'S': /* Source IP for local client to be used on multihomed host */ + case 'T': /* chroot on startup (primarily for testing) */ + case 'u': /* misc user settings */ + case 'w': /* disable disallowed host warnings */ + case 'C': + case 'o': /* write output config file */ + case 'x': /* disable dns for remote messages */ + CHKiRet(bufOptAdd(ch, optarg)); + break; +#if defined(_AIX) + case 'R': /* This option is a no-op for AIX */ + break; +#endif + case 'd': /* debug - must be handled now, so that debug is active during init! */ + debugging_on = 1; + Debug = 1; + yydebug = 1; + break; + case 'D': /* BISON debug */ + yydebug = 1; + break; + case 'M': /* default module load path -- this MUST be carried out immediately! */ + glblModPath = (uchar*) optarg; + break; + case 'v': /* MUST be carried out immediately! */ + printVersion(); + exit(0); /* exit for -v option - so this is a "good one" */ + case 'h': + case '?': + default: + rsyslogd_usage(); + } + } + + if(argc - optind) + rsyslogd_usage(); + + DBGPRINTF("rsyslogd %s startup, module path '%s', cwd:%s\n", + VERSION, glblModPath == NULL ? "" : (char*)glblModPath, + getcwd(cwdbuf, sizeof(cwdbuf))); + + /* we are done with the initial option parsing and processing. Now we init the system. */ + + CHKiRet(rsyslogd_InitGlobalClasses()); + + /* doing some core initializations */ + + if((iRet = modInitIminternal()) != RS_RET_OK) { + fprintf(stderr, "fatal error: could not initialize errbuf object (error code %d).\n", + iRet); + exit(1); /* "good" exit, leaving at init for fatal error */ + } + + /* we now can emit error messages "the regular way" */ + + if(getenv("TZ") == NULL) { + const char *const tz = + (access("/etc/localtime", R_OK) == 0) ? "TZ=/etc/localtime" : "TZ=UTC"; + putenv((char*)tz); + if(emitTZWarning) { + LogMsg(0, RS_RET_NO_TZ_SET, LOG_WARNING, "environment variable TZ is not " + "set, auto correcting this to %s", tz); + } else { + dbgprintf("environment variable TZ is not set, auto correcting this to %s\n", tz); + } + } + + /* END core initializations - we now come back to carrying out command line options*/ + + while((iRet = bufOptRemove(&ch, &arg)) == RS_RET_OK) { + DBGPRINTF("deque option %c, optarg '%s'\n", ch, (arg == NULL) ? "" : arg); + switch((char)ch) { + case '4': + fprintf (stderr, "rsyslogd: the -4 command line option has gone away.\n" + "Please use the global(net.ipprotocol=\"ipv4-only\") " + "configuration parameter instead.\n"); + break; + case '6': + fprintf (stderr, "rsyslogd: the -6 command line option will has gone away.\n" + "Please use the global(net.ipprotocol=\"ipv6-only\") " + "configuration parameter instead.\n"); + break; + case 'A': + fprintf (stderr, "rsyslogd: the -A command line option will go away " + "soon.\n" + "Please use the omfwd parameter \"upd.sendToAll\" instead.\n"); + send_to_all++; + break; + case 'S': /* Source IP for local client to be used on multihomed host */ + fprintf (stderr, "rsyslogd: the -S command line option will go away " + "soon.\n" + "Please use the omrelp parameter \"localClientIP\" instead.\n"); + if(glbl.GetSourceIPofLocalClient() != NULL) { + fprintf (stderr, "rsyslogd: Only one -S argument allowed, the first one is taken.\n"); + } else { + glbl.SetSourceIPofLocalClient((uchar*)arg); + } + break; + case 'f': /* configuration file */ + ConfFile = (uchar*) arg; + break; + case 'i': /* pid file name */ + free((void*)PidFile); + PidFile = arg; + break; + case 'n': /* don't fork */ + doFork = 0; + break; + case 'N': /* enable config verify mode */ + iConfigVerify = (arg == NULL) ? 0 : atoi(arg); + break; + case 'o': + if(fp_rs_full_conf_output != NULL) { + fprintf(stderr, "warning: -o option given multiple times. Now " + "using value %s\n", (arg == NULL) ? "-" : arg); + fclose(fp_rs_full_conf_output); + fp_rs_full_conf_output = NULL; + } + if(arg == NULL || !strcmp(arg, "-")) { + fp_rs_full_conf_output = stdout; + } else { + fp_rs_full_conf_output = fopen(arg, "w"); + } + if(fp_rs_full_conf_output == NULL) { + perror(arg); + fprintf (stderr, "rsyslogd: cannot open config output file %s - " + "-o option will be ignored\n", arg); + } else { + time_t tTime; + struct tm tp; + datetime.GetTime(&tTime); + localtime_r(&tTime, &tp); + fprintf(fp_rs_full_conf_output, + "## full conf created by rsyslog version %s at " + "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d ##\n", + VERSION, tp.tm_year + 1900, tp.tm_mon + 1, tp.tm_mday, + tp.tm_hour, tp.tm_min, tp.tm_sec); + } + break; + case 'q': /* add hostname if DNS resolving has failed */ + fprintf (stderr, "rsyslogd: the -q command line option has gone away.\n" + "Please use the global(net.aclAddHostnameOnFail=\"on\") " + "configuration parameter instead.\n"); + break; + case 'Q': /* dont resolve hostnames in ACL to IPs */ + fprintf (stderr, "rsyslogd: the -Q command line option has gone away.\n" + "Please use the global(net.aclResolveHostname=\"off\") " + "configuration parameter instead.\n"); + break; + case 'T':/* chroot() immediately at program startup, but only for testing, NOT security yet */ + if(arg == NULL) { + /* note this case should already be handled by getopt, + * but we want to keep the static analyzer happy. + */ + fprintf(stderr, "-T options needs a parameter\n"); + exit(1); + } + if(chroot(arg) != 0) { + perror("chroot"); + exit(1); + } + if(chdir("/") != 0) { + perror("chdir"); + exit(1); + } + break; + case 'u': /* misc user settings */ + iHelperUOpt = (arg == NULL) ? 0 : atoi(arg); + if(iHelperUOpt & 0x01) { + fprintf (stderr, "rsyslogd: the -u command line option has gone away.\n" + "For the 0x01 bit, please use the " + "global(parser.parseHostnameAndTag=\"off\") " + "configuration parameter instead.\n"); + } + if(iHelperUOpt & 0x02) { + fprintf (stderr, "rsyslogd: the -u command line option will go away " + "soon.\n" + "For the 0x02 bit, please use the -C option instead."); + bChDirRoot = 0; + } + break; + case 'C': + bChDirRoot = 0; + break; + case 'w': /* disable disallowed host warnigs */ + fprintf (stderr, "rsyslogd: the -w command line option has gone away.\n" + "Please use the global(net.permitWarning=\"off\") " + "configuration parameter instead.\n"); + break; + case 'x': /* disable dns for remote messages */ + fprintf (stderr, "rsyslogd: the -x command line option has gone away.\n" + "Please use the global(net.enableDNS=\"off\") " + "configuration parameter instead.\n"); + break; + case 'h': + case '?': + default: + rsyslogd_usage(); + } + } + + if(iRet != RS_RET_END_OF_LINKEDLIST) + FINALIZE; + + if(iConfigVerify) { + doFork = 0; + fprintf(stderr, "rsyslogd: version %s, config validation run (level %d), master config %s\n", + VERSION, iConfigVerify, ConfFile); + } + + resetErrMsgsFlag(); + localRet = rsconf.Load(&ourConf, ConfFile); + +#ifdef ENABLE_LIBCAPNG + if (loadConf->globals.bCapabilityDropEnabled) { + /* + * Drop capabilities to the necessary set + */ + int capng_rc, capng_failed = 0; + typedef struct capabilities_s { + int capability; /* capability code */ + const char *name; /* name of the capability to be displayed */ + /* is the capability present that is needed by rsyslog? if so we do not drop it */ + sbool present; + capng_type_t type; + } capabilities_t; + + capabilities_t capabilities[] = { + #define CAP_FIELD(code, type) { code, #code, 0 , type} + CAP_FIELD(CAP_BLOCK_SUSPEND, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_NET_RAW, CAPNG_EFFECTIVE | CAPNG_PERMITTED ), + CAP_FIELD(CAP_CHOWN, CAPNG_EFFECTIVE | CAPNG_PERMITTED ), + CAP_FIELD(CAP_IPC_LOCK, CAPNG_EFFECTIVE | CAPNG_PERMITTED ), + CAP_FIELD(CAP_LEASE, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_NET_ADMIN, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_NET_BIND_SERVICE, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_DAC_OVERRIDE, CAPNG_EFFECTIVE | CAPNG_PERMITTED | CAPNG_BOUNDING_SET), + CAP_FIELD(CAP_SETGID, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_SETUID, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_SYS_ADMIN, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_SYS_CHROOT, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_SYS_RESOURCE, CAPNG_EFFECTIVE | CAPNG_PERMITTED), + CAP_FIELD(CAP_SYSLOG, CAPNG_EFFECTIVE | CAPNG_PERMITTED) + #undef CAP_FIELD + }; + + if (capng_have_capabilities(CAPNG_SELECT_CAPS) > CAPNG_NONE) { + /* Examine which capabilities are available to us, so we do not try to + drop something that is not present. We need to do this in two steps, + because capng_clear clears the capability set. In the second step, + we add back those caps, which were present before clearing the selected + posix capabilities set. + */ + unsigned long caps_len = sizeof(capabilities) / sizeof(capabilities_t); + for (unsigned long i = 0; i < caps_len; i++) { + if (capng_have_capability(CAPNG_EFFECTIVE, capabilities[i].capability)) { + capabilities[i].present = 1; + } + } + + capng_clear(CAPNG_SELECT_BOTH); + + for (unsigned long i = 0; i < caps_len; i++) { + if (capabilities[i].present) { + DBGPRINTF("The %s capability is present, " + "will try to preserve it.\n", capabilities[i].name); + if ((capng_rc = capng_update(CAPNG_ADD, capabilities[i].type, + capabilities[i].capability)) != 0) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not update the internal posix capabilities" + " settings based on the options passed to it," + " capng_update=%d", capng_rc); + capng_failed = 1; + } + } else { + DBGPRINTF("The %s capability is not present, " + "will not try to preserve it.\n", capabilities[i].name); + } + } + + if ((capng_rc = capng_apply(CAPNG_SELECT_BOTH)) != 0) { + LogError(0, RS_RET_LIBCAPNG_ERR, + "could not transfer the specified internal posix capabilities " + "settings to the kernel, capng_apply=%d", capng_rc); + capng_failed = 1; + } + + if (capng_failed) { + DBGPRINTF("Capabilities were not dropped successfully.\n"); + if (loadConf->globals.bAbortOnFailedLibcapngSetup) { + ABORT_FINALIZE(RS_RET_LIBCAPNG_ERR); + } + } else { + DBGPRINTF("Capabilities were dropped successfully\n"); + } + } else { + DBGPRINTF("No capabilities to drop\n"); + } + } +#endif + + if(fp_rs_full_conf_output != NULL) { + if(fp_rs_full_conf_output != stdout) { + fclose(fp_rs_full_conf_output); + } + fp_rs_full_conf_output = NULL; + } + + /* check for "hard" errors that needs us to abort in any case */ + if( (localRet == RS_RET_CONF_FILE_NOT_FOUND) + || (localRet == RS_RET_NO_ACTIONS) ) { + /* for extreme testing, we keep the ability to let rsyslog continue + * even on hard config errors. Note that this may lead to segfaults + * or other malfunction further down the road. + */ + if((loadConf->globals.glblDevOptions & DEV_OPTION_KEEP_RUNNING_ON_HARD_CONF_ERROR) == 1) { + fprintf(stderr, "rsyslogd: NOTE: developer-only option set to keep rsyslog " + "running where it should abort - this can lead to " + "more problems later in the run.\n"); + } else { + ABORT_FINALIZE(localRet); + } + } + + glbl.GenerateLocalHostNameProperty(); + + if(hadErrMsgs()) { + if(loadConf->globals.bAbortOnUncleanConfig) { + fprintf(stderr, "rsyslogd: global(AbortOnUncleanConfig=\"on\") is set, and " + "config is not clean.\n" + "Check error log for details, fix errors and restart. As a last\n" + "resort, you may want to use global(AbortOnUncleanConfig=\"off\") \n" + "to permit a startup with a dirty config.\n"); + exit(2); + } + if(iConfigVerify) { + /* a bit dirty, but useful... */ + exit(1); + } + localRet = RS_RET_OK; + } + CHKiRet(localRet); + + CHKiRet(rsyslogd_InitStdRatelimiters()); + + if(bChDirRoot) { + if(chdir("/") != 0) + fprintf(stderr, "Can not do 'cd /' - still trying to run\n"); + } + + if(iConfigVerify) + FINALIZE; + /* after this point, we are in a "real" startup */ + + thrdInit(); + CHKiRet(checkStartupOK()); + if(doFork) { + parentPipeFD = forkRsyslog(); + } + glblSetOurPid(getpid()); + + hdlr_enable(SIGPIPE, SIG_IGN); + hdlr_enable(SIGXFSZ, SIG_IGN); + if(Debug || loadConf->globals.permitCtlC) { + hdlr_enable(SIGUSR1, rsyslogdDebugSwitch); + hdlr_enable(SIGINT, rsyslogdDoDie); + hdlr_enable(SIGQUIT, rsyslogdDoDie); + } else { + hdlr_enable(SIGUSR1, SIG_IGN); + hdlr_enable(SIGINT, SIG_IGN); + hdlr_enable(SIGQUIT, SIG_IGN); + } + hdlr_enable(SIGTERM, rsyslogdDoDie); + hdlr_enable(SIGCHLD, hdlr_sigchld); + hdlr_enable(SIGHUP, hdlr_sighup); + + if(rsconfNeedDropPriv(loadConf)) { + /* need to write pid file early as we may loose permissions */ + CHKiRet(writePidFile()); + } + + CHKiRet(rsconf.Activate(ourConf)); + + if(runConf->globals.bLogStatusMsgs) { + char bufStartUpMsg[512]; + snprintf(bufStartUpMsg, sizeof(bufStartUpMsg), + "[origin software=\"rsyslogd\" " "swVersion=\"" VERSION \ + "\" x-pid=\"%d\" x-info=\"https://www.rsyslog.com\"] start", + (int) glblGetOurPid()); + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)bufStartUpMsg, 0); + } + + if(!rsconfNeedDropPriv(runConf)) { + CHKiRet(writePidFile()); + } + + /* END OF INTIALIZATION */ + DBGPRINTF("rsyslogd: initialization completed, transitioning to regular run mode\n"); + + if(doFork) { + tellChildReady(parentPipeFD, "OK"); + stddbg = -1; /* turn off writing to fd 1 */ + close(1); + close(2); + runConf->globals.bErrMsgToStderr = 0; + } + +finalize_it: + if(iRet == RS_RET_VALIDATION_RUN) { + fprintf(stderr, "rsyslogd: End of config validation run. Bye.\n"); + exit(0); + } else if(iRet != RS_RET_OK) { + fprintf(stderr, "rsyslogd: run failed with error %d (see rsyslog.h " + "or try https://www.rsyslog.com/e/%d to learn what that number means)\n", + iRet, iRet*-1); + exit(1); + } + +} + + +/* this function pulls all internal messages from the buffer + * and puts them into the processing engine. + * We can only do limited error handling, as this would not + * really help us. TODO: add error messages? + * rgerhards, 2007-08-03 + */ +void +processImInternal(void) +{ + smsg_t *pMsg; + smsg_t *repMsg; + + while(iminternalRemoveMsg(&pMsg) == RS_RET_OK) { + rsRetVal localRet = ratelimitMsg(internalMsg_ratelimiter, pMsg, &repMsg); + if(repMsg != NULL) { + logmsgInternal_doWrite(repMsg); + } + if(localRet == RS_RET_OK) { + logmsgInternal_doWrite(pMsg); + } + } +} + + +/* This takes a received message that must be decoded and submits it to + * the main message queue. This is a legacy function which is being provided + * to aid older input plugins that do not support message creation via + * the new interfaces themselves. It is not recommended to use this + * function for new plugins. -- rgerhards, 2009-10-12 + */ +rsRetVal +parseAndSubmitMessage(const uchar *const hname, const uchar *const hnameIP, const uchar *const msg, + const int len, const int flags, const flowControl_t flowCtlType, + prop_t *const pInputName, + const struct syslogTime *const stTime, + const time_t ttGenTime, + ruleset_t *const pRuleset) +{ + prop_t *pProp = NULL; + smsg_t *pMsg = NULL; + DEFiRet; + + /* we now create our own message object and submit it to the queue */ + if(stTime == NULL) { + CHKiRet(msgConstruct(&pMsg)); + } else { + CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime)); + } + if(pInputName != NULL) + MsgSetInputName(pMsg, pInputName); + MsgSetRawMsg(pMsg, (char*)msg, len); + MsgSetFlowControlType(pMsg, flowCtlType); + MsgSetRuleset(pMsg, pRuleset); + pMsg->msgFlags = flags | NEEDS_PARSING; + + MsgSetRcvFromStr(pMsg, hname, ustrlen(hname), &pProp); + CHKiRet(prop.Destruct(&pProp)); + CHKiRet(MsgSetRcvFromIPStr(pMsg, hnameIP, ustrlen(hnameIP), &pProp)); + CHKiRet(prop.Destruct(&pProp)); + CHKiRet(submitMsg2(pMsg)); + +finalize_it: + if(iRet != RS_RET_OK) { + DBGPRINTF("parseAndSubmitMessage() error, discarding msg: %s\n", msg); + if(pMsg != NULL) { + msgDestruct(&pMsg); + } + } + RETiRet; +} + + +/* helper to doHUP(), this "HUPs" each action. The necessary locking + * is done inside the action class and nothing we need to take care of. + * rgerhards, 2008-10-22 + */ +DEFFUNC_llExecFunc(doHUPActions) +{ + actionCallHUPHdlr((action_t*) pData); + return RS_RET_OK; /* we ignore errors, we can not do anything either way */ +} + + +/* This function processes a HUP after one has been detected. Note that this + * is *NOT* the sighup handler. The signal is recorded by the handler, that record + * detected inside the mainloop and then this function is called to do the + * real work. -- rgerhards, 2008-10-22 + * Note: there is a VERY slim chance of a data race when the hostname is reset. + * We prefer to take this risk rather than sync all accesses, because to the best + * of my analysis it can not really hurt (the actual property is reference-counted) + * but the sync would require some extra CPU for *each* message processed. + * rgerhards, 2012-04-11 + */ +static void +doHUP(void) +{ + char buf[512]; + + DBGPRINTF("doHUP: doing modules\n"); + if(ourConf->globals.bLogStatusMsgs) { + snprintf(buf, sizeof(buf), + "[origin software=\"rsyslogd\" " "swVersion=\"" VERSION + "\" x-pid=\"%d\" x-info=\"https://www.rsyslog.com\"] rsyslogd was HUPed", + (int) glblGetOurPid()); + errno = 0; + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0); + } + + queryLocalHostname(runConf); /* re-read our name */ + ruleset.IterateAllActions(ourConf, doHUPActions, NULL); + DBGPRINTF("doHUP: doing modules\n"); + modDoHUP(); + DBGPRINTF("doHUP: doing lookup tables\n"); + lookupDoHUP(); + DBGPRINTF("doHUP: doing errmsgs\n"); + errmsgDoHUP(); +} + +/* rsyslogdDoDie() is a signal handler. If called, it sets the bFinished variable + * to indicate the program should terminate. However, it does not terminate + * it itself, because that causes issues with multi-threading. The actual + * termination is then done on the main thread. This solution might introduce + * a minimal delay, but it is much cleaner than the approach of doing everything + * inside the signal handler. + * rgerhards, 2005-10-26 + * Note: + * - we do not call DBGPRINTF() as this may cause us to block in case something + * with the threading is wrong. + * - we do not really care about the return state of write(), but we need this + * strange check we do to silence compiler warnings (thanks, Ubuntu!) + */ +void +rsyslogdDoDie(int sig) +{ +# define MSG1 "DoDie called.\n" +# define MSG2 "DoDie called 5 times - unconditional exit\n" + static int iRetries = 0; /* debug aid */ + dbgprintf(MSG1); + if(Debug == DEBUG_FULL) { + if(write(1, MSG1, sizeof(MSG1) - 1) == -1) { + dbgprintf("%s:%d: write failed\n", __FILE__, __LINE__); + } + } + if(iRetries++ == 4) { + if(Debug == DEBUG_FULL) { + if(write(1, MSG2, sizeof(MSG2) - 1) == -1) { + dbgprintf("%s:%d: write failed\n", __FILE__, __LINE__); + } + } + abort(); + } + bFinished = sig; + if(runConf->globals.debugOnShutdown) { + /* kind of hackish - set to 0, so that debug_swith will enable + * and AND emit the "start debug log" message. + */ + debugging_on = 0; + rsyslogdDebugSwitch(); + } +# undef MSG1 +# undef MSG2 + /* at least on FreeBSD we seem not to necessarily awake the main thread. + * So let's do it explicitely. + */ + dbgprintf("awaking mainthread\n"); + pthread_kill(mainthread, SIGTTIN); +} + + +static void +wait_timeout(const sigset_t *sigmask) +{ + struct timespec tvSelectTimeout; + + tvSelectTimeout.tv_sec = runConf->globals.janitorInterval * 60; /* interval is in minutes! */ + tvSelectTimeout.tv_nsec = 0; + +#ifdef _AIX + if(!src_exists) { + /* it looks like select() is NOT interrupted by HUP, even though + * SA_RESTART is not given in the signal setup. As this code is + * not expected to be used in production (when running as a + * service under src control), we simply make a kind of + * "somewhat-busy-wait" algorithm. We compute our own + * timeout value, which we count down to zero. We do this + * in useful subsecond steps. + */ + const long wait_period = 500000000; /* wait period in nanoseconds */ + int timeout = runConf->globals.janitorInterval * 60 * (1000000000 / wait_period); + + tvSelectTimeout.tv_sec = 0; + tvSelectTimeout.tv_nsec = wait_period; + do { + pthread_mutex_lock(&mutHadHUP); + if(bFinished || bHadHUP) { + pthread_mutex_unlock(&mutHadHUP); + break; + } + pthread_mutex_unlock(&mutHadHUP); + pselect(1, NULL, NULL, NULL, &tvSelectTimeout, sigmask); + } while(--timeout > 0); + } else { + char buf[256]; + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(SRC_FD, &rfds); + if(pselect(SRC_FD + 1, (fd_set *)&rfds, NULL, NULL, &tvSelectTimeout, sigmask)) + { + if(FD_ISSET(SRC_FD, &rfds)) + { + rc = recvfrom(SRC_FD, &srcpacket, SRCMSG, 0, &srcaddr, &addrsz); + if(rc < 0) { + if (errno != EINTR) + { + fprintf(stderr,"%s: ERROR: '%d' recvfrom\n", progname,errno); + exit(1); //TODO: this needs to be handled gracefully + } else { /* punt on short read */ + return; + } + + switch(srcpacket.subreq.action) + { + case START: + dosrcpacket(SRC_SUBMSG,"ERROR: rsyslogd does not support this " + "option.\n", sizeof(struct srcrep)); + break; + case STOP: + if (srcpacket.subreq.object == SUBSYSTEM) { + dosrcpacket(SRC_OK,NULL,sizeof(struct srcrep)); + (void) snprintf(buf, sizeof(buf) / sizeof(char), " [origin " + "software=\"rsyslogd\" " "swVersion=\"" VERSION \ + "\" x-pid=\"%d\" x-info=\"https://www.rsyslog.com\"]" + " exiting due to stopsrc.", + (int) glblGetOurPid()); + errno = 0; + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0); + return ; + } else + dosrcpacket(SRC_SUBMSG,"ERROR: rsyslogd does not support " + "this option.\n",sizeof(struct srcrep)); + break; + case REFRESH: + dosrcpacket(SRC_SUBMSG,"ERROR: rsyslogd does not support this " + "option.\n", sizeof(struct srcrep)); + break; + default: + dosrcpacket(SRC_SUBICMD,NULL,sizeof(struct srcrep)); + break; + + } + } + } + } + } +#else + pselect(0, NULL, NULL, NULL, &tvSelectTimeout, sigmask); +#endif /* AIXPORT : SRC end */ +} + + +static void +reapChild(void) +{ + pid_t child; + do { + int status; + child = waitpid(-1, &status, WNOHANG); + if(child != -1 && child != 0) { + glblReportChildProcessExit(runConf, NULL, child, status); + } + } while(child > 0); +} + + +/* This is the main processing loop. It is called after successful initialization. + * When it returns, the syslogd terminates. + * Its sole function is to provide some housekeeping things. The real work is done + * by the other threads spawned. + */ +static void +mainloop(void) +{ + time_t tTime; + sigset_t origmask; + sigset_t sigblockset; + int need_free_mutex; + + sigemptyset(&sigblockset); + sigaddset(&sigblockset, SIGTERM); + sigaddset(&sigblockset, SIGCHLD); + sigaddset(&sigblockset, SIGHUP); + + do { + pthread_sigmask(SIG_BLOCK, &sigblockset, &origmask); + pthread_mutex_lock(&mutChildDied); + need_free_mutex = 1; + if(bChildDied) { + bChildDied = 0; + pthread_mutex_unlock(&mutChildDied); + need_free_mutex = 0; + reapChild(); + } + if(need_free_mutex) { + pthread_mutex_unlock(&mutChildDied); + } + + pthread_mutex_lock(&mutHadHUP); + need_free_mutex = 1; + if(bHadHUP) { + need_free_mutex = 0; + pthread_mutex_unlock(&mutHadHUP); + doHUP(); + pthread_mutex_lock(&mutHadHUP); + bHadHUP = 0; + pthread_mutex_unlock(&mutHadHUP); + } + if(need_free_mutex) { + pthread_mutex_unlock(&mutHadHUP); + } + + processImInternal(); + + if(bFinished) + break; /* exit as quickly as possible */ + + wait_timeout(&origmask); + pthread_sigmask(SIG_UNBLOCK, &sigblockset, NULL); + + janitorRun(); + + datetime.GetTime(&tTime); + checkGoneAwaySenders(tTime); + + } while(!bFinished); /* end do ... while() */ +} + +/* Finalize and destruct all actions. + */ +static void +rsyslogd_destructAllActions(void) +{ + ruleset.DestructAllActions(runConf); + PREFER_STORE_0_TO_INT(&bHaveMainQueue); /* flag that internal messages need to be temporarily stored */ +} + + +/* de-initialize everything, make ready for termination */ +static void +deinitAll(void) +{ + char buf[256]; + + DBGPRINTF("exiting on signal %d\n", bFinished); + + /* IMPORTANT: we should close the inputs first, and THEN send our termination + * message. If we do it the other way around, logmsgInternal() may block on + * a full queue and the inputs still fill up that queue. Depending on the + * scheduling order, we may end up with logmsgInternal being held for a quite + * long time. When the inputs are terminated first, that should not happen + * because the queue is drained in parallel. The situation could only become + * an issue with extremely long running actions in a queue full environment. + * However, such actions are at least considered poorly written, if not + * outright wrong. So we do not care about this very remote problem. + * rgerhards, 2008-01-11 + */ + + /* close the inputs */ + DBGPRINTF("Terminating input threads...\n"); + glbl.SetGlobalInputTermination(); + + thrdTerminateAll(); + + /* and THEN send the termination log message (see long comment above) */ + if(bFinished && runConf->globals.bLogStatusMsgs) { + (void) snprintf(buf, sizeof(buf), + "[origin software=\"rsyslogd\" " "swVersion=\"" VERSION \ + "\" x-pid=\"%d\" x-info=\"https://www.rsyslog.com\"]" " exiting on signal %d.", + (int) glblGetOurPid(), bFinished); + errno = 0; + logmsgInternal(NO_ERRCODE, LOG_SYSLOG|LOG_INFO, (uchar*)buf, 0); + } + processImInternal(); /* make sure not-yet written internal messages are processed */ + /* we sleep a couple of ms to give the queue a chance to pick up the late messages + * (including exit message); otherwise we have seen cases where the message did + * not make it to log files, even on idle systems. + */ + srSleep(0, 50); + + /* drain queue (if configured so) and stop main queue worker thread pool */ + DBGPRINTF("Terminating main queue...\n"); + qqueueDestruct(&runConf->pMsgQueue); + runConf->pMsgQueue = NULL; + + /* Free ressources and close connections. This includes flushing any remaining + * repeated msgs. + */ + DBGPRINTF("Terminating outputs...\n"); + rsyslogd_destructAllActions(); + + DBGPRINTF("all primary multi-thread sources have been terminated - now doing aux cleanup...\n"); + + DBGPRINTF("destructing current config...\n"); + rsconf.Destruct(&runConf); + + modExitIminternal(); + + if(pInternalInputName != NULL) + prop.Destruct(&pInternalInputName); + + /* the following line cleans up CfSysLineHandlers that were not based on loadable + * modules. As such, they are not yet cleared. */ + unregCfSysLineHdlrs(); + + /* this is the last spot where this can be done - below output modules are unloaded! */ + + parserClassExit(); + rsconfClassExit(); + strExit(); + ratelimitModExit(); + dnscacheDeinit(); + thrdExit(); + objRelease(net, LM_NET_FILENAME); + + module.UnloadAndDestructAll(eMOD_LINK_ALL); + + rsrtExit(); /* runtime MUST always be deinitialized LAST (except for debug system) */ + DBGPRINTF("Clean shutdown completed, bye\n"); + + errmsgExit(); + /* dbgClassExit MUST be the last one, because it de-inits the debug system */ + dbgClassExit(); + + /* NO CODE HERE - dbgClassExit() must be the last thing before exit()! */ + clearPidFile(); +} + +/* This is the main entry point into rsyslogd. This must be a function in its own + * right in order to intialize the debug system in a portable way (otherwise we would + * need to have a statement before variable definitions. + * rgerhards, 20080-01-28 + */ +int +main(int argc, char **argv) +{ +#if defined(_AIX) + /* SRC support : fd 0 (stdin) must be the SRC socket + * startup. fd 0 is duped to a new descriptor so that stdin can be used + * internally by rsyslogd. + */ + + pthread_mutex_init(&mutHadHUP, NULL); + pthread_mutex_init(&mutChildDied, NULL); + strncpy(progname,argv[0], sizeof(progname)-1); + addrsz = sizeof(srcaddr); + if ((rc = getsockname(0, &srcaddr, &addrsz)) < 0) { + fprintf(stderr, "%s: continuing without SRC support\n", progname); + src_exists = FALSE; + } + if (src_exists) + if(dup2(0, SRC_FD) == -1) { + fprintf(stderr, "%s: dup2 failed exiting now...\n", progname); + /* In the unlikely event of dup2 failing we exit */ + exit(-1); + } +#endif + + mainthread = pthread_self(); + if((int) getpid() == 1) { + fprintf(stderr, "rsyslogd %s: running as pid 1, enabling " + "container-specific defaults, press ctl-c to " + "terminate rsyslog\n", VERSION); + PidFile = strdup("NONE"); /* disables pid file writing */ + glblPermitCtlC = 1; + runningInContainer = 1; + emitTZWarning = 1; + } else { + /* "dynamic defaults" - non-container case */ + PidFile = strdup(PATH_PIDFILE); + } + if(PidFile == NULL) { + fprintf(stderr, "rsyslogd: could not alloc memory for pid file " + "default name - aborting\n"); + exit(1); + } + + /* disable case-sensitive comparisons in variable subsystem: */ + fjson_global_do_case_sensitive_comparison(0); + + dbgClassInit(); + + initAll(argc, argv); +#ifdef HAVE_LIBSYSTEMD + sd_notify(0, "READY=1"); + dbgprintf("done signaling to systemd that we are ready!\n"); +#endif + DBGPRINTF("max message size: %d\n", glblGetMaxLine(runConf)); + DBGPRINTF("----RSYSLOGD INITIALIZED\n"); + LogMsg(0, RS_RET_OK, LOG_DEBUG, "rsyslogd fully started up and initialized " + "- begin actual processing"); + + mainloop(); + LogMsg(0, RS_RET_OK, LOG_DEBUG, "rsyslogd shutting down"); + deinitAll(); + osf_close(); + pthread_mutex_destroy(&mutChildDied); + pthread_mutex_destroy(&mutHadHUP); + return 0; +} diff --git a/tools/smfile.c b/tools/smfile.c new file mode 100644 index 0000000..afd919b --- /dev/null +++ b/tools/smfile.c @@ -0,0 +1,137 @@ +/* smfile.c + * This is a strgen module for the traditional file format. + * + * Format generated: + * "%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" + * Note that this is the same as smtradfile.c, except that we do have a RFC3339 timestamp. However, + * we have copied over the code from there, it is too simple to go through all the hassle + * of having a single code base. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2010-06-01 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "unicode-helper.h" + +MODULE_TYPE_STRGEN +MODULE_TYPE_NOKEEP +STRGEN_NAME("RSYSLOG_FileFormat") + +/* internal structures + */ +DEF_SMOD_STATIC_DATA + + +/* config data */ + + +/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings + * needed (including their length) and then calculating the actual space required. So when we + * finally copy, we know exactly what we need. So we do at most one alloc. + */ +BEGINstrgen + register int iBuf; + uchar *pTimeStamp; + size_t lenTimeStamp; + uchar *pHOSTNAME; + size_t lenHOSTNAME; + uchar *pTAG; + int lenTAG; + uchar *pMSG; + size_t lenMSG; + size_t lenTotal; +CODESTARTstrgen + /* first obtain all strings and their length (if not fixed) */ + pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3339Date); + lenTimeStamp = ustrlen(pTimeStamp); + pHOSTNAME = (uchar*) getHOSTNAME(pMsg); + lenHOSTNAME = getHOSTNAMELen(pMsg); + getTAG(pMsg, &pTAG, &lenTAG, LOCK_MUTEX); + pMSG = getMSG(pMsg); + lenMSG = getMSGLen(pMsg); + + /* calculate len, constants for spaces and similar fixed strings */ + lenTotal = lenTimeStamp + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 2; + if(pMSG[0] != ' ') + ++lenTotal; /* then we need to introduce one additional space */ + + /* now make sure buffer is large enough */ + if(lenTotal >= iparam->lenBuf) + CHKiRet(ExtendBuf(iparam, lenTotal)); + + /* and concatenate the resulting string */ + memcpy(iparam->param, pTimeStamp, lenTimeStamp); + iBuf = lenTimeStamp; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pHOSTNAME, lenHOSTNAME); + iBuf += lenHOSTNAME; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pTAG, lenTAG); + iBuf += lenTAG; + + if(pMSG[0] != ' ') + iparam->param[iBuf++] = ' '; + memcpy(iparam->param + iBuf, pMSG, lenMSG); + iBuf += lenMSG; + + /* trailer */ + iparam->param[iBuf++] = '\n'; + iparam->param[iBuf] = '\0'; + + iparam->lenStr = lenTotal - 1; /* do not count \0! */ + +finalize_it: +ENDstrgen + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_SMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(smfile) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + + dbgprintf("rsyslog standard file format strgen init called, compiled with version %s\n", VERSION); +ENDmodInit diff --git a/tools/smfile.h b/tools/smfile.h new file mode 100644 index 0000000..d412554 --- /dev/null +++ b/tools/smfile.h @@ -0,0 +1,31 @@ +/* smfile.h + * These are the definitions for the traditional file format stringen module. + * + * File begun on 2010-06-04 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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. + */ +#ifndef SMFILE_H_INCLUDED +#define SMFILE_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitsmfile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef SMFILE_H_INCLUDED */ diff --git a/tools/smfwd.c b/tools/smfwd.c new file mode 100644 index 0000000..95ea425 --- /dev/null +++ b/tools/smfwd.c @@ -0,0 +1,145 @@ +/* smfwd.c + * This is a strgen module for the traditional (network) forwarding format. + * + * Format generated: + * "<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%" + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2010-06-01 by RGerhards + * + * Copyright 2010-2016 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "unicode-helper.h" + +MODULE_TYPE_STRGEN +MODULE_TYPE_NOKEEP +STRGEN_NAME("RSYSLOG_ForwardFormat") + +/* internal structures + */ +DEF_SMOD_STATIC_DATA + + +/* config data */ + + +/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings + * needed (including their length) and then calculating the actual space required. So when we + * finally copy, we know exactly what we need. So we do at most one alloc. + */ +BEGINstrgen + register int iBuf; + const char *pPRI; + size_t lenPRI; + uchar *pTimeStamp; + size_t lenTimeStamp; + uchar *pHOSTNAME; + size_t lenHOSTNAME; + uchar *pTAG; + int lenTAG; + uchar *pMSG; + size_t lenMSG; + size_t lenTotal; +CODESTARTstrgen + /* first obtain all strings and their length (if not fixed) */ + pPRI = getPRI(pMsg); + lenPRI = strlen(pPRI); + pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3339Date); + lenTimeStamp = ustrlen(pTimeStamp); + pHOSTNAME = (uchar*) getHOSTNAME(pMsg); + lenHOSTNAME = getHOSTNAMELen(pMsg); + getTAG(pMsg, &pTAG, &lenTAG, LOCK_MUTEX); + if(lenTAG > 32) + lenTAG = 32; /* for forwarding, a max of 32 chars is permitted (RFC!) */ + pMSG = getMSG(pMsg); + lenMSG = getMSGLen(pMsg); + + /* calculate len, constants for spaces and similar fixed strings */ + lenTotal = 1 + lenPRI + 1 + lenTimeStamp + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 1; + if(pMSG[0] != ' ') + ++lenTotal; /* then we need to introduce one additional space */ + + /* now make sure buffer is large enough */ + if(lenTotal >= iparam->lenBuf) + CHKiRet(ExtendBuf(iparam, lenTotal)); + + /* and concatenate the resulting string */ + iparam->param[0] = '<'; + memcpy(iparam->param + 1, pPRI, lenPRI); + iBuf = lenPRI + 1; + iparam->param[iBuf++] = '>'; + + memcpy(iparam->param + iBuf, pTimeStamp, lenTimeStamp); + iBuf += lenTimeStamp; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pHOSTNAME, lenHOSTNAME); + iBuf += lenHOSTNAME; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pTAG, lenTAG); + iBuf += lenTAG; + + if(pMSG[0] != ' ') + iparam->param[iBuf++] = ' '; + memcpy(iparam->param + iBuf, pMSG, lenMSG); + iBuf += lenMSG; + + /* string terminator */ + iparam->param[iBuf] = '\0'; + + iparam->lenStr = lenTotal - 1; /* do not count \0! */ + +finalize_it: +ENDstrgen + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_SMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(smfwd) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + + dbgprintf("rsyslog standard (network) forward format strgen init called, compiled with version" + " %s\n", VERSION); +ENDmodInit diff --git a/tools/smfwd.h b/tools/smfwd.h new file mode 100644 index 0000000..b695aea --- /dev/null +++ b/tools/smfwd.h @@ -0,0 +1,30 @@ +/* smfwd.h + * + * File begun on 2010-06-04 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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. + */ +#ifndef SMFWD_H_INCLUDED +#define SMFWD_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitsmfwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef SMFWD_H_INCLUDED */ diff --git a/tools/smtradfile.c b/tools/smtradfile.c new file mode 100644 index 0000000..9a86ff4 --- /dev/null +++ b/tools/smtradfile.c @@ -0,0 +1,130 @@ +/* smtradfile.c + * This is a strgen module for the traditional file format. + * + * Format generated: + * "%TIMESTAMP% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2010-06-01 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "unicode-helper.h" + +MODULE_TYPE_STRGEN +MODULE_TYPE_NOKEEP +STRGEN_NAME("RSYSLOG_TraditionalFileFormat") + +/* internal structures + */ +DEF_SMOD_STATIC_DATA + + +/* config data */ + + +/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings + * needed (including their length) and then calculating the actual space required. So when we + * finally copy, we know exactly what we need. So we do at most one alloc. + */ +BEGINstrgen + register int iBuf; + uchar *pTimeStamp; + uchar *pHOSTNAME; + size_t lenHOSTNAME; + uchar *pTAG; + int lenTAG; + uchar *pMSG; + size_t lenMSG; + size_t lenTotal; +CODESTARTstrgen + /* first obtain all strings and their length (if not fixed) */ + pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3164Date); + pHOSTNAME = (uchar*) getHOSTNAME(pMsg); + lenHOSTNAME = getHOSTNAMELen(pMsg); + getTAG(pMsg, &pTAG, &lenTAG, LOCK_MUTEX); + pMSG = getMSG(pMsg); + lenMSG = getMSGLen(pMsg); + + /* calculate len, constants for spaces and similar fixed strings */ + lenTotal = CONST_LEN_TIMESTAMP_3164 + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 2; + if(pMSG[0] != ' ') + ++lenTotal; /* then we need to introduce one additional space */ + + /* now make sure buffer is large enough */ + if(lenTotal >= iparam->lenBuf) + CHKiRet(ExtendBuf(iparam, lenTotal)); + + /* and concatenate the resulting string */ + memcpy(iparam->param, pTimeStamp, CONST_LEN_TIMESTAMP_3164); + iparam->param[CONST_LEN_TIMESTAMP_3164] = ' '; + + memcpy(iparam->param + CONST_LEN_TIMESTAMP_3164 + 1, pHOSTNAME, lenHOSTNAME); + iBuf = CONST_LEN_TIMESTAMP_3164 + 1 + lenHOSTNAME; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pTAG, lenTAG); + iBuf += lenTAG; + + if(pMSG[0] != ' ') + iparam->param[iBuf++] = ' '; + memcpy(iparam->param + iBuf, pMSG, lenMSG); + iBuf += lenMSG; + + /* trailer */ + iparam->param[iBuf++] = '\n'; + iparam->param[iBuf] = '\0'; + + iparam->lenStr = lenTotal - 1; /* do not count \0! */ + +finalize_it: +ENDstrgen + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_SMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(smtradfile) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + dbgprintf("traditional file format strgen init called, compiled with version %s\n", VERSION); +ENDmodInit diff --git a/tools/smtradfile.h b/tools/smtradfile.h new file mode 100644 index 0000000..dd5ee16 --- /dev/null +++ b/tools/smtradfile.h @@ -0,0 +1,31 @@ +/* smtradfile.h + * These are the definitions for the traditional file format stringen module. + * + * File begun on 2010-06-01 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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. + */ +#ifndef SMTRADFILE_H_INCLUDED +#define SMTRADFILE_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitsmtradfile(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, + rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef SMTRADFILE_H_INCLUDED */ diff --git a/tools/smtradfwd.c b/tools/smtradfwd.c new file mode 100644 index 0000000..163083f --- /dev/null +++ b/tools/smtradfwd.c @@ -0,0 +1,142 @@ +/* smtradfwd.c + * This is a strgen module for the traditional forwarding format. + * + * Format generated: + * "<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%" + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2010-06-01 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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 <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "syslogd.h" +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "msg.h" +#include "module-template.h" +#include "unicode-helper.h" + +MODULE_TYPE_STRGEN +MODULE_TYPE_NOKEEP +STRGEN_NAME("RSYSLOG_TraditionalForwardFormat") + +/* internal structures + */ +DEF_SMOD_STATIC_DATA + + +/* config data */ + + +/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings + * needed (including their length) and then calculating the actual space required. So when we + * finally copy, we know exactly what we need. So we do at most one alloc. + */ +BEGINstrgen + register int iBuf; + const char *pPRI; + size_t lenPRI; + uchar *pTimeStamp; + uchar *pHOSTNAME; + size_t lenHOSTNAME; + uchar *pTAG; + int lenTAG; + uchar *pMSG; + size_t lenMSG; + size_t lenTotal; +CODESTARTstrgen + /* first obtain all strings and their length (if not fixed) */ + pPRI = getPRI(pMsg); + lenPRI = strlen(pPRI); + pTimeStamp = (uchar*) getTimeReported(pMsg, tplFmtRFC3164Date); + pHOSTNAME = (uchar*) getHOSTNAME(pMsg); + lenHOSTNAME = getHOSTNAMELen(pMsg); + getTAG(pMsg, &pTAG, &lenTAG, LOCK_MUTEX); + if(lenTAG > 32) + lenTAG = 32; /* for forwarding, a max of 32 chars is permitted (RFC!) */ + pMSG = getMSG(pMsg); + lenMSG = getMSGLen(pMsg); + + /* calculate len, constants for spaces and similar fixed strings */ + lenTotal = 1 + lenPRI + 1 + CONST_LEN_TIMESTAMP_3164 + 1 + lenHOSTNAME + 1 + lenTAG + lenMSG + 1; + if(pMSG[0] != ' ') + ++lenTotal; /* then we need to introduce one additional space */ + + /* now make sure buffer is large enough */ + if(lenTotal >= iparam->lenBuf) + CHKiRet(ExtendBuf(iparam, lenTotal)); + + /* and concatenate the resulting string */ + iparam->param[0] = '<'; + memcpy(iparam->param + 1, pPRI, lenPRI); + iBuf = lenPRI + 1; + iparam->param[iBuf++] = '>'; + + memcpy(iparam->param + iBuf, pTimeStamp, CONST_LEN_TIMESTAMP_3164); + iBuf += CONST_LEN_TIMESTAMP_3164; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pHOSTNAME, lenHOSTNAME); + iBuf += lenHOSTNAME; + iparam->param[iBuf++] = ' '; + + memcpy(iparam->param + iBuf, pTAG, lenTAG); + iBuf += lenTAG; + + if(pMSG[0] != ' ') + iparam->param[iBuf++] = ' '; + memcpy(iparam->param + iBuf, pMSG, lenMSG); + iBuf += lenMSG; + + /* string terminator */ + iparam->param[iBuf] = '\0'; + + iparam->lenStr = lenTotal - 1; /* do not count \0! */ + +finalize_it: +ENDstrgen + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_SMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(smtradfwd) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + dbgprintf("rsyslog traditional (network) forward format strgen init called, compiled with " + "version %s\n", VERSION); +ENDmodInit diff --git a/tools/smtradfwd.h b/tools/smtradfwd.h new file mode 100644 index 0000000..2bbaee3 --- /dev/null +++ b/tools/smtradfwd.h @@ -0,0 +1,30 @@ +/* smtradfwd.h + * + * File begun on 2010-06-04 by RGerhards + * + * Copyright 2010-2014 Rainer Gerhards and 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. + */ +#ifndef SMTRADFWD_H_INCLUDED +#define SMTRADFWD_H_INCLUDED 1 + +/* prototypes */ +rsRetVal modInitsmtradfwd(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, +rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t*); + +#endif /* #ifndef SMTRADFWD_H_INCLUDED */ diff --git a/tools/syslogd.c b/tools/syslogd.c new file mode 100644 index 0000000..1229228 --- /dev/null +++ b/tools/syslogd.c @@ -0,0 +1,136 @@ +/** + * main rsyslog file with GPLv3 content. + * + * *********************** NOTE ************************ + * * Do no longer patch this file. If there is hard * + * * need to, talk to Rainer as to how we can make any * + * * patch be licensed under ASL 2.0. * + * * THIS FILE WILL GO AWAY. The new main file is * + * * rsyslogd.c. * + * ***************************************************** + * + * Please visit the rsyslog project at + * https://www.rsyslog.com + * to learn more about it and discuss any questions you may have. + * + * rsyslog had initially been forked from the sysklogd project. + * I would like to express my thanks to the developers of the sysklogd + * package - without it, I would have had a much harder start... + * + * Please note that while rsyslog started from the sysklogd code base, + * it nowadays has almost nothing left in common with it. Allmost all + * parts of the code have been rewritten. + * + * This Project was intiated and is maintained by + * Rainer Gerhards <rgerhards@hq.adiscon.com>. + * + * rsyslog - An Enhanced syslogd Replacement. + * Copyright 2003-2016 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" +#include "rsyslog.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <ctype.h> +#include <limits.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> +#include <assert.h> +#include <errno.h> + +#ifdef OS_SOLARIS +# include <fcntl.h> +# include <stropts.h> +# include <sys/termios.h> +# include <sys/types.h> +#else +# include <libgen.h> +#endif + +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <sys/file.h> +#include <sys/resource.h> +#include <grp.h> + +#ifdef HAVE_SYS_TIMESPEC_H +# include <sys/timespec.h> +#endif + +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif + +#include <signal.h> + +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#include "srUtils.h" +#include "stringbuf.h" +#include "syslogd-types.h" +#include "template.h" +#include "outchannel.h" +#include "syslogd.h" + +#include "msg.h" +#include "iminternal.h" +#include "threads.h" +#include "parser.h" +#include "unicode-helper.h" +#include "dnscache.h" +#include "ratelimit.h" + +#ifndef HAVE_SETSID +/* stems back to sysklogd in whole */ +void untty(void) +{ + int i; + pid_t pid; + + if(!Debug) { + /* Peng Haitao <penght@cn.fujitsu.com> contribution */ + pid = getpid(); + if (setpgid(pid, pid) < 0) { + perror("setpgid"); + exit(1); + } + /* end Peng Haitao <penght@cn.fujitsu.com> contribution */ + + i = open(_PATH_TTY, O_RDWR|O_CLOEXEC); + if (i >= 0) { +# if !defined(__hpux) + (void) ioctl(i, (int) TIOCNOTTY, NULL); +# else + /* TODO: we need to implement something for HP UX! -- rgerhards, 2008-03-04 */ + /* actually, HP UX should have setsid, so the code directly above should + * trigger. So the actual question is why it doesn't do that... + */ +# endif + close(i); + } + } +} +#endif diff --git a/tools/syslogd.h b/tools/syslogd.h new file mode 100644 index 0000000..9bfe432 --- /dev/null +++ b/tools/syslogd.h @@ -0,0 +1,38 @@ +/* common header for syslogd + * Copyright 2007-2012 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. + */ +#ifndef SYSLOGD_H_INCLUDED +#define SYSLOGD_H_INCLUDED 1 + +#include "syslogd-types.h" +#include "objomsr.h" +#include "modules.h" +#include "template.h" +#include "action.h" +#include "linkedlist.h" + +/* the following prototypes should go away once we have an input + * module interface -- rgerhards, 2007-12-12 + */ +extern int NoHops; +extern int send_to_all; +extern int Debug; +#include "dirty.h" + +#endif /* #ifndef SYSLOGD_H_INCLUDED */ |