Index: trunk/Makefile.in
===================================================================
--- trunk/Makefile.in	(revision 182)
+++ trunk/Makefile.in	(revision 183)
@@ -118,5 +118,6 @@
 	rijndael-boxes-fst.h sh_socket.h sh_ignore.h sh_prelude.h \
 	sh_mounts.h sh_userfiles.h sh_static.h sh_prelink.h \
-	sh_processcheck.h sh_portcheck.h sh_pthread.h sh_string.h
+	sh_processcheck.h sh_portcheck.h sh_pthread.h sh_string.h \
+	sh_log_check.h sh_log_evalrule.h
 
 
@@ -153,5 +154,7 @@
 	$(srcsrc)/sh_processcheck.c \
 	$(srcsrc)/sh_pthread.c $(srcsrc)/sh_string.c \
-	$(srcsrc)/dnmalloc.c \
+	$(srcsrc)/sh_log_parse_syslog.c $(srcsrc)/sh_log_parse_pacct.c \
+	$(srcsrc)/sh_log_parse_apache.c $(srcsrc)/sh_log_evalrule.c \
+	$(srcsrc)/sh_log_check.c $(srcsrc)/dnmalloc.c \
 	$(srcsrc)/t-test1.c
 
@@ -168,4 +171,6 @@
 	sh_mounts.o sh_userfiles.o sh_prelink.o sh_static.o \
 	sh_processcheck.o sh_portcheck.o sh_port2proc.o \
+	sh_log_parse_syslog.o sh_log_parse_pacct.o sh_log_parse_apache.o \
+	sh_log_evalrule.o sh_log_check.o \
 	sh_pthread.o sh_string.o dnmalloc.o
 
@@ -1665,5 +1670,5 @@
 sh_entropy.o: $(srcsrc)/sh_entropy.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_tiger.h $(srcinc)/sh_calls.h $(srcinc)/sh_pthread.h $(srcinc)/sh_static.h $(srcinc)/sh_pthread.h $(srcinc)/CuTest.h 
 sh_forward.o: $(srcsrc)/sh_forward.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_tiger.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_forward.h $(srcinc)/sh_srp.h $(srcinc)/sh_fifo.h $(srcinc)/sh_tools.h $(srcinc)/sh_entropy.h $(srcinc)/sh_html.h $(srcinc)/sh_mail.h $(srcinc)/sh_socket.h $(srcinc)/sh_static.h $(srcinc)/rijndael-api-fst.h $(srcinc)/sh_readconf.h $(srcinc)/zAVLTree.h $(srcinc)/sh_extern.h 
-sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h 
+sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h $(srcinc)/sh_logmon.h 
 sh_utmp.o: $(srcsrc)/sh_utmp.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_utmp.h $(srcinc)/sh_pthread.h 
 sh_kern.o: $(srcsrc)/sh_kern.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_kern.h sh_ks_xor.h $(srcinc)/sh_unix.h $(srcinc)/sh_hash.h 
@@ -1714,2 +1719,7 @@
 t-test1.o: $(srcsrc)/t-test1.c Makefile config.h $(srcinc)/malloc.h 
 sh_port2proc.o: $(srcsrc)/sh_port2proc.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error_min.h $(srcinc)/sh_utils.h $(srcinc)/sh_pthread.h 
+sh_log_parse_syslog.o: $(srcsrc)/sh_log_parse_syslog.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
+sh_log_parse_pacct.o: $(srcsrc)/sh_log_parse_pacct.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
+sh_log_parse_apache.o: $(srcsrc)/sh_log_parse_apache.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
+sh_log_evalrule.o: $(srcsrc)/sh_log_evalrule.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/zAVLTree.h 
+sh_log_check.o: $(srcsrc)/sh_log_check.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_modules.h 
Index: trunk/aclocal.m4
===================================================================
--- trunk/aclocal.m4	(revision 182)
+++ trunk/aclocal.m4	(revision 183)
@@ -409,5 +409,5 @@
 x_libraries=NONE
 DESTDIR=
-SH_ENABLE_OPTS="db-reload xml-log message-queue login-watch process-check port-check mounts-check userfiles debug ptrace static network udp nocl stealth micro-stealth install-name identity khide suidcheck base largefile mail external-scripts encrypt srp dnmalloc"
+SH_ENABLE_OPTS="db-reload xml-log message-queue login-watch process-check port-check mounts-check logfile-monitor userfiles debug ptrace static network udp nocl stealth micro-stealth install-name identity khide suidcheck base largefile mail external-scripts encrypt srp dnmalloc"
 SH_WITH_OPTS="prelude libprelude-prefix database libwrap cflags libs console altconsole timeserver alttimeserver rnd egd-socket port logserver altlogserver kcheck gpg checksum fp recipient sender trusted tmp-dir config-file log-file pid-file state-dir data-file html-file"
 
Index: trunk/configure.ac
===================================================================
--- trunk/configure.ac	(revision 182)
+++ trunk/configure.ac	(revision 183)
@@ -242,4 +242,75 @@
   AC_EGREP_HEADER(ut_type, utmp.h, AC_DEFINE(HAVE_UTTYPE) )
 fi
+
+dnl
+dnl figure out where acct.h lives
+dnl and whether fields are int/comp_t
+dnl
+dnl GNU Accounting Utilities
+dnl Copyright (C) 1993, 1996, 1997, 2003, 2005 Free Software Foundation, Inc.
+dnl The GNU Accounting Utilities are free software; you can redistribute
+dnl them and/or modify them under the terms of the GNU General Public
+dnl License as published by the Free Software Foundation; either version
+dnl 2, or (at your option) any later version.
+dnl
+AC_CHECK_HEADER(sys/acct.h,
+		AC_DEFINE(HAVE_SYS_ACCT_H, ,
+			  [Define if you have the <sys/acct.h> header file.])
+		AC_HEADER_EGREP(ac_utime, sys/acct.h,
+				AC_DEFINE(HAVE_ACUTIME, ,
+					  [Define if <sys/acct.h> has the AC_UTIME field.])
+				AC_HEADER_EGREP(comp_t.*ac_utime, sys/acct.h,
+						AC_DEFINE(ACUTIME_COMPT, ,
+							  [Define if <sys/acct.h>'s AC_UTIME field is a COMP_T.]))
+		)
+		AC_HEADER_EGREP(ac_stime, sys/acct.h,
+				AC_DEFINE(HAVE_ACSTIME, ,
+					  [Define if <sys/acct.h> has the AC_STIME field.])
+				AC_HEADER_EGREP(comp_t.*ac_stime, sys/acct.h,
+						AC_DEFINE(ACSTIME_COMPT, ,
+							  [Define if <sys/acct.h>'s AC_STIME field is a COMP_T.]))
+		)
+		AC_HEADER_EGREP(ac_etime, sys/acct.h,
+				AC_DEFINE(HAVE_ACETIME, ,
+					  [Define if <sys/acct.h> has the AC_ETIME field.])
+				AC_HEADER_EGREP(comp_t.*ac_etime, sys/acct.h,
+						AC_DEFINE(ACETIME_COMPT, ,
+							  [Define if <sys/acct.h>'s AC_ETIME field is a COMP_T.]))
+		)
+		AC_HEADER_EGREP(ac_io,    sys/acct.h,
+				AC_DEFINE(HAVE_ACIO, ,
+					  [Define if <sys/acct.h> has the AC_IO field.])
+				AC_HEADER_EGREP(comp_t.*ac_io,    sys/acct.h,
+						AC_DEFINE(ACIO_COMPT, ,
+							  [Define if <sys/acct.h>'s AC_IO field is a COMP_T.]))
+		)
+		AC_HEADER_EGREP(ac_mem,   sys/acct.h,
+				AC_DEFINE(HAVE_ACMEM, ,
+					  [Define if <sys/acct.h> has the AC_MEM field.])
+				AC_HEADER_EGREP(comp_t.*ac_mem,   sys/acct.h,
+						AC_DEFINE(ACMEM_COMPT, ,
+							  [Define if <sys/acct.h>'s AC_MEM field is a COMP_T.]))
+		)
+		AC_HEADER_EGREP(ac_minflt,   sys/acct.h,
+				AC_HEADER_EGREP(ac_majflt,   sys/acct.h,
+						AC_HEADER_EGREP(ac_swaps,   sys/acct.h,
+								AC_DEFINE(HAVE_PAGING, ,
+									  [Define if <sys/acct.h> has the AC_MINFLT, AC_MAJFLT and AC_SWAPS fields.])
+								AC_HEADER_EGREP(comp_t.*ac_minflt, sys/acct.h,
+										AC_DEFINE(ACMINFLT_COMPT, ,
+											  [Define if <sys/acct.h>'s AC_MINFLT field is a COMP_T.]))
+								AC_HEADER_EGREP(comp_t.*ac_mayflt, sys/acct.h,
+										AC_DEFINE(ACMAJFLT_COMPT, ,
+											  [Define if <sys/acct.h>'s AC_MAJFLT field is a COMP_T.]))
+								AC_HEADER_EGREP(comp_t.*ac_swaps, sys/acct.h,
+										AC_DEFINE(ACSWAPS_COMPT, ,
+											  [Define if <sys/acct.h>'s AC_SWAPS field is a COMP_T.]))
+						)
+				)
+		)
+		AC_HEADER_EGREP(comp_t,   sys/acct.h, AC_DEFINE(HAVE_COMP_T, ,
+								[Define if <sys/acct.h> uses the COMP_T type.]))
+ )
+
 
 dnl need to check because AIX 4.2 does not have it
@@ -589,5 +660,5 @@
 then
 	AC_MSG_RESULT([no])
-	AC_MSG_ERROR([No ps in /usr/ucb /bin /usr/bin])
+	AC_MSG_ERROR([Cannot find ps in any of /usr/ucb /bin /usr/bin])
 fi
 AC_DEFINE_UNQUOTED([PSPATH], _("$PS"), [Path to ps])
@@ -687,8 +758,8 @@
 	   elif test "x${enable_message_queue}" != xno; then
 	       echo "${enableval}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-		   AC_MSG_ERROR([--enable-message-queue: MODE must be numeric])
+		   AC_MSG_ERROR([With --enable-message-queue=MODE, MODE must be numeric])
 	       echo "${enableval}" | \
 		   grep ['0[0123456789][0123456789][0123456789]'] >/dev/null 2>&1 ||
-		   AC_MSG_ERROR([--enable-message-queue: MODE must be an octal (0nnn) number])
+		   AC_MSG_ERROR([With --enable-message-queue=MODE, MODE must be an octal (0nnn) number])
                AC_DEFINE(WITH_MESSAGE_QUEUE)
                AC_DEFINE_UNQUOTED(MESSAGE_QUEUE_MODE, ${enable_message_queue})
@@ -762,5 +833,5 @@
      AC_CHECK_HEADER(tcpd.h,
 		     [],
-		     [ AC_MSG_ERROR([Could not find tcpd.h for libwrap. You must first install tcp_wrappers]) ])
+		     [ AC_MSG_ERROR([Could not find tcpd.h for libwrap. You need to install tcp_wrappers.]) ])
      AC_TRY_LINK([ #include <tcpd.h>
 		   int allow_severity; int deny_severity; ],
@@ -810,5 +881,5 @@
 dnl				[sh_use_lcaps="no"])
 	else
-		AC_MSG_ERROR([--enable-network=WHAT: WHAT must be client, server, or no])
+		AC_MSG_ERROR([With --enable-network=WHAT, WHAT must be client, server, or no])
         fi
         ],
@@ -963,5 +1034,5 @@
 	       case "$sh_libprelude_version" in
 	       0.8*)
-	        AC_MSG_ERROR([Libprelude 0.8 is too old, 0.9.6 or higher is required.])
+	        AC_MSG_ERROR([You have Libprelude 0.8, which is too old. Version 0.9.6 or higher is required.])
 	       ;;
 	       *)
@@ -995,5 +1066,5 @@
         [
         if test x"$enable_xml_log" != xyes; then
-                AC_MSG_ERROR([--with-database:  --enable-xml-log required])
+                AC_MSG_ERROR([With --with-database,  --enable-xml-log is required as well.])
         fi
         if test "x${withval}" = "xmysql"; then
@@ -1101,5 +1172,5 @@
 		fi
 		echo
-		AC_MSG_ERROR([libmysql not found or unuseable]) 
+		AC_MSG_ERROR([Could not find libmysql, or it is not useable.]) 
           fi
 	  AC_CHECK_HEADERS(mysql/mysql.h)
@@ -1284,5 +1355,5 @@
 
         else
-          AC_MSG_ERROR([--with-database:  unsupported database ${withval}])
+          AC_MSG_ERROR([Option --with-database=database used with unsupported database ${withval}])
         fi
         ]
@@ -1353,4 +1424,20 @@
         ]
 )
+
+AC_ARG_ENABLE(logfile-monitor,
+        [  --enable-logfile-monitor		monitor logfiles [[no]]],
+        [
+        if test "x${enable_logfile_monitor}" = xyes; then
+	   AC_CHECK_HEADER(pcre.h,
+                           [ 
+			   AC_DEFINE(USE_LOGFILE_MONITOR, 1, [Define if you want the logfile monitor module.])
+			   LIBS="-lpcre $LIBS"
+			   ],
+	                   AC_MSG_ERROR([The --enable-logfile-monitor option requires libpcre. For compiling the pcre development package is needed.])
+	   )
+        fi
+        ]
+)
+
 
 AC_ARG_ENABLE(process-check,
@@ -1507,5 +1594,5 @@
     * )
       AC_MSG_RESULT([invalid argument])
-      AC_MSG_ERROR([--with-rnd: there is no random module ${use_static_rnd}])
+      AC_MSG_ERROR([Option --with-rnd=module used with unsupported module ${use_static_rnd}])
       ;;
 esac
@@ -1605,5 +1692,5 @@
         [
 	echo "${withval}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-		AC_MSG_ERROR([--with-port: PORT must be numeric])
+		AC_MSG_ERROR([For --with-port=PORT, PORT must be numeric.])
 	myport=${withval}
 	],
@@ -1657,14 +1744,14 @@
 	fi
 	if test "x${enableval}" = "xstop" || test "x${enableval}" = "xstart"; then
-	  AC_MSG_ERROR([--enable-nocl: start/stop/reload/restart/status are reserved words])
+	  AC_MSG_ERROR([For --enable-nocl=PW start/stop/reload/restart/status are reserved words.])
 	fi
 	if test "x${enableval}" = "xreload" || test "x${enableval}" = "xrestart"; then
-	  AC_MSG_ERROR([--enable-nocl: start/stop/reload/restart/status are reserved words])
+	  AC_MSG_ERROR([For --enable-nocl=PW start/stop/reload/restart/status are reserved words.])
 	fi
 	if test "x${enableval}" = "xstatus"; then
-	  AC_MSG_ERROR([--enable-nocl: start/stop/reload/restart/status are reserved words])
+	  AC_MSG_ERROR([For --enable-nocl=PW start/stop/reload/restart/status are reserved words.])
 	fi
 	if test "x${enableval}" = "xno"; then
-	  AC_MSG_ERROR([--enable-nocl: use of --enable-nocl=no is ambiguous])
+	  AC_MSG_ERROR([With --enable-nocl=PW, the use of --enable-nocl=no is ambiguous.])
 	fi
 	nocl_code="${enable_nocl}"
@@ -1678,5 +1765,5 @@
 	if test "x${enableval}" != "xyes"; then
 		echo "${enableval}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-			AC_MSG_ERROR([--enable-stealth: XOR_VAL must be numeric])
+			AC_MSG_ERROR([For --enable-stealth=XOR_VAL, XOR_VAL must be numeric.])
 		if test "${enableval}" -lt 127 || test "${enableval}" -gt 255; then
 			if test x"${enableval}" = x0
@@ -1684,5 +1771,5 @@
 				:
 			else
-				AC_MSG_ERROR([--enable-stealth: XOR_VAL must be in the range 127 to 255])
+				AC_MSG_ERROR([For --enable-stealth=XOR_VAL, XOR_VAL must be in the range 127 to 255.])
 			fi
 		fi
@@ -1704,5 +1791,5 @@
 	if test "x${enableval}" != "xyes"; then
 		echo "${enableval}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-			AC_MSG_ERROR([--enable-micro-stealth: XOR_VAL must be numeric])
+			AC_MSG_ERROR([For --enable-micro-stealth=XOR_VAL, XOR_VAL must be numeric.])
 		if test "${enableval}" -lt 127 || test "${enableval}" -gt 255; then
 			if test x"${enableval}" = x0
@@ -1710,5 +1797,5 @@
 				:
 			else
-				AC_MSG_ERROR([--enable-micro-stealth: XOR_VAL must be in the range 127 to 255])
+				AC_MSG_ERROR([For --enable-micro-stealth=XOR_VAL, XOR_VAL must be in the range 127 to 255.])
 			fi
 		fi
@@ -1750,9 +1837,9 @@
 	fi
 	echo "${myident}" | grep ['[^0123456789]'] >/dev/null 2>&1 || \
-		AC_MSG_ERROR([--enable-identity: need username, not UID])
+		AC_MSG_ERROR([With --enable-identity=USER, please supply a username, not a UID.])
 	myident_uid=`(cat /etc/passwd; ypcat passwd) 2>/dev/null |\
 	  grep "^${myident}:" | awk -F: '{ print $3; }'`
 	if test x"${myident_uid}" = x; then
-	  AC_MSG_WARN([--enable-identity: user ${myident} will be added upon install])
+	  AC_MSG_WARN([Option --enable-identity used, user ${myident} will be added upon install.])
 	  need_user_install=1
 	fi
@@ -1794,10 +1881,10 @@
 	   sh_syscalltable=`egrep '(D|d|R|r) sys_call_table' ${khidemap} | awk '{print $1}'`
 	   if test x"$sh_syscalltable" = x; then
-		AC_MSG_ERROR([--enable-khide: symbol sys_call_table not found in ${khidemap}])
+		AC_MSG_ERROR([Option --enable-khide cannot be used since the symbol sys_call_table was not found in ${khidemap}.])
 	   fi
 	   sh_syscalltable="0x${sh_syscalltable}"
 	   install_name_len=`echo ${install_name} | awk '{ print(length()); }'`
 	   if test "${install_name_len}" -gt 15 ; then
-		AC_MSG_ERROR([--enable-khide: install_name exceeds 15 char length limit])
+		AC_MSG_ERROR([If --enable-khide is used, install_name must not exceed a length of 15 chars.])
 	   fi
 	   AC_DEFINE(SH_USE_LKM)
@@ -1833,5 +1920,5 @@
 		sh_list_modules=`egrep 'd modules$' ${khidemap} | awk '{print $1}'` 
 		if test x"$sh_list_modules" = x; then
-			AC_MSG_ERROR([--enable-khide: symbol modules not found in ${khidemap}])
+			AC_MSG_ERROR([Option --enable-khide cannot be used, since the symbol modules was not found in ${khidemap}.])
 		fi
 		sh_list_modules="0x${sh_list_modules}"
@@ -1926,5 +2013,5 @@
 			:
 		else
-			AC_MSG_ERROR([--with-kcheck: cannot find system map ${systemmap}])
+			AC_MSG_ERROR([Option --with-kcheck=systemmap cannot be used, because system map ${systemmap} does not exist.])
 		fi
 	fi
@@ -1942,13 +2029,13 @@
         AC_MSG_RESULT(${my_key_A} ${my_key_B})
 	if test "x${my_key_A}" = x; then
-		AC_MSG_ERROR([--enable-base: first base key has zero length])
+		AC_MSG_ERROR([Option --enable-base=B1,B2 used with invalid first base key (zero length).])
 	fi
 	if test "x${my_key_B}" = x; then
-		AC_MSG_ERROR([--enable-base: second base key has zero length])
+		AC_MSG_ERROR([Option --enable-base=B1,B2 used with invalid second base key (zero length).])
 	fi
 	echo "${my_key_A}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-		AC_MSG_ERROR([--enable-base: base key must be numeric in the range 0 to 2147483647])
+		AC_MSG_ERROR([For --enable-base=B1,B2,  B1 and B2 must be numeric in the range 0 to 2147483647.])
 	echo "${my_key_B}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-		AC_MSG_ERROR([--enable-base: base key must be numeric in the range 0 to 2147483647])
+		AC_MSG_ERROR([For --enable-base=B1,B2,  B1 and B2 must be numeric in the range 0 to 2147483647.])
         ],
         [
@@ -2092,5 +2179,5 @@
 		else
 			if test "x${mychk}" = "x"; then
-				AC_MSG_ERROR([--with-checksum: gpg CHKSUM not specified])
+				AC_MSG_ERROR([Option --with-checksum=CHKSUM: checksum CHKSUM of the gpg binary not specified.])
 			fi
 		fi
@@ -2119,5 +2206,5 @@
 			echo "${withval0}" | \
 			grep ['[^0123456789abcdefABCDEF]'] >/dev/null 2>&1 &&
-			AC_MSG_ERROR([--with-fp: invalid character(s) in FINGERPRINT=${withval0}])
+			AC_MSG_ERROR([In option --with-fp=FINGERPRINT, there is an invalid character(s) in FINGERPRINT=${withval0}.])
 			sh_len=`echo ${withval0} | wc -c | sed 's% %%g'`
 			sh_len0=`expr ${sh_len} \- 1`
@@ -2129,8 +2216,8 @@
 				echo "${myfp}" | sed 's,.*:,,g' | sed 's, ,,g' | sed 's,\(.\),\1:,g' | awk '{ split($0, arr, ":"); m = length($1)/2; print "#ifndef FINGERPRINT_H"; print "#define FINGERPRINT_H"; printf "char gpgfp[%d];\n", m+1; for (i=1; i <= m; i++) printf "gpgfp[%d] = %c%s%c;\n", i-1, 39, arr[i], 39; printf "gpgfp[%d] = %c%c0%c;\n", m, 39, 92, 39; print "#endif"; }' > sh_gpg_fp.h 
 			else
-				AC_MSG_ERROR([--with-fp: length (${sh_len0}) of FINGERPRINT ${withval0} incorrect])
+				AC_MSG_ERROR([In option --with-fp=FINGERPRINT, the length (${sh_len0}) of FINGERPRINT ${withval0} is incorrect.])
 			fi
 		else
-			AC_MSG_ERROR([--with-fp: usage error ... FINGERPRINT=yes])
+			AC_MSG_ERROR([For option --with-fp=FINGERPRINT, FINGERPRINT=yes is invalid, please specify a valid key fingerprint.])
 		fi
 	fi
@@ -2155,9 +2242,9 @@
 		if test "x${sh_tmp}" != "x1"
 		then
-			AC_MSG_ERROR([--with-recipient: invalid mail address ${sh_item}])
+			AC_MSG_ERROR([Option --with-recipient=ADDR used with invalid mail address ${sh_item}.])
 		fi 
 		;;
 		*)
-		AC_MSG_ERROR([--with-recipient: invalid mail address ${sh_item}])
+		AC_MSG_ERROR([Option --with-recipient=ADDR used with invalid mail address ${sh_item}.])
 		;;
 		esac
@@ -2192,5 +2279,5 @@
 	do
 		echo "${sh_tmp1}" | grep ['[^0123456789]'] >/dev/null 2>&1 &&
-			AC_MSG_ERROR([--with-trusted: non-numeric UID in ${withval}])
+			AC_MSG_ERROR([Option --with-trusted=UID used with non-numeric UID in ${withval}.])
 		if test "x${sh_tmp1}" = "x0"
 		then
@@ -2388,5 +2475,5 @@
 			echo "It should be REQ_FROM_SERVER/some/local/path"
 		fi
-		AC_MSG_ERROR([--with-data-file: invalid path ${withval}])
+		AC_MSG_ERROR([Option --with-data-file=FILE used with invalid path ${withval}.])
 	fi
 	],
Index: trunk/depend.dep
===================================================================
--- trunk/depend.dep	(revision 182)
+++ trunk/depend.dep	(revision 183)
@@ -18,5 +18,5 @@
 sh_entropy.o: $(srcsrc)/sh_entropy.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_tiger.h $(srcinc)/sh_calls.h $(srcinc)/sh_pthread.h $(srcinc)/sh_static.h $(srcinc)/sh_pthread.h $(srcinc)/CuTest.h 
 sh_forward.o: $(srcsrc)/sh_forward.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_tiger.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_forward.h $(srcinc)/sh_srp.h $(srcinc)/sh_fifo.h $(srcinc)/sh_tools.h $(srcinc)/sh_entropy.h $(srcinc)/sh_html.h $(srcinc)/sh_mail.h $(srcinc)/sh_socket.h $(srcinc)/sh_static.h $(srcinc)/rijndael-api-fst.h $(srcinc)/sh_readconf.h $(srcinc)/zAVLTree.h $(srcinc)/sh_extern.h 
-sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h 
+sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h $(srcinc)/sh_logmon.h 
 sh_utmp.o: $(srcsrc)/sh_utmp.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_utmp.h $(srcinc)/sh_pthread.h 
 sh_kern.o: $(srcsrc)/sh_kern.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_kern.h sh_ks_xor.h $(srcinc)/sh_unix.h $(srcinc)/sh_hash.h 
@@ -69,2 +69,7 @@
 dnmalloc.o: $(srcsrc)/dnmalloc.c Makefile config.h 
 sh_port2proc.o: $(srcsrc)/sh_port2proc.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error_min.h $(srcinc)/sh_utils.h $(srcinc)/sh_pthread.h 
+sh_log_parse_syslog.o: $(srcsrc)/sh_log_parse_syslog.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
+sh_log_parse_pacct.o: $(srcsrc)/sh_log_parse_pacct.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
+sh_log_parse_apache.o: $(srcsrc)/sh_log_parse_apache.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
+sh_log_evalrule.o: $(srcsrc)/sh_log_evalrule.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/zAVLTree.h 
+sh_log_check.o: $(srcsrc)/sh_log_check.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h $(srcinc)/sh_modules.h 
Index: trunk/depend.sum
===================================================================
--- trunk/depend.sum	(revision 182)
+++ trunk/depend.sum	(revision 183)
@@ -1,1 +1,1 @@
-2749055739
+3470897936
Index: trunk/docs/Changelog
===================================================================
--- trunk/docs/Changelog	(revision 182)
+++ trunk/docs/Changelog	(revision 183)
@@ -1,3 +1,4 @@
-2.4.7:
+2.5.0:
+	* fixed constness in trustfile interface
 	* remove libprelude 0.8 support (obsolete)
 	* sh_forward.c: increase TIME_OUT_DEF to 900 secs
Index: trunk/include/sh_cat.h
===================================================================
--- trunk/include/sh_cat.h	(revision 182)
+++ trunk/include/sh_cat.h	(revision 183)
@@ -157,4 +157,14 @@
 #ifdef SH_USE_USERFILES
  MSG_USERFILES_SUMMARY,
+#endif
+
+#ifdef USE_LOGFILE_MONITOR
+ MSG_LOGMON_CHKS,
+ MSG_LOGMON_CHKE,
+ MSG_LOGMON_MISS,
+ MSG_LOGMON_EOPEN,
+ MSG_LOGMON_EREAD,
+ MSG_LOGMON_REP,
+ MSG_LOGMON_SUM,
 #endif
 
Index: trunk/include/sh_log_check.h
===================================================================
--- trunk/include/sh_log_check.h	(revision 183)
+++ trunk/include/sh_log_check.h	(revision 183)
@@ -0,0 +1,107 @@
+#ifndef SH_LOGCHECK_H
+#define SH_LOGCHECK_H
+
+#include <sys/types.h>
+#include <time.h>
+
+/* Convert a struct tm to unix timestamp with caching 
+ */
+time_t conv_timestamp (struct tm * btime, 
+		       struct tm * old_tm, time_t * old_time);
+
+/* Definition of a log record entry, to be returned from parsing function.
+ */
+#define PID_INVALID 0
+struct sh_logrecord 
+{
+  char      * filename;
+  sh_string * prefix; /* actually a string to group messages */
+  sh_string * host;
+  sh_string * timestr;
+  pid_t       pid;
+  time_t      timestamp;
+  sh_string * message;
+};
+
+#define SH_LOGFILE_MOVED  (1<<0)
+#define SH_LOGFILE_REWIND (1<<1)
+
+struct sh_logfile 
+{
+  FILE * fp;
+  int    flags;
+  char * filename;
+  dev_t  device_id;
+  ino_t  inode;
+  fpos_t offset;
+
+  /* Info for the parser, e.g. a regular expression
+   */
+  void * fileinfo;
+
+  /* Callback function to read the next record
+   */
+  sh_string *           (*get_record)  (sh_string * record, 
+					struct sh_logfile * logfile);
+
+  /* Callback function to parse the record into standard format
+   */
+  struct sh_logrecord * (*parse_record)(sh_string * logline, void * fileinfo);
+
+  struct sh_logfile * next;
+};
+
+/****************************************************************
+ **
+ ** Parsing and reading functions
+ **/
+
+/* Open file, position at stored offset. */
+int sh_open_for_reader (struct sh_logfile * logfile);
+
+/* Simple line reader.   */ 
+sh_string * sh_default_reader (sh_string * record, 
+			       struct sh_logfile * logfile);
+
+/* Binary reader */
+sh_string * sh_binary_reader (void * s, size_t size, struct sh_logfile * logfile);
+
+/* Parses a syslog-style line. */
+struct sh_logrecord * sh_parse_syslog (sh_string * logline, void * fileinfo);
+
+/* Format info for apache log. */
+void * sh_eval_fileinfo_apache(char * str);
+
+/* Parses a apache-style line. */
+struct sh_logrecord * sh_parse_apache (sh_string * logline, void * fileinfo);
+
+/* Get a pacct record */
+sh_string * sh_read_pacct (sh_string * record, struct sh_logfile * logfile);
+
+/* Parses a pacct record. */
+struct sh_logrecord * sh_parse_pacct (sh_string * logline, void * fileinfo);
+
+/**
+*****************************************************************/
+
+int sh_get_hidepid();
+int sh_set_hidepid(const char *s);
+
+#define SH_MAX_LCODE_SIZE 16
+
+struct sh_logfile_type 
+{
+  char code[SH_MAX_LCODE_SIZE];
+
+  /* read callback */
+  /*@null@*/sh_string * (*get_record)  (sh_string * record,
+					struct sh_logfile * logfile);
+  /* parsing callback */
+  struct sh_logrecord * (*parse_record)(sh_string * logline, void * fileinfo);
+
+  /* evaluate fileinfo */
+  void * (*eval_fileinfo)(char * str); 
+};
+
+
+#endif
Index: trunk/include/sh_log_evalrule.h
===================================================================
--- trunk/include/sh_log_evalrule.h	(revision 183)
+++ trunk/include/sh_log_evalrule.h	(revision 183)
@@ -0,0 +1,38 @@
+#ifndef SH_EVALRULE_H
+#define SH_EVALRULE_H
+
+/* Clean up everything.
+ */
+void sh_eval_cleanup();
+
+/* Define a new reporting queue, str := label:interval:(report|sum):severity
+ */
+int sh_eval_qadd (const char * str);
+
+/* Add a new rule, str := queue:regex
+ * If there is an open group, add it to its rules.
+ * ..else, add it to the currently open host (open the
+ * default host, if there is no open one)
+ */
+int sh_eval_radd (const char * str);
+
+/* Open a new host group definition.
+ */
+int sh_eval_hadd (const char * str);
+/*
+ * End the host definition
+ */
+int sh_eval_hend (const char * str);
+
+
+/* Open a new group definition. If a host is currently open, 
+ * the new group will automatically be added to that host.
+ */
+int sh_eval_gadd (const char * str);
+/*
+ * End the group definition
+ */
+int sh_eval_gend (const char * str);
+
+int sh_eval_process_msg(struct sh_logrecord * record);
+#endif
Index: trunk/include/sh_logmon.h
===================================================================
--- trunk/include/sh_logmon.h	(revision 183)
+++ trunk/include/sh_logmon.h	(revision 183)
@@ -0,0 +1,12 @@
+#ifndef SH_LOGMON_H
+#define SH_LOGMON_H
+
+extern sh_rconf sh_log_check_table[];
+
+int sh_log_check_init (struct mod_type * arg);
+int sh_log_check_timer(time_t tcurrent);
+int sh_log_check_check(void);
+int sh_log_check_reconf(void); 
+int sh_log_check_cleanup(void);
+
+#endif
Index: trunk/include/sh_string.h
===================================================================
--- trunk/include/sh_string.h	(revision 182)
+++ trunk/include/sh_string.h	(revision 183)
@@ -19,21 +19,25 @@
 /* concat string to sh_string
  */
-sh_string * sh_string_cat_lchar(sh_string * s, char * str, size_t len);
+sh_string * sh_string_cat_lchar(sh_string * s, const char * str, size_t len);
+
+/* add char array to end of string */
+sh_string * sh_string_add_from_char(sh_string * s, const char * str);
 
 /* set sh_string from string
  */
-sh_string * sh_string_set_from_char(sh_string * s, char * str);
+sh_string * sh_string_set_from_char(sh_string * s, const char * str);
 
 /* create new sh_string from array of given length
  */
-sh_string * sh_string_new_from_lchar(char * str, size_t len);
+sh_string * sh_string_new_from_lchar(const char * str, size_t len);
 
-#define sh_string_copy(a) ((a) ? sh_string_new_from_lchar(((a)->str), ((a)->len)) : NULL)
+#define sh_string_copy(a)  ((a) ? sh_string_new_from_lchar(((a)->str), ((a)->len)) : NULL)
+#define sh_string_add(a,b) ((a && b) ? sh_string_add_from_lchar((a), ((b)->str), ((b)->len)) : NULL)
 
 /* create new sh_string from three arrays of given length
  */
-sh_string * sh_string_new_from_lchar3(char * str1, size_t len1,
-                                      char * str2, size_t len2,
-                                      char * str3, size_t len3);
+sh_string * sh_string_new_from_lchar3(const char * str1, size_t len1,
+                                      const char * str2, size_t len2,
+                                      const char * str3, size_t len3);
 
 /* Truncate to desired length.
@@ -45,3 +49,37 @@
 sh_string * sh_string_grow(sh_string * s, size_t increase);
 
+/* Read a string from a file, with maxlen. Return 0 on EOF,
+ * -1 on error, and -2 if a line exceeds maxlen.
+ */
+size_t sh_string_read(sh_string * s, FILE * fp, size_t maxlen);
+
+/* Split array at delim in at most nfields fields. 
+ * Empty fields are returned as empty (zero-length) strings. 
+ * Leading and trailing WS are removed from token. 
+ * The number of fields is returned in 'nfields', their
+ * lengths in 'lengths'.
+ * A single delimiter will return two empty fields.
+ */
+char ** split_array(char *line, unsigned int * nfields, 
+                    char delim, size_t * lengths);
+
+/* Split array at whitespace in at most nfields fields.
+ * Multiple whitespaces are collapsed. 
+ * Empty fields are returned as empty (zero-length) strings.
+ * The number of fields is returned in nfields.
+ * An empty string will return zero fields.
+ * If nfields < actual fields, last string will be remainder.
+ */
+char ** split_array_ws(char *line, unsigned int * nfields, size_t * lengths);
+
+/* Replaces fields in s with 'replacement'. Fields are given
+ * in the ordered array ovector, comprising ovecnum pairs 
+ * ovector[i], ovector[i+1] which list offset of first char
+ * of field, offset of first char after field (this is how
+ * the pcre library does it).
+ */  
+sh_string * sh_string_replace(const sh_string * s, 
+                              const int * ovector, int ovecnum, 
+                              const char * replacement, size_t rlen);
+
 #endif
Index: trunk/include/sh_unix.h
===================================================================
--- trunk/include/sh_unix.h	(revision 182)
+++ trunk/include/sh_unix.h	(revision 183)
@@ -243,5 +243,5 @@
 /* check a file 
  */
-int tf_trust_check (char * file, int mode);
+int tf_trust_check (const char * file, int mode);
 
 /* initialize group vector
Index: trunk/include/slib.h
===================================================================
--- trunk/include/slib.h	(revision 182)
+++ trunk/include/slib.h	(revision 183)
@@ -405,9 +405,9 @@
   /* Check whether file is trustworthy.
    */
-  int sl_trustfile(char * path, uid_t * ok, uid_t * bad);
+  int sl_trustfile(const char * path, uid_t * ok, uid_t * bad);
 
   /* Check whether file is trustworthy.
    */
-  int sl_trustfile_euid(char * filename, uid_t euid);
+  int sl_trustfile_euid(const char * filename, uid_t euid);
 
   /* purge list of trusted users
Index: trunk/src/sh_cat.c
===================================================================
--- trunk/src/sh_cat.c	(revision 182)
+++ trunk/src/sh_cat.c	(revision 183)
@@ -97,6 +97,6 @@
 #ifdef SH_USE_KERN
   /* FreeBSD */
-  { MSG_KERN_POLICY, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY KERNEL BSD syscall table: new: %#lx old: %#lx\" syscall=\"%03d %s\"") },
-  { MSG_KERN_POL_CO, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY KERNEL BSD syscall code: new: %#x,%#x old: %#x,%#x\" syscall=\"%03d %s\"") },
+  { MSG_KERN_POLICY, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Kernel] BSD syscall table: new: %#lx old: %#lx\" syscall=\"%03d %s\"") },
+  { MSG_KERN_POL_CO, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Kernel] BSD syscall code: new: %#x,%#x old: %#x,%#x\" syscall=\"%03d %s\"") },
 
   /* Linux */
@@ -143,10 +143,20 @@
   { MSG_MNT_CHECK,   SH_ERR_INFO,    RUN,   N_("msg=\"Checking mounts\"")},
   { MSG_MNT_MEMLIST, SH_ERR_ERR,     RUN,   N_("msg=\"Cannot read mount list from memory\"")},
-  { MSG_MNT_MNTMISS, SH_ERR_WARN,    EVENT, N_("msg=\"Mount missing\" path=\"%s\"")},
-  { MSG_MNT_OPTMISS, SH_ERR_WARN,    EVENT, N_("msg=\"Mount option missing\" path=\"%s\" option=\"%s\"")},
+  { MSG_MNT_MNTMISS, SH_ERR_WARN,    EVENT, N_("msg=\"POLICY [Mounts] Mount missing\" path=\"%s\"")},
+  { MSG_MNT_OPTMISS, SH_ERR_WARN,    EVENT, N_("msg=\"POLICY [Mounts] Mount option missing\" path=\"%s\" option=\"%s\"")},
 #endif
 
 #ifdef SH_USE_USERFILES
   { MSG_USERFILES_SUMMARY,SH_ERR_INFO,    RUN,   N_("msg=\"Checked for users files\"") },
+#endif
+
+#ifdef USE_LOGFILE_MONITOR
+  { MSG_LOGMON_CHKS, SH_ERR_INFO,    RUN,   N_("msg=\"Checking logfile %s\"") },
+  { MSG_LOGMON_CHKE, SH_ERR_INFO,    RUN,   N_("msg=\"Finished logfile %s, %lu new records processed\"") },
+  { MSG_LOGMON_MISS, SH_ERR_ERR,     RUN,   N_("msg=\"Missing logfile %s\"") },
+  { MSG_LOGMON_EOPEN,SH_ERR_ERR,     RUN,   N_("msg=\"Cannot open logfile %s\"") },
+  { MSG_LOGMON_EREAD,SH_ERR_ERR,     RUN,   N_("msg=\"Error while reading logfile %s\"") },
+  { MSG_LOGMON_REP,  SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] %s\" time=\"%s\" host=\"%s\" path=\"%s\"") },
+  { MSG_LOGMON_SUM,  SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] %s\" host=\"%s\" path=\"%s\"") },
 #endif
 
@@ -413,6 +423,6 @@
 
 #ifdef SH_USE_KERN
-  { MSG_KERN_POLICY, SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY KERNEL BSD syscall table: new: %#lx old: %#lx>, syscall=<%03d %s>") },
-  { MSG_KERN_POL_CO, SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY KERNEL BSD syscall code: new: %#x,%#x old: %#x,%#x>, syscall=<%03d %s>") },
+  { MSG_KERN_POLICY, SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Kernel] BSD syscall table: new: %#lx old: %#lx>, syscall=<%03d %s>") },
+  { MSG_KERN_POL_CO, SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Kernel] BSD syscall code: new: %#x,%#x old: %#x,%#x>, syscall=<%03d %s>") },
 
   { MSG_KERN_SYSCALL,SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Kernel] SYSCALL modified> syscall=<%03d %s>, %s") },
@@ -461,10 +471,20 @@
   { MSG_MNT_CHECK,   SH_ERR_INFO,    RUN,   N_("msg=<Checking mounts>")},
   { MSG_MNT_MEMLIST, SH_ERR_ERR,     RUN,   N_("msg=<Cannot read mount list from memory>")},
-  { MSG_MNT_MNTMISS, SH_ERR_WARN,    EVENT, N_("msg=<Mount missing>, path=<%s>")},
-  { MSG_MNT_OPTMISS, SH_ERR_WARN,    EVENT, N_("msg=<Mount option missing>, path=<%s>, option=<%s>")},
+  { MSG_MNT_MNTMISS, SH_ERR_WARN,    EVENT, N_("msg=<POLICY [Mounts] Mount missing>, path=<%s>")},
+  { MSG_MNT_OPTMISS, SH_ERR_WARN,    EVENT, N_("msg=<POLICY [Mounts] Mount option missing>, path=<%s>, option=<%s>")},
 #endif
 
 #ifdef SH_USE_USERFILES
   { MSG_USERFILES_SUMMARY,SH_ERR_INFO,    RUN,   N_("msg=<Checked for users files>") },
+#endif
+
+#ifdef USE_LOGFILE_MONITOR
+  { MSG_LOGMON_CHKS, SH_ERR_INFO,    RUN,   N_("msg=<Checking logfile %s>") },
+  { MSG_LOGMON_CHKE, SH_ERR_INFO,    RUN,   N_("msg=<Finished logfile %s, %lu new records processed>") },
+  { MSG_LOGMON_MISS, SH_ERR_ERR,     RUN,   N_("msg=<Missing logfile %s>") },
+  { MSG_LOGMON_EOPEN,SH_ERR_ERR,     RUN,   N_("msg=<Cannot open logfile %s>") },
+  { MSG_LOGMON_EREAD,SH_ERR_ERR,     RUN,   N_("msg=<Error while reading logfile %s>") },
+  { MSG_LOGMON_REP,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] %s> time=<%s> host=<%s> path=<%s>") },
+  { MSG_LOGMON_SUM,  SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [Logfile] %s> host=<%s> path=<%s>") },
 #endif
 
Index: trunk/src/sh_getopt.c
===================================================================
--- trunk/src/sh_getopt.c	(revision 182)
+++ trunk/src/sh_getopt.c	(revision 183)
@@ -535,4 +535,8 @@
   if (num > 0) fputc (',', stdout);
   fputs (_(" ports"), stdout); ++num;
+#endif
+#ifdef USE_LOGFILE_MONITOR
+  if (num > 0) fputc (',', stdout);
+  fputs (_(" logfile monitor"), stdout); ++num;
 #endif
   if (num == 0)
Index: trunk/src/sh_log_check.c
===================================================================
--- trunk/src/sh_log_check.c	(revision 183)
+++ trunk/src/sh_log_check.c	(revision 183)
@@ -0,0 +1,1089 @@
+
+#include "config_xor.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef USE_LOGFILE_MONITOR
+
+#undef  FIL__
+#define FIL__  _("sh_log_check.c")
+
+/* Debian/Ubuntu: libpcre3-dev */
+#include <pcre.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+#include "sh_log_check.h"
+#include "sh_log_evalrule.h"
+
+/* List of supported logfile types, format is
+ * { "TYPE_CODE", Reader_Callback_Function, Parser_Callback_function }
+ * If Reader_Callback_Function is NULL, the default (line-oriented
+ * text file) reader is used.
+ */
+struct sh_logfile_type sh_logtypes_def[] = {
+    {  "SYSLOG", NULL,          sh_parse_syslog, NULL },
+    {  "APACHE", NULL,          sh_parse_apache, sh_eval_fileinfo_apache },
+#if defined(HAVE_SYS_ACCT_H)
+    {  "PACCT",  sh_read_pacct, sh_parse_pacct,  NULL },
+#endif
+};
+
+/* -------------------------- Internal Stuff -------------------------- */
+
+struct logfile_record {
+  dev_t  device_id;
+  ino_t  inode;
+  fpos_t offset;
+  /* FIXME include filename hash */
+};
+
+const char * save_dir = NULL;
+
+static char * build_path (struct sh_logfile * record)
+{
+  size_t plen;
+  int    retval;
+  char * path = NULL;
+
+  if (!save_dir)
+    {
+      save_dir = sh_util_strdup(DEFAULT_PIDDIR);
+
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      retval = tf_trust_check (save_dir, SL_YESPRIV);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      if (retval != 0)
+	{
+	  return(NULL);
+	}
+    }
+
+  plen = strlen(save_dir);
+
+  if (SL_TRUE == sl_ok_adds(plen, 129))
+    {
+      plen += 129; /* 64 + 64 + 1 */
+      path = SH_ALLOC(plen);
+      (void) sl_snprintf(path, plen, "%s/%lu_%lu", save_dir,
+			 (unsigned long) record->device_id, 
+			 (unsigned long) record->inode);
+    }
+
+  return path;
+}
+
+static void save_pos (struct sh_logfile * record)
+{
+  char * path;
+  FILE * fd;
+  struct logfile_record save_rec;
+
+  path = build_path(record);
+
+  if (path)
+    {
+      fd = fopen(path, "wb");
+      if (fd)
+	{
+	  save_rec.device_id = record->device_id;
+	  save_rec.inode     = record->inode;
+	  memcpy(&(save_rec.offset), &(record->offset), sizeof(fpos_t));
+	  if (1 != fwrite(&save_rec, sizeof(struct logfile_record), 1, fd))
+	    {
+	      (void) fclose(fd);
+	      (void) remove(path);
+	    }
+	  else
+	    {
+	      (void) fclose(fd);
+	    }
+	}
+      SH_FREE(path);
+    }
+  return;
+}
+
+static int read_pos (struct sh_logfile * record)
+{
+  int    retval = 0;
+  char * path;
+  FILE * fd;
+  struct logfile_record save_rec;
+
+  path = build_path(record);
+
+  if (path)
+    {
+      fd = fopen(path, "rb");
+      if (fd)
+	{
+	  if (1 == fread(&save_rec, sizeof(struct logfile_record), 1, fd))
+	    {
+	      if (save_rec.device_id == record->device_id &&
+		  save_rec.inode     == record->inode)
+		{
+		  memcpy(&(record->offset),&(save_rec.offset),sizeof(fpos_t));
+		  retval = 1;
+		}
+	    }
+	  (void) fclose(fd);
+	}
+      SH_FREE(path);
+    }
+  return retval;
+}
+
+/*@null@*/ static struct sh_logfile * sh_watched_logs = NULL;
+
+int sh_add_watch (const char * str)
+{
+  char * filename;
+
+  unsigned int    i;
+  unsigned int    defsize;
+  struct sh_logfile_type * log_type = NULL;
+  struct sh_logfile * thisfile;
+  struct stat buf;
+
+  unsigned int nfields = 3; /* logtype:path[:regex] */
+  size_t       lengths[3];
+  char *       new = sh_util_strdup(str);
+  char **      splits = split_array(new, &nfields, ':', lengths);
+
+  if (nfields < 2 || (lengths[0] == 0 || lengths[0] >= SH_MAX_LCODE_SIZE || lengths[1] == 0))
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Format error: "));
+      sh_string_add_from_char(msg, str);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("sh_add_watch"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      sh_string_destroy(&msg);
+
+      SH_FREE(new);
+      return -2;
+    }
+
+  defsize = 
+    (unsigned int) (sizeof(sh_logtypes_def)/sizeof(struct sh_logfile_type));
+
+  for (i = 0; i < defsize; ++i)
+    {
+      if (0 == strcmp(splits[0], sh_logtypes_def[i].code))
+	{
+	  log_type = &(sh_logtypes_def[i]);
+	  break;
+	}
+    }
+
+  if (log_type == NULL)
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Unsupported log type: "));
+      sh_string_add_from_char(msg, splits[0]);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("sh_add_watch"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      sh_string_destroy(&msg);
+
+      SH_FREE(new);
+      return -3;
+    }
+
+  if (splits[1][0] != '/')
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Logfile path not absolute: "));
+      sh_string_add_from_char(msg, splits[1]);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("sh_add_watch"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      sh_string_destroy(&msg);
+
+      SH_FREE(new);
+      return -4;
+    }
+
+  filename = /*@i@*/sh_util_strdup(splits[1]);
+  thisfile = SH_ALLOC(sizeof(struct sh_logfile));
+
+  thisfile->filename     = filename;
+  thisfile->flags        = SH_LOGFILE_REWIND;
+  thisfile->inode        = 0;
+  thisfile->device_id    = 0;
+  thisfile->fp           = NULL;
+  if (log_type->get_record)
+    thisfile->get_record   = log_type->get_record;
+  else
+    thisfile->get_record   = sh_default_reader;
+  thisfile->parse_record = log_type->parse_record;
+
+  /* An optional regex for parsing the file. The result
+   * 'fileinfo' should contain info about host/time position.
+   */
+  if (log_type->eval_fileinfo)
+    {
+      if (nfields == 3 && lengths[2] > 0)
+	{
+	  thisfile->fileinfo     = log_type->eval_fileinfo(splits[2]);
+
+	  if (thisfile->fileinfo == NULL)
+	    {
+	      sh_string * msg =  sh_string_new(0);
+	      sh_string_add_from_char(msg, _("Logfile format description not recognized: "));
+	      sh_string_add_from_char(msg, splits[2]);
+	      
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
+	      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+			      sh_string_str(msg),
+			      _("sh_add_watch"));
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	      sh_string_destroy(&msg);
+
+	      SH_FREE(filename);
+	      SH_FREE(thisfile);
+	      SH_FREE(new);
+	      return -1;
+	    }
+	}
+      else
+	{
+	  sh_string * msg =  sh_string_new(0);
+	  sh_string_add_from_char(msg, _("Logfile format description missing: "));
+	  sh_string_add_from_char(msg, splits[1]);
+	  
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+			  sh_string_str(msg),
+			  _("sh_add_watch"));
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	  sh_string_destroy(&msg);
+
+	  SH_FREE(filename);
+	  SH_FREE(thisfile);
+	  SH_FREE(new);
+	  return -1;
+	}
+    }
+  else
+    {
+      thisfile->fileinfo     = NULL;
+    }
+  thisfile->next         = sh_watched_logs;
+
+  /* Try reading saved offset. On success clear rewind flag.
+   */
+  if (0 == stat(thisfile->filename, &buf))
+    {
+      thisfile->inode     = buf.st_ino;
+      thisfile->device_id = buf.st_dev;
+      
+      if (0 != read_pos(thisfile))
+	{
+	  thisfile->flags &= ~SH_LOGFILE_REWIND;
+	}
+    }
+
+  sh_watched_logs        = thisfile;
+
+  SH_FREE(new);
+  return 0;
+}
+
+void sh_dump_watches()
+{
+  struct sh_logfile * thisfile;
+
+  while (sh_watched_logs)
+    {
+      thisfile        = sh_watched_logs;
+      sh_watched_logs = thisfile->next;
+
+      save_pos(thisfile);
+
+      if (thisfile->fp)
+	fclose(thisfile->fp);
+      if (thisfile->filename)
+	SH_FREE(thisfile->filename);
+      SH_FREE(thisfile);
+    }
+  return;
+}
+
+/* This variable is not used anywhere. It only exist
+ * to assign &new to them, which keeps gcc from
+ * putting it into a register, and avoids the 'clobbered
+ * by longjmp' warning. And no, 'volatile' proved insufficient.
+ */
+static void * sh_dummy_thisfile = NULL;
+
+void sh_check_watches()
+{
+  static size_t      count = 0;
+  struct sh_logrecord * logrecord;
+  struct sh_logfile * thisfile = sh_watched_logs;
+  sh_string * record = sh_string_new(0);
+  char * tmp;
+
+  /* Take the address to keep gcc from putting them into registers. 
+   * Avoids the 'clobbered by longjmp' warning. 
+   */
+  sh_dummy_thisfile = (void*) &thisfile;
+
+  while (thisfile)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      tmp = sh_util_safe_name (thisfile->filename);
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_CHKS,
+		      tmp);
+      SH_FREE(tmp);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      for (;;) {
+
+	record = thisfile->get_record(record, thisfile);
+
+	if (record)
+	  {
+	    logrecord = thisfile->parse_record(record, thisfile->fileinfo);
+	    ++count;
+
+	    if (logrecord)
+	      {
+		logrecord->filename = thisfile->filename;
+
+		sh_eval_process_msg(logrecord);
+
+		if (logrecord->message) 
+		  sh_string_destroy(&(logrecord->message));
+		if (logrecord->host)
+		  sh_string_destroy(&(logrecord->host));
+		if (logrecord->prefix)
+		  sh_string_destroy(&(logrecord->prefix));
+		SH_FREE(logrecord);
+	      }
+	  }
+	else
+	  {
+	    (void) sh_string_truncate(record, 0);
+	    break;
+	  }
+      }
+
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      tmp = sh_util_safe_name (thisfile->filename);
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_CHKE,
+		      tmp, (unsigned long)count);
+      SH_FREE(tmp);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      thisfile = thisfile->next;
+    }
+  sh_string_destroy(&record);
+  return;
+}
+
+/********************************************************
+ * Search rotated logfile
+ */
+#include <unistd.h>
+#include <libgen.h>
+#include <dirent.h>
+
+char * sh_rotated_log_search(const char * path, struct stat * buf)
+{
+
+  size_t size;
+  int    i;
+  char * searchpath;
+  struct stat sbuf;
+  DIR  * dp;
+  char * dname;
+  char * bname;
+
+  dname  = sh_util_dirname(path);
+  bname  = sh_util_basename(path);
+
+  size = strlen(dname) + strlen(bname) + 4;
+  searchpath = SH_ALLOC(size);
+
+  for (i = 0; i < 2; ++i)
+    {
+      snprintf(searchpath, size, "%s/%s.%1d", dname, bname, i);
+      if (0 == stat(searchpath, &sbuf) && sbuf.st_ino == buf->st_ino)
+	{
+	  SH_FREE(dname);
+	  SH_FREE(bname);
+	  return searchpath;
+	}
+    }
+
+  SH_FREE(searchpath);
+
+  if (NULL != (dp = opendir(dname)))
+    {
+      struct dirent * de;
+
+      while (NULL != (de = readdir(dp)))
+	{
+	  if (0 == strcmp(de->d_name, ".") || 0 == strcmp(de->d_name, ".."))
+	    continue;
+
+	  size = strlen(dname) + strlen(de->d_name) + 2;
+	  searchpath = SH_ALLOC(size);
+	  snprintf(searchpath, size, "%s/%s", dname, de->d_name);
+
+	  if (0 == stat(searchpath, &sbuf) && sbuf.st_ino == buf->st_ino)
+	    {
+	      SH_FREE(dname);
+	      SH_FREE(bname);
+	      closedir(dp);
+	      return searchpath;
+	    }
+	  
+	  SH_FREE(searchpath);
+	}
+      closedir(dp);
+    }
+
+  SH_FREE(dname);
+  SH_FREE(bname);
+
+  return NULL;
+}
+
+/* Open file, position at stored offset
+ */
+int sh_open_for_reader (struct sh_logfile * logfile)
+{
+  struct stat buf;
+  sh_string * filename;
+
+  /* check whether file exists, get inode to check for
+   * logfile rotation
+   */
+  if (0 != retry_stat(FIL__, __LINE__, logfile->filename, &buf))
+    {
+      char * tmp;
+
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      tmp = sh_util_safe_name (logfile->filename);
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_MISS,
+		      tmp);
+      SH_FREE(tmp);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      memset (&(logfile->offset), '\0', sizeof(fpos_t));
+      logfile->flags |= SH_LOGFILE_REWIND;
+      return 0;
+    }
+
+  filename = sh_string_new(0);
+  (void) sh_string_set_from_char (filename, logfile->filename);
+
+  /* detect and handle logfile rotation
+   */
+  if (logfile->inode != buf.st_ino && logfile->inode != 0)
+    {
+      /* Case 1) We have dealt with the moved file already.
+       *         Clear the moved flag, set the rewind flag,
+       *         fix logfile->inode.
+       */
+      if ((logfile->flags & SH_LOGFILE_MOVED) != 0)
+	{
+	  /* done with rotated file, start with current file
+	   */
+	  memset (&(logfile->offset), '\0', sizeof(fpos_t));
+	  logfile->flags    |= SH_LOGFILE_REWIND;
+	  logfile->flags    &= ~SH_LOGFILE_MOVED;
+	  logfile->inode     = buf.st_ino;
+	  logfile->device_id = buf.st_dev;
+	}
+
+      /* Case 2) Searching for rotated file. 
+       *         If found:     set the moved flag, fix path for fopen.
+       *         If not found: set the rewind flag, fix logfile->inode.
+       */
+      else
+	{
+	  char *oldfile = sh_rotated_log_search(logfile->filename, &buf);
+
+	  if (NULL != oldfile)
+	    {
+	      (void) sh_string_set_from_char (filename, oldfile);
+	      SH_FREE(oldfile);
+	      logfile->flags |= SH_LOGFILE_MOVED;
+	    }
+	  else
+	    {
+	      memset (&(logfile->offset), '\0', sizeof(fpos_t));
+	      logfile->flags    |= SH_LOGFILE_REWIND;
+	      logfile->inode     = buf.st_ino;
+	      logfile->device_id = buf.st_dev;
+	    }
+	}
+    }
+
+  /* open file
+   */
+  logfile->fp = fopen(filename->str, "r");
+  if (!logfile->fp)
+    {
+      char * tmp;
+
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      tmp = sh_util_safe_name (logfile->filename);
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EOPEN,
+		      tmp);
+      SH_FREE(tmp);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      sh_string_destroy(&filename);
+      return 0;
+    }
+
+  sh_string_destroy(&filename);
+  
+  if ((logfile->flags & SH_LOGFILE_REWIND) != 0)
+    {
+      rewind(logfile->fp);
+      fgetpos(logfile->fp, &(logfile->offset));
+      logfile->flags &= ~SH_LOGFILE_REWIND;
+    }
+  else
+    {
+      /* file too short
+       */
+      if (0 != fsetpos(logfile->fp, &(logfile->offset)))
+	{
+	  rewind(logfile->fp);
+	  fgetpos(logfile->fp, &(logfile->offset));
+	}
+    }
+
+  return 1;
+}
+
+/******************************************************
+ *  Default reader for ascii text files 
+ */
+sh_string * sh_default_reader (sh_string * s, struct sh_logfile * logfile)
+{
+  int         status;
+  char * tmp;
+
+ start_read:
+
+  if (logfile->fp)
+    {
+      /* Result cannot be larger than 8192, thus cast is ok
+       */
+      status = (int) sh_string_read(s, logfile->fp, 8192);
+      if (status <= 0)
+	{
+	  fgetpos(logfile->fp, &(logfile->offset));
+	  fclose(logfile->fp);
+	  logfile->fp = NULL;
+	  sh_string_destroy(&s);
+	  if (status == 0)
+	    {
+	      return NULL;
+	    }
+
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  tmp = sh_util_safe_name (logfile->filename);
+	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
+			  tmp);
+	  SH_FREE(tmp);
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+	  return NULL;
+	}
+      return s;
+    }
+
+  if (0 != sh_open_for_reader(logfile))
+    goto start_read;
+
+  return NULL;
+}
+
+/******************************************************
+ *  Reader for binary files 
+ */
+sh_string * sh_binary_reader (void * s, size_t size, struct sh_logfile * logfile)
+{
+  size_t         status;
+
+ start_read:
+
+  if (logfile->fp)
+    {
+
+      status = fread(s, size, 1, logfile->fp);
+
+      if (status != 1)
+	{
+	  if (ferror(logfile->fp))
+	    {
+	      char * tmp;
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
+	      tmp = sh_util_safe_name (logfile->filename);
+	      sh_error_handle((-1), FIL__, __LINE__, errno, MSG_LOGMON_EREAD,
+			      tmp);
+	      SH_FREE(tmp);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	    }
+	  fgetpos(logfile->fp, &(logfile->offset));
+	  fclose(logfile->fp);
+	  logfile->fp = NULL;
+	  memset(s, '\0', size);
+	  return NULL;
+	}
+      return s;
+    }
+
+  if (0 != sh_open_for_reader(logfile))
+    goto start_read;
+
+  return NULL;
+}
+
+
+
+/**********************************************************
+ *
+ * Utilities
+ *
+ **********************************************************/
+
+time_t conv_timestamp (struct tm * btime, 
+		       struct tm * old_tm, time_t * old_time)
+{
+  time_t timestamp;
+  long   offtime;
+
+  /* timestamp - mktime is slooow, thus cache result
+   */
+  if (btime->tm_isdst == old_tm->tm_isdst &&
+      btime->tm_year  == old_tm->tm_year  &&
+      btime->tm_mon   == old_tm->tm_mon   &&
+      btime->tm_mday  == old_tm->tm_mday)
+    {
+      offtime = 
+	(btime->tm_hour - old_tm->tm_hour) * 3600 +
+	(btime->tm_min  - old_tm->tm_min)  * 60   +
+	(btime->tm_sec  - old_tm->tm_sec);
+      *old_time += offtime;
+      memcpy(old_tm, btime, sizeof(struct tm));
+      timestamp = *old_time;
+    }
+  else
+    {
+      timestamp = mktime(btime);
+      *old_time  = timestamp;
+      memcpy(old_tm, btime, sizeof(struct tm));
+    }
+  return timestamp;
+}
+
+/*********************************************************
+ *
+ * MODULE STUFF
+ *
+ *********************************************************/
+#include "sh_modules.h"
+
+SH_MUTEX_STATIC(mutex_logmon_check, PTHREAD_MUTEX_INITIALIZER);
+
+static int ShLogmonActive        = S_FALSE;
+#define SH_LOGMON_INTERVAL 10
+static time_t sh_logmon_interval = SH_LOGMON_INTERVAL;
+
+int sh_log_check_init (struct mod_type * arg)
+{
+  if (ShLogmonActive == S_FALSE)
+    return SH_MOD_FAILED;
+#ifdef HAVE_PTHREAD
+  if (arg != NULL && arg->initval < 0 &&
+      (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
+    {
+      if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
+	return SH_MOD_THREAD;
+      else
+	return SH_MOD_FAILED;
+    }
+#endif
+  if (sh_watched_logs != NULL)
+    return 0;
+
+  return -1;
+}
+
+int sh_log_check_timer(time_t tcurrent) 
+{
+  static time_t lastcheck = 0;
+
+  SL_ENTER(_("sh_log_check_timer"));
+  if ((time_t) (tcurrent - lastcheck) >= sh_logmon_interval)
+    {
+      lastcheck  = tcurrent;
+      SL_RETURN((-1), _("sh_log_check_timer"));
+    }
+  SL_RETURN(0, _("sh_log_check_timer"));
+}
+
+
+int sh_log_check_check(void) 
+{
+  int status = 0;
+
+  SL_ENTER(_("sh_log_check_check"));
+
+  SH_MUTEX_LOCK(mutex_logmon_check);
+
+  status = 0;
+
+  if( ShLogmonActive != S_FALSE )
+    {
+      sh_check_watches();
+    }
+  SH_MUTEX_UNLOCK(mutex_logmon_check);
+
+  SL_RETURN(status, _("sh_log_check_check"));
+}
+
+int sh_log_check_reconf(void) 
+{
+  int status = 0;
+
+  SL_ENTER(_("sh_log_check_check"));
+
+  SH_MUTEX_LOCK(mutex_logmon_check);
+
+  ShLogmonActive     = S_FALSE;
+  sh_logmon_interval = SH_LOGMON_INTERVAL;
+  sh_dump_watches();
+  sh_eval_cleanup();
+
+  SH_MUTEX_UNLOCK(mutex_logmon_check);
+
+  SL_RETURN(status, _("sh_log_check_check"));
+}
+
+int sh_log_check_cleanup(void) 
+{
+  return sh_log_check_reconf();
+}
+
+/*********************  OPTIONS **********************/
+
+static int sh_logmon_set_active  (const char *str);
+static int sh_logmon_set_interval(const char *str);
+static int sh_logmon_add_watch (const char * str);
+static int sh_logmon_add_group (const char * str);
+static int sh_logmon_end_group (const char * str);
+static int sh_logmon_add_host  (const char * str);
+static int sh_logmon_end_host  (const char * str);
+static int sh_logmon_add_queue (const char * str);
+static int sh_logmon_add_rule  (const char * str);
+extern int sh_set_hidepid(const char *s);
+
+sh_rconf sh_log_check_table[] = {
+    {
+        N_("logmonactive"),
+        sh_logmon_set_active,
+    },
+    {
+        N_("logmoninterval"),
+        sh_logmon_set_interval,
+    },
+    {
+        N_("logmonwatch"),
+        sh_logmon_add_watch,
+    },
+    {
+        N_("logmonqueue"),
+        sh_logmon_add_queue,
+    },
+    {
+        N_("logmongroup"),
+        sh_logmon_add_group,
+    },
+    {
+        N_("logmonendgroup"),
+        sh_logmon_end_group,
+    },
+    {
+        N_("logmonhost"),
+        sh_logmon_add_host,
+    },
+    {
+        N_("logmonendhost"),
+        sh_logmon_end_host,
+    },
+    {
+        N_("logmonrule"),
+        sh_logmon_add_rule,
+    },
+    {
+        N_("logmonhidepid"),
+        sh_set_hidepid,
+    },
+    {
+        NULL,
+        NULL
+    }
+};
+
+/* Decide if we're active.
+ */
+static int sh_logmon_set_active(const char *str) 
+{
+  int value;
+    
+  SL_ENTER(_("sh_logmon_set_active"));
+
+  value = sh_util_flagval(str, &ShLogmonActive);
+
+  SL_RETURN((value), _("sh_logmon_set_active"));
+}
+
+static int sh_logmon_set_interval (const char * c)
+{
+  int retval = 0;
+  long val;
+
+  SL_ENTER(_("sh_logmon_set_interval"));
+  val = strtol (c, (char **)NULL, 10);
+  if (val <= 0)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
+		       _("log monitoring interval"), c);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      retval = -1;
+    }
+
+  sh_logmon_interval = (time_t) val;
+  SL_RETURN(0, _("sh_logmon_set_interval"));
+}
+
+/* Add a watch on a logfile.
+ * Format: TYPE : Filename [: File_Format]
+ */
+static int sh_logmon_add_watch (const char * str)
+{
+  return sh_add_watch(str);
+}
+
+/* Add a host.
+ * Format: Name_Regex
+ */
+static int sh_logmon_add_host (const char * str)
+{
+  return sh_eval_hadd(str);
+}
+
+/* End a host.
+ * Format: Name
+ */
+static int sh_logmon_end_host (const char * str)
+{
+  (void) str;
+  return sh_eval_hend(NULL);
+}
+
+/* Add a group of rules. 
+ * Groups can be under hosts, but not vice versa.
+ * Format: Name : Prefix_Regex
+ */
+static int sh_logmon_add_group (const char * str)
+{
+  return sh_eval_gadd(str);
+}
+
+/* End a group of rules.
+ * Format: Name
+ */
+static int sh_logmon_end_group (const char * str)
+{
+  (void) str;
+  return sh_eval_gend(NULL);
+}
+
+/* Define a reporting queue.
+ * Format: Label : [Interval] : TYPE : Severity
+ * TYPE must be 'report' or 'sum'
+ * Interval is ignored for TYPE='report'
+ */
+static int sh_logmon_add_queue (const char * str)
+{
+  return sh_eval_qadd(str);
+}
+
+/* Define a check rule.
+ * Format: Queue_Label : Regex
+ * TYPE must be 'report' or 'sum'
+ */
+static int sh_logmon_add_rule (const char * str)
+{
+  return sh_eval_radd(str);
+}
+
+
+#if 0
+
+/* >>>>>>>>>>>  MAIN <<<<<<<<<<<<<<<<<<< */
+
+int main (int argc, char * argv[])
+{
+  int status, i;
+  FILE * fp;
+  sh_string * s = NULL;
+  static char template[] = "/tmp/xtest.XXXXXX";
+
+  /* pacct */
+  status = sh_add_watch("PACCT:/var/log/account/pacct");
+  sh_check_watches();
+  sh_dump_watches();
+  exit(0);
+
+  /* apache log */
+  sh_eval_gadd("four_o_four:404");
+  sh_eval_qadd("test:1:sum:7");
+  sh_eval_radd("test:^(\\d+.\\d+.\\d+.\\d+).*");
+  sh_eval_gend(NULL);
+  sh_eval_radd("trash:.*");
+  status = sh_add_watch("APACHE:/var/log/apache2/access.log:combined");
+  sh_check_watches();
+  sh_dump_watches();
+  exit(0);
+
+  /* logfile */
+  sh_set_hidepid(1);
+  sh_eval_hadd("hslxmsrv1");
+  sh_eval_gadd("postfix:postfix");
+  sh_eval_qadd("test::report:7");
+  sh_eval_radd("test:postfix/smtpd: disconnect from localhost.*");
+  sh_eval_radd("trash:postfix/smtpd: disconnect.*");
+  sh_eval_hadd("hspc05");
+  sh_eval_gadd("cron:CRON");
+  sh_eval_qadd("test:1:sum:7");
+  sh_eval_radd("test:CRON: PAM adding faulty module: (/lib/security/.*.so)");
+  sh_eval_radd("trash:.*");
+  status = sh_add_watch("SYSLOG:/var/log/messages");
+  sh_check_watches();
+
+  sh_dump_watches();
+  exit(0);
+
+  printf("%d types\n",
+	 (int) (sizeof(sh_logtypes_def)/sizeof(struct sh_logfile_type)));
+
+  /* test sh_add_watch 
+   */
+  status = sh_add_watch("");
+  printf("%2d: zero length, expect -1\n", status);
+  status = sh_add_watch(NULL);
+  printf("%2d: NULL, expect -2\n", status);
+  status = sh_add_watch("0123456789012345:/var/log/messages");
+  printf("%2d: long, expect -2\n", status);
+  status = sh_add_watch("012345678901234:/var/log/messages");
+  printf("%2d: exact length, expect -3\n", status);
+  status = sh_add_watch("01234567890123:56789");
+  printf("%2d: short length, expect -3\n", status);
+  status = sh_add_watch("SYSLOG:var/log/messages");
+  printf("%2d: short badpath, expect -4\n", status);
+  status = sh_add_watch("SYSLOG:/var/log/messages");
+  // status = sh_add_watch("SYSLOG:/var/log/dpkg.log.1");
+  printf("%2d: short path ok, expect 0\n", status);
+
+  /* test sh_string_read 
+   */
+  s = sh_string_new();
+
+  status = /*@i@*/mkstemp(template);
+
+  if (status < 0) {
+    fprintf(stderr, "error in mkstemp!\n"); exit(EXIT_FAILURE); }
+
+  fp = fdopen(status, "r+");
+  if (!fp) {
+    fprintf(stderr, "error in fdopen!\n"); exit(EXIT_FAILURE); }
+
+  for (i = 0; i <  80; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 0
+  for (i = 0; i < 118; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 1
+  for (i = 0; i < 119; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 2
+  for (i = 0; i < 120; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 3
+  for (i = 0; i < 121; ++i) { fputc ('a', fp); } fputc ('\n', fp); // 4
+  for (i = 0; i < 238; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+  for (i = 0; i < 239; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+  for (i = 0; i < 240; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+  for (i = 0; i < 241; ++i) { fputc ('a', fp); } fputc ('\n', fp);
+
+  rewind(fp);
+
+  for (i = 0; i < 9; ++i)
+    {
+      status = (int) sh_string_read(s, fp, 120);
+      printf("%d: status = %d, len = %d, size = %d\n",
+	     i, status, (int)s->len, (int)s->siz);
+      if (status == -2)
+	(void) sh_string_read(s, fp, 240);
+      else
+	printf("%s\n", s->str);
+    }
+
+  rewind(fp);
+
+  (void) sh_string_truncate(s, 0);
+
+  for (i = 0; i < 9; ++i)
+    {
+      status = (int) sh_string_read(s, fp, 240);
+      printf("%d: status = %d, len = %d, size = %d\n",
+	     i, status, (int)s->len, (int)s->siz);
+      if (status == -2)
+	(void) sh_string_read(s, fp, 240);
+      else
+	{
+	  for (status = 0; status < (int)s->len; ++status)
+	    {
+	      if (s->str[status] != 'a')
+		{
+		  break;
+		}
+	    }
+	  printf("%d %s\n", status, s->str);
+	}
+    }
+
+  fclose(fp); remove(template);
+
+
+
+  return 0;
+}
+#endif
+
+/* #ifdef USE_LOGFILE_MONITOR */
+#endif
+
Index: trunk/src/sh_log_evalrule.c
===================================================================
--- trunk/src/sh_log_evalrule.c	(revision 183)
+++ trunk/src/sh_log_evalrule.c	(revision 183)
@@ -0,0 +1,867 @@
+
+#include "config_xor.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#ifdef USE_LOGFILE_MONITOR
+
+#undef  FIL__
+#define FIL__  _("sh_log_evalrule.c")
+
+/* Debian/Ubuntu: libpcre3-dev */
+#include <pcre.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+#include "sh_log_check.h"
+#include "sh_log_evalrule.h"
+#include "zAVLTree.h"
+
+/* #define DEBUG_EVALRULES */
+
+#ifdef DEBUG_EVALRULES
+void DEBUG(const char *fmt, ...)
+{
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap); /* flawfinder: ignore *//* we control fmt string */
+  va_end(ap);
+  return;
+}
+#else
+void DEBUG(const char *fmt, ...)
+{
+  (void) fmt;
+  return;
+}
+#endif
+
+enum policies {
+  EVAL_REPORT,
+  EVAL_SUM
+};
+
+struct sh_ceval    /* Counter for summarizing    */
+{
+  sh_string   * hostname;
+  sh_string   * counted_str;
+  sh_string   * filename;
+  unsigned long count;
+  time_t        start;
+  time_t        interval;
+};
+
+void sh_ceval_free(void * item)
+{
+  struct sh_ceval * counter = (struct sh_ceval *) item;
+  if (!counter)
+    return;
+  sh_string_destroy(&(counter->hostname));
+  sh_string_destroy(&(counter->counted_str));
+  sh_string_destroy(&(counter->filename));
+  SH_FREE(counter);
+}
+
+struct sh_qeval  /* Queue with definitions */
+{
+  sh_string       * label;
+  enum policies     policy;
+  int               severity;
+  time_t            interval;        /* if EVAL_SUM, interval   */ 
+  struct sh_qeval * next;
+};
+
+struct sh_geval  /* Group of rules (may be a single rule) */
+{
+  sh_string       * label;           /* label for this group    */
+  pcre            * rule;            /* compiled regex for rule */
+  pcre_extra      * rule_extra;
+  int             * ovector;         /* captured substrings     */
+  int               ovecnum;         /* how many captured       */
+  int               captures;        /* (captures+1)*3 required */
+  zAVLTree        * counterlist;     /* counters if EVAL_SUM    */
+  struct sh_qeval * queue;           /* queue for this rule     */
+  struct sh_geval * nextrule;        /* next rule in this group */
+  struct sh_geval * next;            /* next group of rules     */
+};
+
+struct sh_heval  /* host-specific rules */
+{
+  pcre            * hostname;        /* compiled regex for hostname */
+  pcre_extra      * hostname_extra;
+  struct sh_geval * rulegroups;      /* list of group of rules      */
+  struct sh_heval * next;
+};
+
+static struct sh_heval * hostlist  = NULL;
+static struct sh_qeval * queuelist = NULL;
+static struct sh_geval * grouplist = NULL;
+
+/* These flags are set if we are within 
+ * the define of a host/rule group.
+ */
+static struct sh_heval * host_open  = NULL;
+static struct sh_geval * group_open = NULL;
+
+int sh_eval_gend (const char * str)
+{
+  (void) str;
+  if (group_open) {
+    group_open = NULL;
+    return 0;
+  }
+  return -1;
+}
+
+int sh_eval_gadd (const char * str)
+{
+  struct sh_geval * ng;
+  struct sh_geval * tmp;
+  pcre *  group;
+  pcre_extra * group_extra;
+  const char * error;
+  int          erroffset;
+  unsigned int nfields = 2;
+  size_t       lengths[2];
+  char *       new = sh_util_strdup(str);
+  char **      splits = split_array(new, &nfields, ':', lengths);
+
+  if (group_open)
+    group_open = NULL;
+
+  if (nfields != 2)
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+
+  group = pcre_compile(splits[1], PCRE_NO_AUTO_CAPTURE, 
+		       &error, &erroffset, NULL);
+  if (!group)
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Bad regex: "));
+      sh_string_add_from_char(msg, splits[1]);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("sh_eval_gadd"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      sh_string_destroy(&msg);
+      
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+  group_extra = NULL; /* pcre_study(group, 0, &error); */
+
+  ng = SH_ALLOC(sizeof(struct sh_geval));
+  ng->label       = sh_string_new_from_lchar(splits[0], lengths[0]);
+  ng->rule        = group;
+  ng->rule_extra  = group_extra;
+  ng->ovector     = NULL;
+  ng->captures    = 0;
+  ng->counterlist = NULL;
+  ng->queue       = NULL;
+  ng->nextrule    = NULL;
+
+  /* 
+   * Insert at end, to keep user-defined order 
+   */ 
+  ng->next   = NULL;
+  if (grouplist) 
+    {
+      tmp = grouplist; 
+      while (tmp->next != NULL) { tmp = tmp->next; }
+      tmp->next = ng;
+    } else {
+      grouplist = ng;
+    }
+
+  if (!host_open)
+    {
+      if (0 != sh_eval_hadd("^.*"))
+	{
+	  pcre_free(group);
+	  sh_string_destroy(&(ng->label));
+	  SH_FREE(splits);
+	  SH_FREE(new);
+	  SH_FREE(ng);
+	  return -1;
+	}
+    }
+
+  /* 
+   * If there is an open host group, add it to its
+   * rulegroups
+   */
+  if (host_open)
+    {
+      if (host_open->rulegroups) 
+	{
+	  tmp = host_open->rulegroups; 
+	  while (tmp->next != NULL) { tmp = tmp->next; }
+	  tmp->next = ng;
+	} else {
+	  host_open->rulegroups = ng;
+	}
+    }
+
+  group_open = ng;
+  SH_FREE(splits);
+  SH_FREE(new);
+  return 0;
+}
+
+int sh_eval_hend (const char * str)
+{
+  (void) str;
+  if (host_open) {
+    host_open = NULL;
+    return 0;
+  }
+  return -1;
+}
+
+int sh_eval_hadd (const char * str)
+{
+  struct sh_heval * nh;
+  struct sh_heval * tmp;
+  pcre *  host;
+  pcre_extra * host_extra;
+  const char * error;
+  int          erroffset;
+
+  if (host_open)
+    host_open = NULL;
+
+  host = pcre_compile(str, PCRE_NO_AUTO_CAPTURE, 
+		      &error, &erroffset, NULL);
+  if (!host)
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Bad regex: "));
+      sh_string_add_from_char(msg, str);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("sh_eval_hadd"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      sh_string_destroy(&msg);
+
+      return -1;
+    }
+  host_extra = NULL; /* pcre_study(host, 0, &error); */
+
+  nh = SH_ALLOC(sizeof(struct sh_heval));
+  nh->hostname = host;
+  nh->hostname_extra = host_extra;
+  nh->rulegroups = NULL;
+
+  /* 
+   * Insert at end, to keep user-defined order 
+   */ 
+  nh->next = NULL;
+  if (hostlist) {
+    tmp = hostlist; 
+    while (tmp->next != NULL) { tmp = tmp->next; }
+    tmp->next = nh;
+  } else {
+    hostlist = nh;
+  }
+  host_open = nh;
+
+  return 0;
+}
+
+int sh_eval_qadd (const char * str)
+{
+  struct sh_qeval * nq;
+  int     severity;
+  unsigned int nfields = 4; /* label:interval:(report|sum):severity */
+  size_t  lengths[4];
+  char *  new = sh_util_strdup(str);
+  char ** splits = split_array(new, &nfields, ':', lengths);
+
+  if (nfields != 4)
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+
+  if (strcmp(splits[2], _("sum")) && strcmp(splits[2], _("report")))
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+
+  if (!strcmp(splits[2], _("sum")) && atoi(splits[1]) < 0)
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+  
+  severity = sh_error_convert_level (splits[3]);
+  if (severity < 0)
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+
+  nq = SH_ALLOC(sizeof(struct sh_qeval));
+  nq->label = sh_string_new_from_lchar(splits[0], lengths[0]);
+
+  DEBUG("debug: splits[2] = %s, policy = %d\n",splits[2],nq->policy); 
+  if (0 == strcmp(splits[2], _("report"))) {
+    nq->policy   = EVAL_REPORT;
+    nq->interval = 0;
+  }
+  else {
+    nq->policy   = EVAL_SUM;
+    nq->interval = (time_t) atoi(splits[1]);
+  }
+
+  nq->severity = severity;
+  nq->next     = queuelist;
+  queuelist    = nq;
+
+  SH_FREE(splits);
+  SH_FREE(new);
+  return 0;
+}
+
+static struct sh_qeval * find_queue(const char * str)
+{
+  struct sh_qeval * retval = queuelist;
+
+  if (!str)
+    return NULL;
+
+  while (retval)
+    {
+      if (0 == strcmp(str, sh_string_str(retval->label)))
+	break;
+      retval = retval->next;
+    }
+  return retval;
+}
+
+int sh_eval_radd (const char * str)
+{
+  struct sh_geval * nr;
+  struct sh_geval * tmp;
+  struct sh_qeval * queue = NULL;
+  pcre *  rule;
+  pcre_extra * rule_extra;
+  const char * error;
+  int          erroffset;
+  int          captures = 0;
+  unsigned int nfields = 2; /* queue:regex */
+  size_t       lengths[2];
+  char *       new = sh_util_strdup(str);
+  char **      splits = split_array(new, &nfields, ':', lengths);
+
+  if (nfields != 2)
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+
+  if (0 != strcmp(splits[0], _("trash")))
+      {
+	queue = find_queue(splits[0]);
+	if (!queue)
+	  {
+	    SH_FREE(splits);
+	    SH_FREE(new);
+	    return -1;
+	  }
+      }
+
+  rule = pcre_compile(splits[1], 0, 
+		      &error, &erroffset, NULL);
+  if (!rule)
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Bad regex: "));
+      sh_string_add_from_char(msg, splits[1]);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("sh_eval_radd"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      sh_string_destroy(&msg);
+
+      SH_FREE(splits);
+      SH_FREE(new);
+      return -1;
+    }
+  rule_extra = NULL; /* pcre_study(rule, 0, &error); */
+  pcre_fullinfo(rule, rule_extra, PCRE_INFO_CAPTURECOUNT, &captures);
+
+  DEBUG("adding rule: |%s| with %d captures\n", splits[1], captures);
+
+  SH_FREE(splits);
+  SH_FREE(new);
+
+  nr = SH_ALLOC(sizeof(struct sh_geval));
+  nr->label       = NULL;
+  nr->rule        = rule;
+  nr->rule_extra  = rule_extra;
+  nr->captures    = captures;
+  nr->ovector     = SH_ALLOC(sizeof(int) * (captures+1) * 3);
+  nr->counterlist = NULL;
+  nr->queue       = queue;
+  nr->nextrule    = NULL;
+  nr->next        = NULL;
+
+  /* 
+   * If there is an open group, add it to its
+   * rules
+   */
+  if (group_open)
+    {
+      if (group_open->nextrule) 
+	{
+	  tmp = group_open->nextrule; 
+	  while (tmp->next != NULL) { tmp = tmp->next; }
+	  tmp->next = nr;
+	} else {
+	  group_open->nextrule = nr;
+	}
+    }
+
+  /* 
+   * ..else, add it to the currently open host (open the
+   * default host, if there is no open one)
+   */
+  else
+    {
+      /* 
+       * Add rule as member to grouplist, to facilitate cleanup
+       */
+      if (grouplist) 
+	{
+	  tmp = grouplist; 
+	  while (tmp->next != NULL) { tmp = tmp->next; }
+	  tmp->next = nr;
+	} else {
+	  grouplist = nr;
+	}
+
+      if (!host_open)
+	{
+	  if (0 != sh_eval_hadd("^.*"))
+	    {
+	      SH_FREE(nr);
+	      return -1;
+	    }
+	}
+
+      /* 
+       * Add rule to host rulegroups
+       */
+      if (host_open)
+	{
+	  if (host_open->rulegroups) 
+	    {
+	      /* Second, third, ... rule go to host_open->rulegroups->nextrule,
+	       * since test_rules() iterates over nextrules
+	       */
+	      tmp = host_open->rulegroups; 
+	      while (tmp->nextrule != NULL) { tmp = tmp->nextrule; }
+	      tmp->nextrule = nr;
+	    } else {
+	      /* First rule goes to host_open->rulegroups */
+	      host_open->rulegroups = nr;
+	    }
+	}
+      else
+	{
+	  SH_FREE(nr);
+	  return -1;
+	}
+    }
+
+  return 0;
+}
+
+void sh_eval_cleanup()
+{
+  struct sh_geval * nr;
+  struct sh_geval * tmp;
+
+  struct sh_geval * gtmp;
+  struct sh_qeval * qtmp;
+  struct sh_heval * htmp;
+
+  gtmp = grouplist;
+  while (gtmp)
+    {
+      if (gtmp->label)      sh_string_destroy(&(gtmp->label));
+      if (gtmp->rule_extra) (*pcre_free)(gtmp->rule_extra);
+      if (gtmp->rule)       (*pcre_free)(gtmp->rule);
+      if (gtmp->counterlist)
+	zAVLFreeTree(gtmp->counterlist, sh_ceval_free);
+      if (gtmp->ovector)
+	SH_FREE(gtmp->ovector);
+      if (gtmp->nextrule)
+	{
+	  tmp = gtmp->nextrule;
+	  do {
+	    nr  = tmp->nextrule;
+	    if (tmp->rule_extra) (*pcre_free)(tmp->rule_extra);
+	    if (tmp->rule)       (*pcre_free)(tmp->rule);
+	    if (tmp->counterlist)
+	      zAVLFreeTree(tmp->counterlist, sh_ceval_free);
+	    if (tmp->ovector)
+	      SH_FREE(tmp->ovector);
+	    SH_FREE(tmp);
+	    tmp = nr;
+	  } while (tmp);
+	}
+      grouplist = gtmp->next;
+      SH_FREE(gtmp);
+      gtmp = grouplist;
+    }
+
+  qtmp = queuelist;
+  while (qtmp)
+    {
+      if (qtmp->label)      sh_string_destroy(&(qtmp->label));
+      queuelist = qtmp->next;
+      SH_FREE(qtmp);
+      qtmp = queuelist;
+    }
+
+  htmp = hostlist;
+  while (htmp)
+    {
+      if (htmp->hostname_extra) (*pcre_free)(htmp->hostname_extra);
+      if (htmp->hostname)       (*pcre_free)(htmp->hostname);
+      hostlist = htmp->next;
+      SH_FREE(htmp);
+      htmp = hostlist;
+    }
+
+}
+
+/**********************************************************************
+ *
+ * Actual rule processing
+ *
+ **********************************************************************/ 
+
+/* Test a list of rules against msg; return matched rule, with ovector 
+ * filled in
+ */
+static struct sh_geval * test_rule (struct sh_geval * rule, sh_string *msg)
+{
+  int res, count;
+
+  if (!rule)
+    DEBUG("debug: (NULL) rule\n");
+
+  if (rule && sh_string_len(msg) < (size_t)INT_MAX)
+    {
+      count = 1;
+      do {
+	DEBUG("debug: check rule %d for <%s>\n", count, msg->str);
+	res = pcre_exec(rule->rule, rule->rule_extra, 
+			sh_string_str(msg), (int)sh_string_len(msg), 0,
+			0, rule->ovector, (3*(1+rule->captures)));
+	if (res >= 0)
+	  {
+	    rule->ovecnum = res;
+	    DEBUG("debug: rule %d matches, result = %d\n", count, res);
+	    break; /* return the matching rule; ovector is filled in */
+	  }
+	DEBUG("debug: rule %d did not match\n", count);
+	rule = rule->nextrule; ++count;
+      } while (rule);
+    }
+  if (!rule)
+    DEBUG("debug: no match found\n");
+  /* If there was no match, this is NULL */
+  return rule;
+}
+  
+/* Test a (struct sh_geval *), which may be single rule or a group of rules,
+ * against msg (if it's a group of rules, test against prefix first).
+ */
+static struct sh_geval * test_grules (struct sh_heval * host, 
+				      sh_string *prefix, sh_string *msg)
+{
+  struct sh_geval * result = NULL;
+  struct sh_geval * rules = host->rulegroups;
+
+  if (rules && sh_string_len(prefix) < (size_t)INT_MAX)
+    {
+      DEBUG("debug: if rules\n");
+      do {
+	if(rules->label != NULL) 
+	  {
+	    /* this is a rule group; only groups have labels */
+
+	    DEBUG("debug: if rules->label %s\n", rules->label->str);
+	    if (pcre_exec(rules->rule, rules->rule_extra, 
+			  sh_string_str(prefix), (int) sh_string_len(prefix),
+			  0, 0, NULL, 0) >= 0)
+	      {
+		result = test_rule(rules->nextrule, msg);
+		if (result)
+		  break;
+	      }
+	  }
+	else
+	  {
+	    /* First rule is in host->rulegroups */
+
+	    DEBUG("debug: else (single rule)\n");
+	    result = test_rule(rules, msg);
+	    if (result)
+	      break;
+	  }
+	rules = rules->next; /* next group of rules */
+      } while (rules);
+    }
+  return result;
+}
+
+/* Top-level find_rule() function
+ */
+static struct sh_geval * find_rule (sh_string *host, 
+				    sh_string *prefix, sh_string *msg)
+{
+  struct sh_geval * result = NULL;
+  struct sh_heval * hlist  = hostlist;
+
+  if (hlist && sh_string_len(host) < (size_t)INT_MAX)
+    {
+      do {
+	if (pcre_exec(hlist->hostname, hlist->hostname_extra, 
+		      sh_string_str(host), (int) sh_string_len(host), 
+		      0, 0, NULL, 0) >= 0)
+	  {
+	    /* matching host, check rules/groups of rules */
+	    result = test_grules(hlist, prefix, msg);
+	    if (result)
+	      break;
+	  }
+	hlist = hlist->next;
+      } while (hlist);
+    }
+  return result;
+}
+
+static void msg_report(int severity, struct sh_logrecord * record)
+{
+  char * tmp;
+  char * msg;
+  char * ttt;
+  SH_MUTEX_LOCK(mutex_thread_nolog);
+  tmp = sh_util_safe_name (record->filename);
+  msg = sh_util_safe_name (sh_string_str(record->message));
+  ttt = sh_util_safe_name (sh_string_str(record->timestr));
+  sh_error_handle (severity, FIL__, __LINE__, 0, MSG_LOGMON_REP,
+		   msg,
+		   ttt,
+		   sh_string_str(record->host),
+		   tmp);
+  SH_FREE(ttt);
+  SH_FREE(msg);
+  SH_FREE(tmp);
+  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+}
+
+static void sum_report(int severity, sh_string * host, sh_string * message, sh_string * path)
+{
+  char * tmp;
+  char * msg;
+  SH_MUTEX_LOCK(mutex_thread_nolog);
+  tmp = sh_util_safe_name (sh_string_str(path));
+  msg = sh_util_safe_name (sh_string_str(message));
+  sh_error_handle (severity, FIL__, __LINE__, 0, MSG_LOGMON_SUM,
+		   msg,
+		   sh_string_str(host), 
+		   tmp);
+  SH_FREE(msg);
+  SH_FREE(tmp);
+  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+}
+
+static zAVLKey sh_eval_getkey(void const *item)
+{
+  return ((struct sh_ceval *)item)->hostname->str;
+}
+
+/* Find the counter, or initialize one if there is none already
+ */
+static struct sh_ceval * find_counter(struct sh_geval * rule, 
+				      sh_string * host, time_t interval)
+{
+  struct sh_ceval * counter;
+
+  if (!(rule->counterlist))
+    {
+      DEBUG("debug: allocate new counterlist AVL tree\n");
+      rule->counterlist = zAVLAllocTree(sh_eval_getkey);
+    }
+
+  counter = (struct sh_ceval *) zAVLSearch (rule->counterlist, 
+					    sh_string_str(host));
+
+  if (!counter)
+    {
+      DEBUG("debug: no counter found\n");
+
+      counter = SH_ALLOC(sizeof(struct sh_ceval));
+      counter->hostname    = sh_string_new_from_lchar(sh_string_str(host), 
+						      sh_string_len(host));
+      counter->counted_str = NULL;
+      counter->filename    = NULL;
+      counter->count       = 0;
+      counter->start       = time(NULL);
+      counter->interval    = interval;
+
+      zAVLInsert(rule->counterlist, counter);
+    }
+  return counter;
+		       
+}
+
+/* copy the message and replace captured substrings with '___'
+ */
+static sh_string * replace_captures(const sh_string * message, 
+				    int * ovector, int ovecnum)
+{
+  sh_string * retval = sh_string_new_from_lchar(sh_string_str(message), 
+						sh_string_len(message));
+
+  if (ovecnum > 1)
+    {
+      retval = sh_string_replace(retval, &(ovector[2]), (ovecnum-1), "___", 3);
+    }
+  return retval;
+}
+
+
+/* process the counter for a SUM rule
+ */
+static int  process_counter(struct sh_ceval * counter, 
+			    struct sh_geval * rule,  struct sh_logrecord * record)
+{
+  int retval = -1;
+  time_t  now;
+
+  if (!(counter->counted_str))
+    {
+      counter->counted_str = replace_captures(record->message, rule->ovector, 
+					      rule->ovecnum);
+      counter->filename    = sh_string_new_from_lchar(record->filename,
+						      strlen(record->filename));
+      DEBUG("debug: counted_str after replace: %s\n", 
+	    sh_string_str(counter->counted_str)); 
+    }
+
+  ++(counter->count);
+  now = time(NULL); now -= counter->start;
+  DEBUG("debug: count %lu, interval %lu, time %lu\n", 
+	counter->count, counter->interval, now);
+  if (now >= counter->interval)
+    {
+      DEBUG("debug: report count\n");
+      sum_report(rule->queue->severity, counter->hostname, 
+		 counter->counted_str, counter->filename);
+      counter->start = time(NULL);
+      counter->count = 0;
+    }
+  return retval;
+}
+
+/* Process a rule
+ */
+static int  process_rule(struct sh_geval * rule, struct sh_logrecord * record)
+{
+  int retval = -1;
+  struct sh_qeval * queue = rule->queue;
+
+  if (queue)
+    {
+      DEBUG("debug: queue policy = %d found\n", queue->policy);
+      if (queue->policy == EVAL_REPORT)
+	{
+	  DEBUG("debug: EVAL_REPORT host: %s, prefix: %s, message: %s\n",
+		 sh_string_str(record->host), 
+		 sh_string_str(record->prefix), 
+		 sh_string_str(record->message));
+	  msg_report(queue->severity, record);
+	  retval = 0;
+	}
+      else if (queue->policy == EVAL_SUM)
+	{
+	  
+	  struct sh_ceval * counter = 
+	    find_counter(rule, record->host, queue->interval);
+	  DEBUG("debug: EVAL_SUM host: %s, prefix: %s, message: %s\n",
+		 sh_string_str(record->host), 
+		 sh_string_str(record->prefix), 
+		 sh_string_str(record->message));
+	  if (counter)
+	    {
+	      DEBUG("debug: counter found\n");
+	      retval = process_counter(counter, rule, record);
+	    }
+	}
+    }
+  else
+    {
+      DEBUG("debug: no queue found -- trash\n");
+      /* No queue means 'trash' */
+      retval = 0;
+    }
+  return retval;
+}
+
+#define DEFAULT_SEVERITY (-1)
+
+int sh_eval_process_msg(struct sh_logrecord * record)
+{
+  static unsigned long i = 0;
+  if (record)
+    {
+      struct sh_geval * rule = find_rule (record->host, record->prefix, 
+					  record->message);
+
+      if (rule)
+	{
+	  DEBUG("debug: (%lu) rule found\n", i); ++i;
+	  return process_rule(rule, record);
+	}
+      else
+	{
+	  DEBUG("debug: (%lu) no rule found\n", i); ++i;
+	  msg_report(DEFAULT_SEVERITY, record);
+	}
+      return 0;
+    }
+  return -1;
+}
+
+#endif
Index: trunk/src/sh_log_parse_apache.c
===================================================================
--- trunk/src/sh_log_parse_apache.c	(revision 183)
+++ trunk/src/sh_log_parse_apache.c	(revision 183)
@@ -0,0 +1,362 @@
+/**************************************
+ **
+ ** PARSER RULES
+ **
+ ** (a) must set record->host 
+ **     (eventually to dummy value)
+ **
+ ** (b) must set record->prefix
+ **     (itoa(status)) 
+ **
+ **
+ **************************************/
+
+/* for strptime */
+#define _XOPEN_SOURCE 500
+
+#include "config_xor.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef USE_LOGFILE_MONITOR
+
+#undef  FIL__
+#define FIL__  _("sh_log_parse_apache.c")
+
+/* Debian/Ubuntu: libpcre3-dev */
+#include <pcre.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_log_check.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+
+extern int flag_err_debug;
+
+struct sh_fileinfo_apache {
+  pcre * line_regex;
+  int  * line_ovector;         /* captured substrings     */
+  int    line_ovecnum;         /* how many captured       */
+  
+  int    pos_host;
+  int    pos_status;
+  int    pos_time;
+};
+
+static const char lf_common0[]   = N_("%h %l %u %t \"%r\" %>s %b");
+static const char lf_combined0[] = N_("%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"");
+
+/* This variable is not used anywhere. It only exist
+ * to assign &new to them, which keeps gcc from
+ * putting it into a register, and avoids the 'clobbered
+ * by longjmp' warning. And no, 'volatile' proved insufficient.
+ */
+static void * sh_dummy_new = NULL;
+
+void * sh_eval_fileinfo_apache(char * str)
+{
+  struct sh_fileinfo_apache * result = NULL;
+  unsigned int i, quotes;
+  unsigned int nfields = 64;
+  size_t       lengths[64];
+  char *       new = NULL;
+  char **      splits;
+  char *       token;
+  sh_string  * re_string;
+  char *       p;
+  volatile int          p_host = -1;
+  volatile int          p_status = -1;
+  volatile int          p_time = -1;
+  const char * error;
+  int          erroffset;
+  
+  /* Take the address to keep gcc from putting them into registers. 
+   * Avoids the 'clobbered by longjmp' warning. 
+   */
+  sh_dummy_new = (void*) &new;
+
+  if (0 == strncmp("common", str, 6))
+    {
+      new    = sh_util_strdup(_(lf_common0));
+    }
+  else if (0 == strncmp("combined", str, 8))
+    {
+      new    = sh_util_strdup(_(lf_combined0));
+    }
+  else
+    {
+      new    = sh_util_strdup(str);
+    }
+
+  if (flag_err_debug == SL_TRUE)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      new,
+		      _("eval_fileinfo"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+
+  splits = split_array_ws(new, &nfields, lengths);
+
+  if (nfields < 1)
+    {
+      SH_FREE(splits);
+      SH_FREE(new);
+      return NULL;
+    }
+
+  /* Build the regex string re_string
+   */
+  re_string =  sh_string_new(0);
+  sh_string_add_from_char(re_string, "^");
+
+  for (i = 0; i < nfields; ++i)
+    {
+
+      if (i > 0)
+	sh_string_add_from_char(re_string, " ");
+
+      if (splits[i][0] != '"')
+	quotes = 0;
+      else
+	quotes = 1;
+
+      if (quotes && lengths[i] > 1 && splits[i][lengths[i]-1] == '"')
+	{
+	  splits[i][lengths[i]-1] = '\0'; /* cut trailing quote */
+	  token = &(splits[i][1]);
+	} else {
+	  token = splits[i];
+	}
+
+      if(quotes)
+	{
+	  if(strcmp(token, "%r") == 0 || 
+	     strstr(token, _("{Referer}")) == 0 || 
+             strstr(token, _("{User-Agent}")) == 0)
+	    {
+	      p = _("\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"");
+	      sh_string_add_from_char(re_string, p);
+	    }    
+	  else
+	    {
+	      p = _("(\\S+)");
+	      sh_string_add_from_char(re_string, p);
+	    }
+	}
+      else if (token[0] == '%' && token[strlen(token)-1] == 't') 
+	{
+	  p = _("\\[([^\\]]+)\\]");
+	  sh_string_add_from_char(re_string, p);
+	  p_time = i+1;
+	}
+      else
+	{
+	  p = _("(\\S+)");
+	  sh_string_add_from_char(re_string, p);
+	  if (token[0] == '%' && token[strlen(token)-1] == 's')
+	    p_status = i+1;
+	  else if (token[0] == '%' && token[strlen(token)-1] == 'v')
+	    p_host = i+1;
+	}
+    }
+  sh_string_add_from_char(re_string, "$");
+
+  if (flag_err_debug == SL_TRUE)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(re_string),
+		      _("eval_fileinfo"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+
+  result = SH_ALLOC(sizeof(struct sh_fileinfo_apache));
+  result->line_regex = pcre_compile(sh_string_str(re_string), 0, 
+				    &error, &erroffset, NULL);
+  if (!(result->line_regex))
+    {
+      sh_string * msg =  sh_string_new(0);
+      sh_string_add_from_char(msg, _("Bad regex: "));
+      sh_string_add_from_char(msg, sh_string_str(re_string));
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(msg),
+		      _("eval_fileinfo"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      SH_FREE(result);
+      SH_FREE(splits);
+      SH_FREE(new);   
+      sh_string_destroy(&msg);
+      sh_string_destroy(&re_string);
+
+      return NULL;
+    }
+  sh_string_destroy(&re_string);
+
+  result->line_ovector  = SH_ALLOC(sizeof(int) * (nfields+1) * 3);
+  result->line_ovecnum  = nfields;
+  result->pos_host      = p_host;
+  result->pos_status    = p_status;
+  result->pos_time      = p_time;
+
+  SH_FREE(splits);
+  SH_FREE(new);
+  return (void*)result;
+}
+
+struct sh_logrecord * sh_parse_apache (sh_string * logline, void * fileinfo)
+{
+  static struct tm old_tm;
+  static time_t    old_time;
+
+  char         tstr[128];
+  char         sstr[128];
+  char       * hstr;
+  int          res;
+  const char **hstr_addr = (const char **) &hstr;
+
+  struct sh_fileinfo_apache * info = (struct sh_fileinfo_apache *) fileinfo;
+
+  if (sh_string_len(logline) > 0 && flag_err_debug == SL_TRUE)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(logline),
+		      _("sh_parse_apache"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+
+  if (logline == NULL || info == NULL)
+    {
+      return NULL;
+    }
+
+  res = pcre_exec(info->line_regex, NULL, 
+		  sh_string_str(logline), (int)sh_string_len(logline), 0,
+		  0, info->line_ovector, (3*(1+info->line_ovecnum)));
+
+  if (res == (1+info->line_ovecnum))
+    {
+      struct sh_logrecord * record;
+      time_t timestamp = 0;
+
+      if (info->pos_time > 0)
+	{
+	  res = pcre_copy_substring(sh_string_str(logline), 
+				    info->line_ovector, res,
+				    info->pos_time, tstr, sizeof(tstr));
+	  if (res <= 0)
+	    goto corrupt;
+	}
+      else
+	{
+	  res = 0;
+	  timestamp = 0;
+	  sl_strlcpy(tstr, _("01/Jan/1970:00:00:00"), sizeof(tstr));
+	}
+
+      if (res > 0)
+	{
+	  struct tm btime;
+	  char * ptr, * q;
+	  
+	  /* example: 01/Jun/2008:07:55:28 +0200 */
+
+	  q = strchr(tstr, ' ');
+	  if (q) *q = '\0';
+
+	  ptr = /*@i@*/strptime(tstr, "%d/%b/%Y:%T", &btime);
+	  if (ptr && *ptr == '\0')
+	    {
+	      timestamp = conv_timestamp(&btime, &old_tm, &old_time);
+	    }
+	  else
+	    goto corrupt;
+	}
+
+      if (info->pos_status > 0)
+	{
+	  res = pcre_copy_substring(sh_string_str(logline), 
+				    info->line_ovector, res,
+				    info->pos_status, sstr, sizeof(sstr));
+	  if (res <= 0)
+	    goto corrupt;
+	}
+      else
+	{
+	  sl_strlcpy(sstr, _("000"), sizeof(sstr)); /* FIXME API */
+	}
+
+      if (info->pos_host > 0)
+	{
+	  res = pcre_get_substring(sh_string_str(logline), 
+				   info->line_ovector, res,
+				   info->pos_host, hstr_addr);
+	  if (res <= 0)
+	    goto corrupt;
+	}
+      else
+	{
+	  hstr = NULL;
+	}
+
+      record = SH_ALLOC(sizeof(struct sh_logrecord));
+      
+      record->timestamp = timestamp;
+      record->timestr   = sh_string_new_from_lchar(tstr, strlen(tstr));
+      record->prefix    = sh_string_new_from_lchar(sstr, strlen(sstr));
+
+      if (hstr)
+	record->host = sh_string_new_from_lchar(hstr, strlen(hstr));
+      else
+	record->host = sh_string_new_from_lchar(sh.host.name, strlen(sh.host.name));
+
+      record->message   = sh_string_new_from_lchar(sh_string_str(logline), 
+						   sh_string_len(logline));
+      record->pid       = 0;
+
+      pcre_free(hstr); 
+      return record;
+    }
+  else
+    {
+      char msg[128];
+      sl_snprintf(msg, sizeof(msg), _("Incorrect number of captured subexpressions: %d vs %d"),
+		  res, info->line_ovecnum);
+      
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      msg,
+		      _("sh_parse_apache"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+
+  /* Corrupted logline */
+ corrupt:
+
+  {
+    sh_string * msg =  sh_string_new(0);
+    sh_string_add_from_char(msg, _("Corrupt logline: "));
+    sh_string_add_from_char(msg, sh_string_str(logline));
+    
+    SH_MUTEX_LOCK(mutex_thread_nolog);
+    sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		    sh_string_str(msg),
+		    _("sh_parse_apache"));
+    SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    sh_string_destroy(&msg);
+  }
+  return NULL;
+}
+
+/* USE_LOGFILE_MONITOR */
+#endif
Index: trunk/src/sh_log_parse_pacct.c
===================================================================
--- trunk/src/sh_log_parse_pacct.c	(revision 183)
+++ trunk/src/sh_log_parse_pacct.c	(revision 183)
@@ -0,0 +1,339 @@
+/**************************************
+ **
+ ** PARSER RULES
+ **
+ ** (a) must set record->host 
+ **     (eventually to dummy value)
+ **
+ ** (b) must set record->prefix
+ **     (command) 
+ **
+ **
+ **************************************/
+
+/* Based on the GNU Accounting Utilities, which is distributed with the
+ * following copyright: 
+ */
+
+/* Copyright (C) 1993, 1996, 1997, 2003, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of the GNU Accounting Utilities
+ *
+ * The GNU Accounting Utilities are free software; you can redistribute
+ * them and/or modify them under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either version
+ * 2, or (at your option) any later version.
+ *
+ * The GNU Accounting Utilities are distributed in the hope that they 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 the GNU Accounting Utilities; see the file COPYING.  If
+ * not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
+ * MA 02139, USA.  */
+
+#include "config_xor.h"
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <dirent.h>
+
+#if defined(USE_LOGFILE_MONITOR) && defined(HAVE_SYS_ACCT_H)
+
+#include <sys/acct.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_log_check.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+
+#undef  FIL__
+#define FIL__  _("sh_log_parse_pacct.c")
+
+extern int flag_err_debug;
+
+#ifndef ACCT_COMM
+#define ACCT_COMM 16
+#endif
+#ifndef AHZ
+#define AHZ 100
+#endif
+
+#if defined(ACUTIME_COMPT) || defined(ACSTIME_COMPT) || defined(ACETIME_COMPT)
+static double comp_t_2_double (comp_t ct)
+{
+  unsigned long out = 0;
+
+  out = ct & 017777;
+  ct >>= 13;
+
+  while (ct) {
+    ct--;
+    out <<= 3;
+  }
+  
+  return (double) out;
+}
+#endif
+
+#ifdef ACUTIME_COMPT
+# define ACUTIME_2_DOUBLE(x) (comp_t_2_double(x))
+#else
+# define ACUTIME_2_DOUBLE(x) ((double)(x))
+#endif
+
+#ifdef ACSTIME_COMPT
+# define ACSTIME_2_DOUBLE(x) (comp_t_2_double(x))
+#else
+# define ACSTIME_2_DOUBLE(x) ((double)(x))
+#endif
+
+#ifdef ACETIME_COMPT
+# define ACETIME_2_DOUBLE(x) (comp_t_2_double(x))
+#else
+# define ACETIME_2_DOUBLE(x) ((double)(x))
+#endif
+
+
+static void expand_flags(char flag, char * out)
+{
+  int i = 0;
+
+#define	BIT(flg, ch)	if (flag & flg) out[i] = ch; else out[i] = ' '; ++i
+
+  BIT(ASU, 'S');
+  BIT(AFORK, 'F');
+#ifdef ACOMPAT
+  BIT(ACOMPAT, 'C');
+#endif
+  BIT(ACORE, 'D');
+  BIT(AXSIG, 'X');
+
+  out[i] = '\0';
+  return;
+}
+
+static char * uid_name (int uid)
+{
+  static int  userid   = 0;
+  static char user[16] = "";
+
+  if (uid == userid && user[0] != '\0')
+    {
+      return user;
+    }
+  else
+    {
+      struct passwd *thispw = getpwuid (uid);
+      if (thispw)
+	sl_strlcpy (user, thispw->pw_name, sizeof(user));
+      else
+	sl_snprintf(user, sizeof(user), "%d", uid);
+      user[sizeof(user)-1] = '\0';
+      userid = uid;
+    }
+  return user;
+}
+
+struct dev_struct {
+  char * device;
+  long   dev_id;
+  struct dev_struct * next;
+};
+static struct dev_struct * devicelist = NULL;
+
+static void add_devices(const char * dir)
+{
+  DIR *  mdir;
+  char   dirl[256];
+
+  sl_strlcpy(dirl, dir, sizeof(dirl));
+  dirl[sizeof(dirl)-1] = '\0';
+
+  mdir = opendir(dir);
+  
+  if (mdir)
+    {
+      char * path;
+      size_t len;
+      struct dirent * dent;
+      struct stat buf;
+
+      while (NULL != (dent = readdir(mdir)))
+	{
+	  if (0 == strcmp(dent->d_name, "."))
+	    continue;
+	  if (0 == strcmp(dent->d_name, ".."))
+	    continue;
+	  len = strlen(dir) + strlen(dent->d_name) + 2;
+	  path = SH_ALLOC(len);
+	  snprintf(path, len, "%s/%s", dir, dent->d_name);
+	  if (0 == lstat(path, &buf) && S_ISCHR(buf.st_mode))
+	    {
+	      struct dev_struct * dstruct;
+	      dstruct = SH_ALLOC(sizeof(struct dev_struct));
+	      /* eliminate leading '/dev/' */
+	      memmove(path, &path[5], strlen(path)-4); 
+	      dstruct->device = path;
+	      dstruct->dev_id = buf.st_rdev;
+	      dstruct->next   = devicelist;
+	      devicelist      = dstruct;
+	    }
+	  else
+	    {
+	      SH_FREE(path);
+	    }
+	}
+      closedir(mdir);
+    }
+  return;
+}
+
+static char * dev_name(long tty)
+{
+  struct dev_struct * dstruct;
+
+  if (!devicelist)
+    {
+      add_devices("/dev");
+      add_devices("/dev/pts");
+      add_devices("/dev/pty");
+      add_devices("/dev/ptym");
+    }
+
+  dstruct = devicelist;
+  while (dstruct)
+    {
+      if (dstruct->dev_id == tty)
+	return dstruct->device;
+      dstruct = dstruct->next;
+    }
+  return "??";
+}
+
+/* This looks strange, but it's real ANSI C. */
+extern struct acct pacct_rd_never_used;
+#define COMM_LEN ((int) sizeof (pacct_rd_never_used.ac_comm))
+
+sh_string * sh_read_pacct (sh_string * record, struct sh_logfile * logfile)
+{
+#if defined(__linux__)
+#  define STRUCT_ACCT struct acct_v3
+#else
+#  define STRUCT_ACCT struct acct
+#endif
+
+  STRUCT_ACCT rec;
+
+  if (NULL != sh_binary_reader ((void*) &rec, sizeof(STRUCT_ACCT), logfile))
+    {
+      time_t btime = (time_t) rec.ac_btime;
+      double ut    = ACUTIME_2_DOUBLE (rec.ac_utime);
+      double st    = ACSTIME_2_DOUBLE (rec.ac_stime);
+      char   fl[6];
+      char   comm[COMM_LEN+1];
+      int    i;
+      char   out[64+COMM_LEN+1+5+8+8+32+4+19+7]; /* see printf format below */
+      
+      expand_flags(rec.ac_flag, fl);
+      
+      /* ac_comm may not be null terminated
+       */
+      for (i = 0; i < COMM_LEN; i++)
+	{
+	  if (rec.ac_comm[i] == '\0')
+	    {
+	      comm[i] = '\0';
+	      break;
+	    }
+	  if (! isprint (rec.ac_comm[i]))
+	    comm[i] = '?';
+	  else
+	    comm[i] = rec.ac_comm[i];
+	}
+      comm[COMM_LEN] = '\0';
+
+      sl_snprintf (out, sizeof(out),
+		   "%ld:%-*.*s %5.5s %-8.8s %-8.8s %6.2f secs %-19.19s",
+		   btime,
+		   COMM_LEN, COMM_LEN, comm, fl, 
+		   uid_name(rec.ac_uid), 
+		   dev_name((long)rec.ac_tty),
+		   ((ut + st) / (double) AHZ),
+		   ctime (&btime));
+
+
+      sh_string_set_from_char(record, out);
+      return record;
+    }
+
+  return NULL;
+}
+
+struct sh_logrecord * sh_parse_pacct (sh_string * logline, void * fileinfo)
+{
+  char * p;
+  char * endptr;
+  unsigned long ltime;
+  struct sh_logrecord * record = NULL;
+
+  (void) fileinfo;
+
+  if (sh_string_len(logline) > 0 && flag_err_debug == SL_TRUE)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      sh_string_str(logline),
+		      _("sh_parse_pacct"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+
+  p = strchr(sh_string_str(logline), ':');
+
+  if (!p || p == sh_string_str(logline))
+    return NULL;
+
+  ltime = strtoul(sh_string_str(logline), &endptr, 10);
+  if (p != endptr)
+    return NULL;
+		  
+  ++p; /* points to first char of pacct record */
+  
+  if (*p != '\0')
+    {
+      size_t lengths[7];
+      unsigned int  fields = 7;
+      char ** array;
+      sh_string * message = sh_string_new_from_lchar(p, strlen(p));
+      array = split_array_ws(p, &fields, lengths);
+
+      if (fields == 7)
+	{
+	  record = SH_ALLOC(sizeof(struct sh_logrecord));
+
+	  record->timestamp = ltime;
+	  record->timestr   = sh_string_new_from_lchar(array[6], lengths[6]);
+	  record->prefix    = sh_string_new_from_lchar(array[0], lengths[0]);
+	  record->message   = message;
+	  record->pid       = 0;
+	  record->host      = sh_string_new_from_lchar(sh.host.name, strlen(sh.host.name));
+	}
+      else
+	{
+	  sh_string_destroy(&message);
+	}
+      SH_FREE(array);
+    }
+  return record;
+}
+/* USE_LOGFILE_MONITOR */
+#endif
Index: trunk/src/sh_log_parse_syslog.c
===================================================================
--- trunk/src/sh_log_parse_syslog.c	(revision 183)
+++ trunk/src/sh_log_parse_syslog.c	(revision 183)
@@ -0,0 +1,184 @@
+/**************************************
+ **
+ ** PARSER RULES
+ **
+ ** (a) must set record->host 
+ **     (eventually to dummy value)
+ **
+ ** (b) must set record->prefix
+ **     (eventually to dummy value) 
+ **
+ **
+ **************************************/
+
+/* for strptime */
+#define _XOPEN_SOURCE
+
+#include "config_xor.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef USE_LOGFILE_MONITOR
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_log_check.h"
+#include "sh_utils.h"
+#include "sh_string.h"
+
+#undef  FIL__
+#define FIL__  _("sh_log_parse_syslog.c")
+
+static int hidepid = 0;
+extern int flag_err_debug;
+
+int sh_get_hidepid()
+{
+  return hidepid;
+}
+
+int sh_set_hidepid(const char *s)
+{
+  return sh_util_flagval(s, &hidepid);
+}
+
+struct sh_logrecord * sh_parse_syslog (sh_string * logline, void * fileinfo)
+{
+  static const char *    format0_1 = N_("%b %d %T");
+  static const char *    format0_2 = N_("%Y-%m-%dT%T");
+
+  static char   format_1[16]; 
+  static char   format_2[16];
+  static int    format_init = 0;
+
+  static struct tm old_tm;
+  static time_t    old_time;
+
+  const unsigned int Tpos = 10;
+  unsigned int tlen = 16;
+
+  (void) fileinfo;
+
+  if (!format_init)
+    {
+      sl_strlcpy(format_1, _(format0_1), sizeof(format_1));
+      sl_strlcpy(format_2, _(format0_2), sizeof(format_2));
+      format_init = 1;
+    }
+
+
+  if (sh_string_len(logline) > 0 && flag_err_debug == SL_TRUE)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		      logline->str,
+		      _("sh_parse_syslog"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+    }
+
+  if (logline && sh_string_len(logline) > tlen)
+    {
+      struct tm btime;
+      char * ptr;
+      int    flag;
+      size_t lengths[3];
+
+      memset(&btime, '\0', sizeof(struct tm));
+
+      /* This is RFC 3164. 
+       */
+      if (logline->str[Tpos] != 'T')
+	{
+	  logline->str[tlen-1] = '\0';
+	  ptr = /*@i@*/strptime(logline->str, format_1, &btime);
+	}
+
+      /* RFC 3339 describes an alternative timestamp format.
+       * Unfortunately, POSIX strptime() does not support reading 
+       * the TZ offset.
+       */
+      else
+	{
+	  ptr = strptime(logline->str, format_2, &btime);
+	  if (ptr)
+	    { 
+	      tlen = 20;
+	      if (*ptr && *ptr != ' ') {
+		do { ++ptr; ++tlen; } while (*ptr && *ptr != ' ');
+		if (*ptr == ' ') *ptr = '\0'; 
+	      }
+	    }
+	}
+
+      if (ptr && *ptr == '\0') /* no error, whole string consumed */
+	{
+	  unsigned int  fields = 3; /* host program(\[pid\])+: message */
+	  char ** array  = split_array_ws(&(logline->str[tlen]), &fields, lengths);
+
+	  if (fields == 3)
+	    {
+	      struct sh_logrecord * record = SH_ALLOC(sizeof(struct sh_logrecord));
+
+	      record->timestamp = conv_timestamp(&btime, &old_tm, &old_time);
+	      record->timestr   = sh_string_new_from_lchar(logline->str, (tlen-1)); 
+
+	      /* host
+	       */
+	      record->host = sh_string_new_from_lchar(array[0], lengths[0]);
+
+	      /* program and pid
+	       */
+	      if (NULL != (ptr = strchr(array[1], '[')))
+		{
+		  *ptr = '\0';
+		  ++ptr;
+		  record->pid = (pid_t) atoi(ptr);
+		  record->prefix = sh_string_new_from_lchar(array[1], lengths[1]);
+		  if (hidepid == 0 || !*ptr) {
+		    --ptr;
+		    *ptr = '[';
+		  } else {
+		    *ptr = '\0'; /* overwrite first digit */
+		    --ptr;
+		    *ptr = ':';  /* overwrite ex-':' */
+		    lengths[1] = strlen(array[1]);
+		  }
+		}
+	      else
+		{
+		  flag = 0;
+		  ptr = array[1];
+		  if (ptr[lengths[1]] == ':') {
+		    ptr[lengths[1]] = '\0';
+		    flag = 1;
+		  }
+		  record->pid = PID_INVALID;
+		  record->prefix = sh_string_new_from_lchar(array[1], lengths[1]);
+		  if (flag == 1) {
+		    ptr[lengths[1]] = ':';
+		  }
+		}
+
+	      /* message
+	       */
+	      record->message = sh_string_new_from_lchar3(array[1], lengths[1],
+							  " ", 1,
+							  array[2], lengths[2]);
+
+	      SH_FREE(array);
+	      return record;
+	    }
+	  SH_FREE(array);
+	}
+    }
+  /* corrupted logline
+   */
+  return NULL;
+}
+
+/* USE_LOGFILE_MONITOR */
+#endif
Index: trunk/src/sh_modules.c
===================================================================
--- trunk/src/sh_modules.c	(revision 182)
+++ trunk/src/sh_modules.c	(revision 183)
@@ -16,4 +16,5 @@
 #include "sh_processcheck.h"
 #include "sh_portcheck.h"
+#include "sh_logmon.h"
 
 sh_mtype modList[] = {
@@ -130,4 +131,20 @@
 #endif
 
+#ifdef USE_LOGFILE_MONITOR
+  {
+    N_("LOGMON"),
+    -1,
+    sh_log_check_init,
+    sh_log_check_timer,
+    sh_log_check_check,
+    sh_log_check_cleanup,
+    sh_log_check_reconf,
+
+    N_("[LogMon]"),
+    sh_log_check_table,
+    PTHREAD_MUTEX_INITIALIZER,
+  },
+#endif
+
   {
     NULL,
Index: trunk/src/sh_processcheck.c
===================================================================
--- trunk/src/sh_processcheck.c	(revision 182)
+++ trunk/src/sh_processcheck.c	(revision 183)
@@ -1311,4 +1311,5 @@
       clean_list (&list_fake);
     }
+
   SH_MUTEX_UNLOCK(mutex_proc_check);
 
Index: trunk/src/sh_string.c
===================================================================
--- trunk/src/sh_string.c	(revision 182)
+++ trunk/src/sh_string.c	(revision 183)
@@ -108,5 +108,5 @@
 
     setnext:
-      lengths[i] = (a-s);
+      lengths[i] = (size_t) (a-s); /* a >= s always */
       arr[i] = s;
       ++i;
@@ -184,5 +184,5 @@
 		}
 	    }
-          lengths[i] = (a-s);
+          lengths[i] = (size_t)(a-s); /* a >= s always */
           arr[i]     = s; 
           ++i;
@@ -266,5 +266,5 @@
 }
 
-sh_string * sh_string_cat_lchar(sh_string * s, char * str, size_t len)
+sh_string * sh_string_cat_lchar(sh_string * s, const char * str, size_t len)
 {
   if (sl_ok_adds(len, s->siz) == SL_TRUE)
@@ -283,5 +283,5 @@
 }
 
-sh_string * sh_string_set_from_char(sh_string * s, char * str)
+sh_string * sh_string_set_from_char(sh_string * s, const char * str)
 {
   size_t len = strlen(str);
@@ -296,5 +296,19 @@
 }
 
-sh_string * sh_string_new_from_lchar(char * str, size_t len)
+sh_string * sh_string_add_from_char(sh_string * s, const char * str)
+{
+  size_t len   = strlen(str);
+  size_t avail = (s->siz - s->len);
+
+  if ((len+1) > avail)
+    {
+      (void) sh_string_grow(s, ((len+1) - avail) );
+    }
+  memcpy(&(s->str[s->len]), str, (len+1));
+  s->len += len;
+  return s;
+}
+
+sh_string * sh_string_new_from_lchar(const char * str, size_t len)
 {
   sh_string * s;
@@ -308,7 +322,7 @@
 }
 
-sh_string * sh_string_new_from_lchar3(char * str1, size_t len1,
-                                      char * str2, size_t len2,
-                                      char * str3, size_t len3)
+sh_string * sh_string_new_from_lchar3(const char * str1, size_t len1,
+                                      const char * str2, size_t len2,
+                                      const char * str3, size_t len3)
 {
   sh_string * s;
Index: trunk/src/sh_unix.c
===================================================================
--- trunk/src/sh_unix.c	(revision 182)
+++ trunk/src/sh_unix.c	(revision 183)
@@ -1064,5 +1064,5 @@
 
 #if defined(HOST_IS_CYGWIN) || defined(__cygwin__) || defined(__CYGWIN32__) || defined(__CYGWIN__)
-int tf_trust_check (char * file, int mode)
+int tf_trust_check (const char * file, int mode)
 {
   (void) file;
@@ -1071,5 +1071,5 @@
 }
 #else
-int tf_trust_check (char * file, int mode)
+int tf_trust_check (const char * file, int mode)
 {
   char * tmp;
Index: trunk/src/slib.c
===================================================================
--- trunk/src/slib.c	(revision 182)
+++ trunk/src/slib.c	(revision 183)
@@ -2652,5 +2652,5 @@
 static struct sl_trustfile_store * sl_trusted_files = NULL;
 
-void sl_add_trusted_file(char * filename, uid_t teuid)
+static void sl_add_trusted_file(const char * filename, uid_t teuid)
 {
   struct sl_trustfile_store *new = SH_ALLOC(sizeof(struct sl_trustfile_store));
@@ -2664,5 +2664,5 @@
 }
 
-char * sl_check_trusted_file(char * filename, uid_t teuid)
+static const char * sl_check_trusted_file(const char * filename, uid_t teuid)
 {
   struct sl_trustfile_store *new = sl_trusted_files;
@@ -2678,5 +2678,5 @@
 }
 
-void sl_clear_trusted_file(struct sl_trustfile_store * file)
+static void sl_clear_trusted_file(struct sl_trustfile_store * file)
 {
   if (file)
@@ -2690,5 +2690,5 @@
 }
 
-int sl_trustfile_euid(char * filename, uid_t teuid)
+int sl_trustfile_euid(const char * filename, uid_t teuid)
 {
   long          status;
Index: trunk/src/trustfile.c
===================================================================
--- trunk/src/trustfile.c	(revision 182)
+++ trunk/src/trustfile.c	(revision 183)
@@ -319,5 +319,5 @@
 
 /* not static to circumvent stupid gcc 4 bug */ 
-int getfname(char *fname, char *rbuf, int rsz)
+int getfname(const char *fname, char *rbuf, int rsz)
 {
 #ifndef TRUST_MAIN
@@ -678,5 +678,5 @@
 }
 
-int sl_trustfile(char *fname, uid_t *okusers, uid_t *badusers)
+int sl_trustfile(const char *fname, uid_t *okusers, uid_t *badusers)
 {
   char fexp[MAXFILENAME];	/* file name fully expanded        */
Index: trunk/test/testcompile.sh
===================================================================
--- trunk/test/testcompile.sh	(revision 182)
+++ trunk/test/testcompile.sh	(revision 183)
@@ -304,5 +304,5 @@
 	[ -z "${SMATCH}" ] || { CC="${SAVE_CC}"; export CC; SMATCH=""; export SMATCH; }
 	#
-	${TOP_SRCDIR}/configure --quiet  --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test  --enable-static --enable-suidcheck --enable-process-check > /dev/null 2>> test_log
+	${TOP_SRCDIR}/configure --quiet  --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test  --enable-static --enable-suidcheck --enable-process-check --enable-logfile-monitor > /dev/null 2>> test_log
 	#
 	let "num = num + 1" >/dev/null
@@ -390,11 +390,11 @@
 	# test standalone compilation with --with-nocl=PW
 	#
-	TEST="${S}standalone w/nocl${E}"
-	#
-	if test -r "Makefile"; then
-		$MAKE clean
-	fi
-	#
-	${TOP_SRCDIR}/configure --quiet  --prefix=$PW_DIR --enable-nocl="owl" --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test > /dev/null 2>> test_log   
+	TEST="${S}standalone w/nocl w/logmon${E}"
+	#
+	if test -r "Makefile"; then
+		$MAKE clean
+	fi
+	#
+	${TOP_SRCDIR}/configure --quiet  --prefix=$PW_DIR --enable-nocl="owl" --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test  --enable-logfile-monitor > /dev/null 2>> test_log   
 	#
 	let "num = num + 1" >/dev/null
@@ -468,5 +468,5 @@
 
 	#
-	# test standalone compilation w/logwatch
+	# test standalone compilation w/loginwatch
 	#
 	TEST="${S}standalone w/login-watch${E}"
@@ -637,5 +637,5 @@
 	[ -z "${SMATCH}" ] || { CC="${SAVE_CC}"; export CC; SMATCH=""; export SMATCH; }
 	#
-	${TOP_SRCDIR}/configure --quiet --enable-network=client  --enable-static --enable-srp --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test   --with-timeserver=127.0.0.1 > /dev/null 2>> test_log   
+	${TOP_SRCDIR}/configure --quiet --enable-network=client  --enable-static --enable-srp --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test   --with-timeserver=127.0.0.1   --enable-logfile-monitor > /dev/null 2>> test_log   
 	#
 	let "num = num + 1" >/dev/null
@@ -693,5 +693,5 @@
 	    fi
 	    #
-	    ${TOP_SRCDIR}/configure --quiet --enable-network=client  --enable-srp --with-gpg=$GPG  --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test > /dev/null 2>> test_log   
+	    ${TOP_SRCDIR}/configure --quiet --enable-network=client  --enable-srp --with-gpg=$GPG  --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test   --enable-logfile-monitor > /dev/null 2>> test_log   
 	    #
 	    let "num = num + 1" >/dev/null
@@ -757,5 +757,5 @@
 	fi
 	#
-	${TOP_SRCDIR}/configure --quiet --enable-network=client --enable-debug --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test > /dev/null 2>> test_log   
+	${TOP_SRCDIR}/configure --quiet --enable-network=client --enable-debug --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test   --enable-logfile-monitor > /dev/null 2>> test_log   
 	#
 	let "num = num + 1" >/dev/null
