Index: trunk/src/sh_portcheck.c
===================================================================
--- trunk/src/sh_portcheck.c	(revision 67)
+++ trunk/src/sh_portcheck.c	(revision 67)
@@ -0,0 +1,1327 @@
+/* SAMHAIN file system integrity testing                                   */
+/* Copyright (C) 2006 Rainer Wichmann                                      */
+/*                                                                         */
+/*  This program is free software; you can redistribute it                 */
+/*  and/or modify                                                          */
+/*  it under the terms of the GNU General Public License as                */
+/*  published by                                                           */
+/*  the Free Software Foundation; either version 2 of the License, or      */
+/*  (at your option) any later version.                                    */
+/*                                                                         */
+/*  This program is distributed in the hope that it will be useful,        */
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*  GNU General Public License for more details.                           */
+/*                                                                         */
+/*  You should have received a copy of the GNU General Public License      */
+/*  along with this program; if not, write to the Free Software            */
+/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
+
+/***************************************************************************
+ *
+ * This file provides a module for samhain to check for open ports
+ * on the local machine.
+ *
+ */
+
+
+/* #define TEST_ONLY */
+#ifndef TEST_ONLY
+#include "config_xor.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <unistd.h>
+
+#define PORTMAP
+#include <rpc/rpc.h>
+#ifdef  HAVE_RPC_RPCENT_H
+#include <rpc/rpcent.h>
+#endif
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <netdb.h>
+
+#define PORTCHK_VERSION "1.0"
+
+#if defined(TEST_ONLY) || (defined(SH_USE_PORTCHECK) && (defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)))
+
+
+/*
+ * struct pmaplist {
+ *      struct pmap     pml_map;
+ *      struct pmaplist *pml_next;
+ * };
+ */
+
+/* struct pmap {
+ *      long unsigned pm_prog;
+ *      long unsigned pm_vers;
+ *      long unsigned pm_prot;
+ *      long unsigned pm_port;
+ * };
+ */
+
+/* TIME_WAIT ? 60-240 seconds */
+
+/* the size of an interface string 
+ */
+#define SH_INTERFACE_SIZE 16
+
+#define SH_PORT_NOT 0
+#define SH_PORT_REQ 1
+#define SH_PORT_OPT 2
+
+#define SH_PORT_MISS 0
+#define SH_PORT_ISOK 1
+#define SH_PORT_UNKN 2
+
+#define SH_PORT_NOREPT 0
+#define SH_PORT_REPORT 1
+
+struct sh_portentry {
+  int  port;
+  char interface[SH_INTERFACE_SIZE];
+  char * service;
+  char * error;
+  int  flag;    /* required or not */
+  int  status;  /* missing or not  */
+  struct sh_portentry * next;
+};
+
+static struct sh_portentry * portlist_tcp = NULL;
+static struct sh_portentry * portlist_udp = NULL;
+
+#define SH_PORTCHK_INTERVAL 300
+
+static int sh_portchk_check_udp = 1;
+static int sh_portchk_active    = 1;
+static int sh_portchk_interval  = SH_PORTCHK_INTERVAL;
+#if !defined(TEST_ONLY)
+
+#define FIL__ _("sh_portcheck.c")
+#include "samhain.h"
+#include "sh_error.h"
+#include "sh_mem.h"
+#include "sh_utils.h"
+#include "sh_modules.h"
+
+static int sh_portchk_severity  = SH_ERR_SEVERE;
+#endif
+
+/* Exported interface to add required ports as 'iface:portlist'
+ */
+static int sh_portchk_add_required (const char * str);
+
+/* Exported interface to add optional ports as 'iface:portlist'
+ */
+static int sh_portchk_add_optional (const char * str);
+
+/* Exported interface to add an ethernet interface
+ */
+static int sh_portchk_add_interface (const char * str);
+
+
+#ifndef TEST_ONLY
+
+static int sh_portchk_set_interval (const char * c)
+{
+  int retval = 0;
+  long val;
+
+  SL_ENTER(_("sh_portchk_set_interval"));
+  val = strtol (c, (char **)NULL, 10);
+  if (val <= 0)
+    {
+      sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
+		       _("port check interval"), c);
+      retval = -1;
+    }
+
+  val = (val <= 0 ? 60 : val);
+
+  sh_portchk_interval = (time_t) val;
+  SL_RETURN(0, _("sh_portchk_set_interval"));
+}
+
+
+static int sh_portchk_set_active   (const char * str)
+{
+  return sh_util_flagval(str, &sh_portchk_active);
+}
+
+static int sh_portchk_set_udp      (const char * str)
+{
+  return sh_util_flagval(str, &sh_portchk_check_udp);
+}
+
+static int sh_portchk_set_severity (const char * str)
+{
+  char tmp[32];
+  tmp[0] = '='; tmp[1] = '\0';
+  sl_strlcat (tmp, str, 32);
+  return sh_error_set_level (tmp, &sh_portchk_severity);
+}
+
+sh_rconf sh_portchk_table[] = {
+    {
+        N_("severityportcheck"),
+        sh_portchk_set_severity,
+    },
+    {
+        N_("portcheckrequired"),
+        sh_portchk_add_required,
+    },
+    {
+        N_("portcheckoptional"),
+        sh_portchk_add_optional,
+    },
+    {
+        N_("portcheckactive"),
+        sh_portchk_set_active,
+    },
+    {
+        N_("portcheckinterface"),
+        sh_portchk_add_interface,
+    },
+    {
+        N_("portcheckinterval"),
+        sh_portchk_set_interval,
+    },
+    {
+        N_("portcheckudp"),
+        sh_portchk_set_udp,
+    },
+    {
+        NULL,
+        NULL
+    }
+};
+
+#endif
+
+/* Interface to initialize port check
+ */
+int sh_portchk_init ();
+
+/* Interface to reset port check
+ */
+int sh_portchk_reset ();
+
+/* Interface to run port check
+ */
+int sh_portchk_check ();
+
+
+static char * check_services (int port, char * proto);
+
+#ifdef TEST_ONLY
+
+static int portchk_debug = 0;
+#define SH_ALLOC       malloc
+#define SH_FREE        free
+#define sh_util_strdup strdup
+#define sl_strlcpy     strncpy
+#define _(a)           a
+
+#else
+
+static int portchk_debug = 0;
+
+#endif
+
+static void sh_portchk_add_to_list (char * proto, 
+				    int port, struct in_addr haddr, char * service,
+				    int flag, int status)
+{
+  struct sh_portentry * new = SH_ALLOC (sizeof(struct sh_portentry));
+
+  if (portchk_debug)
+    fprintf(stderr, _("add to list: port %d/%s %d %d (%s)\n"),
+	    port, proto, flag, status, service ? service : _("undef"));
+
+  new->port = port;
+  sl_strlcpy (new->interface, inet_ntoa(haddr), SH_INTERFACE_SIZE);
+  new->status = status;
+  new->flag   = flag;
+
+  new->error  = NULL;
+
+  if (service)
+    new->service = sh_util_strdup (service);
+  else
+    new->service = NULL;
+  if (0 == strcmp(proto, "tcp"))
+    {
+      new->next = portlist_tcp;
+      portlist_tcp = new;
+    }
+  else
+    {
+      new->next = portlist_udp;
+      portlist_udp = new;
+    }
+  return;
+}
+
+/* Reset the list by setting all entries to UNKN.
+ * In the next cycle we will check, and set found ports to ISOK.
+ * Thereafter, we check for entries that are still UNKN.
+ */
+static void sh_portchk_reset_lists ()
+{
+  struct sh_portentry * portlist;
+
+  portlist = portlist_tcp;
+  while (portlist)
+    {
+      if (portlist->status != SH_PORT_MISS)
+	portlist->status = SH_PORT_UNKN;
+      portlist = portlist->next;
+    }
+  portlist = portlist_udp;
+  while (portlist)
+    {
+      if (portlist->status != SH_PORT_MISS)
+	portlist->status = SH_PORT_UNKN;
+      portlist = portlist->next;
+    }
+  return;
+}
+
+static struct sh_portentry * sh_portchk_kill_list (struct sh_portentry * head)
+{
+  if (head)
+    {
+      if (head->next)
+	sh_portchk_kill_list (head->next);
+
+      if (head->service)
+	SH_FREE(head->service);
+      SH_FREE(head);
+    }
+  return NULL;
+}
+  
+/* check the list of open ports for any that are marked as UNKN
+ */
+static void sh_portchk_check_list (struct sh_portentry ** head, char * proto, int report)
+{
+  struct sh_portentry * ptr = *head;
+  struct sh_portentry * pre = *head;
+  char errbuf[256];
+
+  while (ptr)
+    {
+      if (portchk_debug && report)
+	fprintf(stderr, _("check list: port %d/%s %d %d\n"),
+		ptr->port, proto, ptr->flag, ptr->status);
+
+      if (ptr->status == SH_PORT_UNKN)
+	{
+	  /* Don't report missing ports that are marked as optional
+	   */
+	  if (ptr->flag != SH_PORT_OPT)
+	    {
+	      snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceMissing] port %s:%d/%s (%s)"), 
+			ptr->interface, ptr->port, proto, 
+			ptr->service ? ptr->service : check_services(ptr->port, proto));
+#ifdef TEST_ONLY
+	      if (report == SH_PORT_REPORT)
+		fprintf(stderr, _("%s\n"), errbuf);
+#else
+	      if (report == SH_PORT_REPORT)
+		sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
+				MSG_PORT_REPORT, errbuf);
+#endif
+	    }
+
+	  ptr->status = SH_PORT_MISS;
+
+	  if ((ptr->flag != SH_PORT_REQ) && (ptr->flag != SH_PORT_OPT))
+	    {
+	      if (portchk_debug && report)
+		fprintf(stderr, _("removing: port %d/%s %d %d\n"),
+			ptr->port, proto, ptr->flag, ptr->status);
+	      
+	      if (ptr == *head)
+		{
+		  *head = ptr->next;
+		  if (ptr->service)
+		    SH_FREE(ptr->service);
+		  SH_FREE(ptr);
+		  ptr = *head;
+		  pre = *head;
+		  continue;
+		}
+	      else if (ptr->next == NULL)
+		{
+		  pre->next = NULL;
+		  if (ptr->service)
+		    SH_FREE(ptr->service);
+		  SH_FREE(ptr);
+		  return;
+		}
+	      else
+		{
+		  pre->next = ptr->next;
+		  if (ptr->service)
+		    SH_FREE(ptr->service);
+		  SH_FREE(ptr);
+		  ptr = pre->next;
+		  continue;
+		}
+	    }
+	}
+      pre = ptr;
+      ptr = ptr->next;
+    }
+  return;
+}
+
+
+static struct sh_portentry * sh_portchk_get_from_list (char * proto, int port, 
+						       struct in_addr haddr, char * service)
+{
+  struct sh_portentry * portlist;
+  char iface_all[8] = _("0.0.0.0");
+  
+  if (0 == strcmp(proto, "tcp"))
+    portlist = portlist_tcp;
+  else
+    portlist = portlist_udp;
+
+  if (service)
+    {
+      while (portlist) 
+	{
+	  if (portlist->service && 
+	      0 == strcmp(service, portlist->service) &&
+	      (0 == strcmp(portlist->interface, inet_ntoa(haddr)) ||
+	       0 == strcmp(portlist->interface, iface_all)))
+	    return portlist;
+	  portlist = portlist->next;
+	}
+    }
+  else
+    {
+      while (portlist) 
+	{
+	  if (port == portlist->port &&
+	      (0 == strcmp(portlist->interface, inet_ntoa(haddr)) ||
+	       0 == strcmp(portlist->interface, iface_all)))
+	    return portlist;
+	  portlist = portlist->next;
+	}
+    }
+  return NULL;
+}
+      
+
+static void sh_portchk_cmp_to_list (char * proto, int port, struct in_addr haddr, char * service)
+{
+  struct sh_portentry * portent;
+  char errbuf[256];
+
+  
+  portent = sh_portchk_get_from_list (proto, port, haddr, service);
+
+  if (service)
+    {
+      if (!portent)
+	{
+	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"), 
+		    inet_ntoa(haddr), port, proto, service);
+#ifdef TEST_ONLY
+	  fprintf(stderr, _("open port: %s:%d/%s (%s)\n"), 
+		  inet_ntoa(haddr), port, proto, service);
+#else
+	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
+			  MSG_PORT_REPORT, errbuf);
+#endif
+	  /* 
+	   * was not there, thus it is not in 'required' or 'optional' list
+	   */
+	  sh_portchk_add_to_list (proto, port, haddr, service, SH_PORT_NOT, SH_PORT_ISOK);
+	}
+      else if (portent->status == SH_PORT_MISS)
+	{
+	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s to %d/%s (%s)"), 
+		    inet_ntoa(haddr), portent->port, proto, port, proto, service);
+#ifdef TEST_ONLY
+	  fprintf(stderr, _("service: %s\n"), errbuf);
+#else
+	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
+			  MSG_PORT_REPORT, errbuf);
+#endif
+
+	  portent->status = SH_PORT_ISOK;
+	}
+      else if (port != portent->port && (-1) != portent->port)
+	{
+	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServicePortSwitch] port %s:%d/%s to %d/%s (%s)"), 
+		    inet_ntoa(haddr), portent->port, proto, port, proto, service);
+#ifdef TEST_ONLY
+	  fprintf(stderr, _("service: %s\n"), errbuf);
+#else
+	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
+			  MSG_PORT_REPORT, errbuf);
+#endif
+
+	  portent->status = SH_PORT_ISOK;
+	}
+      else
+	{
+	  portent->status = SH_PORT_ISOK;
+	}
+    }
+  else
+    {
+      if (!portent)
+	{
+	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceNew] port %s:%d/%s (%s)"), 
+		    inet_ntoa(haddr), port, proto, check_services(port, proto));
+#ifdef TEST_ONLY
+	  fprintf(stderr, _("open port: %s:%d/%s (%s)\n"), 
+		  inet_ntoa(haddr), port, proto, check_services(port, proto));
+#else
+	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
+			  MSG_PORT_REPORT, errbuf);
+#endif
+
+	  /* was not there, thus it is not in 'required' or 'optional' list
+	   */
+	  sh_portchk_add_to_list (proto, port, haddr, service, SH_PORT_NOT, SH_PORT_ISOK);
+	}
+      else if (portent->status == SH_PORT_MISS)
+	{
+	  snprintf (errbuf, sizeof(errbuf), _("POLICY [ServiceRestarted] port %s:%d/%s (%s)"), 
+		    inet_ntoa(haddr), port, proto, check_services(port, proto));
+#ifdef TEST_ONLY
+	  fprintf(stderr, _("port   : %s\n"), errbuf);
+#else
+	  sh_error_handle(sh_portchk_severity, FIL__, __LINE__, 0, 
+			  MSG_PORT_REPORT, errbuf);
+#endif
+
+	  portent->status = SH_PORT_ISOK;
+	}
+      else
+	{
+	  portent->status = SH_PORT_ISOK;
+	}
+    }
+
+  return;
+}
+
+			       
+/* Returns a static buffer containing the name of the service
+ * running on port <port> (from /etc/services)
+ * Returns NULL on failure
+ */
+static char * check_services (int port, char * proto)
+{
+  static char buf[256];
+  struct servent * service = getservbyport(htons(port), proto);
+
+  if (service && service->s_name && service->s_name[0] != '\0')
+    {
+      snprintf (buf, sizeof(buf), _("maybe_%s"), service->s_name);
+    }
+  else
+    {
+      snprintf (buf, sizeof(buf), _("unknown"));
+    }
+  return buf;
+}
+
+/* Returns a static buffer containing the name of the service
+ * running on port <port> at <address> (from portmap daemon)
+ * Returns NULL on failure
+ */
+static char * check_rpc_list (int port, struct sockaddr_in * address, 
+			      unsigned long prot)
+{
+  struct pmaplist * head;
+  struct rpcent *r;
+  static char buf[256];
+
+  head = pmap_getmaps(address);
+
+  if (head) 
+    {
+      do /* while (head != NULL) */
+	{
+	  if ((head->pml_map.pm_prot == prot) && 
+	      (port == (int)head->pml_map.pm_port)) 
+	    {
+	      r = getrpcbynumber((int)head->pml_map.pm_prog);
+	      if (r && r->r_name && r->r_name[0] != '\0')
+		{
+		  snprintf (buf, sizeof(buf), "%s", r->r_name);
+		  return buf;
+		}
+	      else
+		{
+		  snprintf (buf, sizeof(buf), "RPC_%lu",
+			    (unsigned long)head->pml_map.pm_prog);
+		  return buf;
+		}
+	    }
+	  head = head->pml_next;
+	}
+      while (head != NULL);
+    }
+
+  return NULL;
+}
+
+static int check_port_udp_internal (int fd, int port, struct in_addr haddr)
+{
+  struct sockaddr_in sinr;
+  /* struct in_addr     haddr; */
+  int                retval;
+  char             * p;
+  char               buf[8];
+
+
+  /* inet_aton(interface, &haddr); */
+
+  sinr.sin_family = AF_INET;
+  sinr.sin_port   = htons (port);
+  sinr.sin_addr   = haddr;
+
+  retval = connect(fd, (struct sockaddr *) &sinr, sizeof(sinr));
+  if (retval == -1)
+    {
+#ifdef TEST_ONLY
+      if (portchk_debug)
+	perror(_("connect"));
+#else
+      sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+		      sh_error_message(errno), _("connect"));
+#endif
+    }
+  else
+    {
+      retval = send (fd, buf, 0, 0);
+
+      if (retval == -1 && errno == ECONNREFUSED)
+	{
+	  if (portchk_debug)
+	    fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"),
+		    port, inet_ntoa(haddr));
+	}
+      else 
+	{
+	  retval = send (fd, buf, 0, 0);
+	  if (retval == -1 && errno == ECONNREFUSED)
+	    {
+	      if (portchk_debug)
+		fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"),
+			port, inet_ntoa(haddr));
+	    }
+	  else if (retval != -1)
+	    {
+	      /* Try to get service name from portmap
+	       */
+	      p = check_rpc_list (port, &sinr, IPPROTO_UDP);
+	      
+	      sh_portchk_cmp_to_list ("udp", port, haddr, p ? p : NULL);
+	      
+	      /* If not an RPC service, try to get name from /etc/services
+	       */
+	      if (!p)
+		p = check_services(port, "udp");
+	      
+	      if (portchk_debug)
+		fprintf(stderr, _("check port: %5d/udp on %15s open %s\n"), 
+			port, inet_ntoa(haddr), p);
+	      
+	    }
+	}
+    }
+  close (fd);
+  return 0;
+}
+
+static int check_port_tcp_internal (int fd, int port, struct in_addr haddr)
+{
+  struct sockaddr_in sinr;
+  /* struct in_addr     haddr; */
+  int                retval;
+  char             * p;
+
+
+  /* inet_aton(interface, &haddr); */
+
+  sinr.sin_family = AF_INET;
+  sinr.sin_port   = htons (port);
+  sinr.sin_addr   = haddr;
+
+  retval = connect(fd, (struct sockaddr *) &sinr, sizeof(sinr));
+  if (retval == -1 && errno == ECONNREFUSED)
+    {
+      if (portchk_debug)
+	fprintf(stderr, _("check port: %5d on %15s established/time_wait\n"),
+		port, inet_ntoa(haddr));
+    }
+  else if (retval == -1)
+    {
+#ifdef TEST_ONLY
+      if (portchk_debug)
+	perror(_("connect"));
+#else
+      sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+		      sh_error_message(errno), _("connect"));
+#endif
+    }
+  else
+    {
+      /* Try to get service name from portmap
+       */
+      p = check_rpc_list (port, &sinr, IPPROTO_TCP);
+
+      sh_portchk_cmp_to_list ("tcp", port, haddr, p ? p : NULL);
+
+      /* If not an RPC service, try to get name from /etc/services
+       */
+      if (!p)
+	p = check_services(port, "tcp");
+
+      if (portchk_debug)
+	fprintf(stderr, _("check port: %5d on %15s open %s\n"), 
+		port, inet_ntoa(haddr), p);
+     }
+  close (fd);
+  return 0;
+}
+
+/* typedef uint32_t in_addr_t;
+ * struct in_addr
+ * {
+ * in_addr_t s_addr;
+ * };
+ */
+
+#define SH_IFACE_MAX 16
+
+struct portchk_interfaces {
+  struct in_addr iface[SH_IFACE_MAX];
+  int            used;
+};
+
+static struct portchk_interfaces iface_list;
+static int iface_initialized = 0;
+
+#ifdef TEST_ONLY
+static char * portchk_hostname = NULL;
+#else
+static char * portchk_hostname = sh.host.name;
+#endif
+
+int sh_portchk_init ()
+{
+  struct hostent * hent;
+  int              i = 0;
+  char errbuf[256];
+
+  if (portchk_debug)
+    fprintf(stderr, _("checking ports on: %s\n"), portchk_hostname ? portchk_hostname : _("NULL"));
+  if (!portchk_hostname)
+    return -1;
+
+  if (iface_initialized == 0)
+    {
+      iface_list.used   = 0;
+      iface_initialized = 1;
+    }
+	    
+  hent = gethostbyname(portchk_hostname);
+
+  while (hent && hent->h_addr_list[i] && (iface_list.used < SH_IFACE_MAX))
+    {
+      memcpy (&(iface_list.iface[iface_list.used].s_addr), hent->h_addr_list[i], sizeof(in_addr_t));
+      sl_snprintf(errbuf, sizeof(errbuf), _("interface: %s"), 
+		  inet_ntoa(iface_list.iface[iface_list.used]));
+      sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+		      errbuf, _("sh_portchk_init"));
+
+      ++iface_list.used;
+      ++i;
+    }
+
+  return 0;
+}
+
+#if !defined(TEST_ONLY)
+int sh_portchk_reconf ()
+{
+  iface_initialized   = 0;
+
+  portlist_udp = sh_portchk_kill_list (portlist_udp);
+  portlist_tcp = sh_portchk_kill_list (portlist_tcp);
+  return 0;
+}
+
+int sh_portchk_cleanup ()
+{
+  return sh_portchk_reconf ();
+}
+
+int sh_portchk_timer (time_t tcurrent) 
+{
+  static time_t lastcheck = 0;
+
+  SL_ENTER(_("sh_portchk_timer"));
+  if ((time_t) (tcurrent - lastcheck) >= sh_portchk_interval)
+    {
+      lastcheck  = tcurrent;
+      SL_RETURN((-1), _("sh_portchk_timer"));
+    }
+  SL_RETURN(0, _("sh_portchk_timer"));
+}
+#endif
+
+static int check_port_generic (int port, int type, int protocol)
+{
+  int              i    =  0;
+  int              sock = -1;
+  int              flag =  1; /* non-zero to enable an option */
+  struct in_addr   haddr;
+ 
+  /* Check all interfaces for this host
+   */
+  while (i < iface_list.used)
+    {
+      if ((sock = socket(AF_INET, type, protocol)) < 0 )
+	{
+#ifdef TEST_ONLY
+	  if (portchk_debug)
+	    perror(_("socket"));
+#else
+	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+			  sh_error_message(errno), _("socket"));
+#endif
+	}
+      if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+		      (void *) &flag, sizeof(flag)) < 0 )
+	{
+#ifdef TEST_ONLY
+	  if (portchk_debug)
+	    perror(_("setsockopt"));
+#else
+	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+			  sh_error_message(errno), _("setsockopt"));
+#endif
+	}
+
+      memcpy (&(haddr.s_addr), &(iface_list.iface[i].s_addr), sizeof(in_addr_t));
+
+      if (protocol == IPPROTO_TCP)
+	check_port_tcp_internal(sock, port, haddr);
+      else
+	check_port_udp_internal(sock, port, haddr);
+
+      ++i;
+    }
+
+  return 0;
+}
+
+
+
+static int check_port_udp (int port)
+{
+  return check_port_generic(port, SOCK_DGRAM, IPPROTO_UDP);
+}
+
+static int check_port_tcp (int port)
+{
+  return check_port_generic(port, SOCK_STREAM, IPPROTO_TCP);
+}
+
+
+
+static int sh_portchk_scan_ports_generic (int min_port, int max_port, int type, int protocol)
+{
+  /*
+  int min_port = 1024;
+  int max_port = 65535;
+  */
+
+  int port;
+  int retval;
+  int sock   = -1;
+  int flag   = 1; /* non-zero to enable an option */
+
+  struct sockaddr_in addr;
+  int addrlen      = sizeof(addr);
+
+  if (min_port == -1)
+     min_port = 0;
+  if (max_port == -1)
+    max_port = 65535;
+
+  for (port = min_port; port <= max_port; ++port) 
+    {
+
+      if ((sock = socket(AF_INET, type, protocol)) < 0 )
+	{
+#ifdef TEST_ONLY
+	  if (portchk_debug)
+	    perror(_("socket"));
+#else
+	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+			  sh_error_message(errno), _("socket"));
+#endif
+	}
+      if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+		      (void *) &flag, sizeof(flag)) < 0 )
+	{
+#ifdef TEST_ONLY
+	  if (portchk_debug)
+	    perror(_("setsockopt"));
+#else
+	  sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+			  sh_error_message(errno), _("setsockopt"));
+#endif
+	}
+
+      addr.sin_family      = AF_INET;
+      addr.sin_port        = htons(port);
+      addr.sin_addr.s_addr = INADDR_ANY;
+
+      retval = bind (sock, (struct sockaddr *) &addr, addrlen);
+
+      if (retval == 0)
+	{
+	  /* we can bind the port, thus it is unused
+	   */
+	  close (sock);
+	}
+      else
+	{
+	  if (errno == EINVAL || errno == EADDRINUSE)
+	    {
+	      /* try to connect to the port
+	       */
+	      if (protocol == IPPROTO_TCP)
+		check_port_tcp(port);
+	      else
+		check_port_udp(port);
+	    }
+	  else
+	    {
+#ifdef TEST_ONLY
+	      if (portchk_debug)
+		perror(_("bind"));
+#else
+	      sh_error_handle((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN, 
+			      sh_error_message(errno), _("bind"));
+#endif
+	    }
+	  close (sock);
+	}
+    }
+  return 0;
+}
+
+static int sh_portchk_scan_ports_tcp (int min_port, int max_port)
+{
+  return sh_portchk_scan_ports_generic (min_port, max_port, SOCK_STREAM, IPPROTO_TCP);
+}
+ 
+static int sh_portchk_scan_ports_udp (int min_port, int max_port)
+{
+  return sh_portchk_scan_ports_generic (min_port, max_port, SOCK_DGRAM, IPPROTO_UDP);
+}
+
+/* Subroutine to add an interface
+ */
+static int sh_portchk_add_interface (const char * str)
+{
+  struct in_addr   haddr;
+  char errbuf[256];
+
+  if (iface_initialized == 0)
+    {
+      iface_list.used   = 0;
+      iface_initialized = 1;
+    }
+
+  if (0 == inet_aton(str, &haddr))
+    return -1;
+
+  if (iface_list.used == SH_IFACE_MAX)
+    return -1;
+
+  sl_snprintf(errbuf, sizeof(errbuf), _("interface: %s"), inet_ntoa(haddr));
+  sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+		  errbuf, _("sh_portchk_add_interface"));
+
+  memcpy (&(iface_list.iface[iface_list.used].s_addr), &(haddr.s_addr), sizeof(in_addr_t));
+  ++iface_list.used;
+
+  return 0;
+}
+
+ 
+/* Subroutine to add a required or optional port/service
+ */
+static int sh_portchk_add_required_port_generic (char * service, char * interface, int type)
+{
+  char buf[256];
+  char proto[4];
+  char * p;
+  char * endptr;
+  unsigned long int  port;
+  struct in_addr   haddr;
+  struct sh_portentry * portent;
+
+  if (0 == inet_aton(interface, &haddr))
+    return -1;
+
+  sl_strlcpy (buf, service, sizeof(buf));
+
+  p = strchr(buf, '/');
+  if (!p)
+    return -1;
+  if (0 == strcmp(p, _("/tcp")))
+    sl_strlcpy(proto, _("tcp"), sizeof(proto));
+  else if  (0 == strcmp(p, _("/udp")))   
+    sl_strlcpy(proto, _("udp"), sizeof(proto));
+  else
+    return -1;
+
+  *p = '\0';
+  port = strtoul(buf, &endptr, 0);
+
+  if (*endptr != '\0')
+    {  
+      portent = sh_portchk_get_from_list (proto, -1, haddr, buf);
+      if (!portent)
+	sh_portchk_add_to_list (proto,   -1, haddr,  buf, type, SH_PORT_UNKN);
+      else
+	{
+#ifdef TEST_ONLY
+	  fprintf(stderr, "** WARNING: duplicate port definition %s/%s\n", buf, proto);
+#else
+	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+			  _("duplicate port definition"), _("sh_portchk_add_required_port_generic"));
+#endif
+	  return -1;
+	}
+    }
+  else if (port <= 65535)
+    {
+      portent = sh_portchk_get_from_list (proto, port, haddr, NULL);
+      if (!portent)
+	sh_portchk_add_to_list (proto, port, haddr, NULL, type, SH_PORT_UNKN);
+      else
+	{
+#ifdef TEST_ONLY
+	  fprintf(stderr, "** WARNING: duplicate port definition %lu/%s\n", port, proto);
+#else
+	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+			  _("duplicate port definition"), _("sh_portchk_add_required_port_generic"));
+#endif
+	  return -1;
+	}
+    }
+  else
+    return -1;
+
+  return 0;
+}
+
+/* Internal interface to add required or optional ports as 'iface:portlist'
+ */
+static int sh_portchk_add_required_generic (const char * str, int type)
+{
+  size_t len;
+  size_t ll = 0;
+
+  char * interface = NULL;
+  char * list;
+  char * p;
+
+  if (!str)
+    return -1;
+
+  if (strchr(str, ':'))
+    {
+      len = strlen(str);
+      for (ll = 0; ll < len; ++ll)
+	{
+	  if (str[ll] == ':' || str[ll] == ' ' || str[ll] == '\t')
+	    {
+	      interface = SH_ALLOC(ll+1);
+	      sl_strlcpy(interface, str, ll+1);
+	      interface[ll] = '\0';
+	      while (str[ll] == ':' || str[ll] == ' ' || str[ll] == '\t')
+		++ll;
+	      break;
+	    }
+	}
+    }
+  else
+    {
+      interface = SH_ALLOC(8);
+      sl_strlcpy(interface, _("0.0.0.0"), 8);
+      interface[7] = '\0';
+      while (str[ll] == ' ' || str[ll] == '\t')
+	++ll;      
+    }
+
+  if (!interface)
+    return -1;
+
+  if (str[ll] == '\0')
+    {
+      SH_FREE(interface);
+      return -1;
+    }
+
+  if (portchk_debug)
+    fprintf(stderr, "add ports for interface: %s\n", interface);
+
+  list = sh_util_strdup(&str[ll]);
+  p    = strtok (list, " ,\t");
+  if (!p)
+    {
+      SH_FREE(interface);
+      SH_FREE(list);
+      return -1;
+    }
+  while (p)
+    {
+      if (-1 == sh_portchk_add_required_port_generic (p, interface, type))
+	{
+	  SH_FREE(interface);
+	  SH_FREE(list);
+	  return -1;
+	}
+      p    = strtok (NULL, " ,\t");
+    }
+  SH_FREE(interface);
+  SH_FREE(list);
+  return 0;
+}
+
+/* User interface to add required ports as 'iface:portlist'
+ */
+static int sh_portchk_add_required (const char * str)
+{
+  return sh_portchk_add_required_generic (str, SH_PORT_REQ); 
+}
+
+/* User interface to add optional ports as 'iface:portlist'
+ */
+static int sh_portchk_add_optional (const char * str)
+{
+  return sh_portchk_add_required_generic (str, SH_PORT_OPT); 
+}
+
+/* Interface to run port check
+ */
+int sh_portchk_check ()
+{
+  int min_port = 0;
+
+  sh_portchk_reset_lists();
+  if (0 != geteuid())
+    {
+      min_port = 1024;
+#ifdef TEST_ONLY
+      fprintf(stderr, "** WARNING not scanning ports < 1024\n");
+#else
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+		      _("not scanning ports below 1024"), _("sh_portchk_check"));
+#endif
+    }
+  if (sh_portchk_check_udp == 1)
+    sh_portchk_scan_ports_udp(min_port, -1);
+  sh_portchk_scan_ports_tcp(min_port, -1);
+  sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_REPORT);
+  if (sh_portchk_check_udp == 1)
+    sh_portchk_check_list (&portlist_udp, "udp", SH_PORT_REPORT);
+  return 0;
+}
+#endif
+
+#ifdef SH_CUTEST
+#include "CuTest.h"
+
+void Test_portcheck_lists (CuTest *tc)
+{
+#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
+  struct in_addr   haddr_local;
+  struct sh_portentry * portent;
+
+  CuAssertTrue(tc, 0 != inet_aton("127.0.0.1", &haddr_local));
+
+  sh_portchk_add_to_list ("tcp",  8000, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN);
+
+  portent = sh_portchk_get_from_list("tcp",  8000, haddr_local, NULL);
+  CuAssertPtrNotNull(tc, portent);
+
+  CuAssertTrue(tc, portent->port == 8000);
+  CuAssertTrue(tc, 0 == strcmp("127.0.0.1", portent->interface));
+  CuAssertTrue(tc, portent->status == SH_PORT_UNKN);
+  CuAssertTrue(tc, portent->flag == SH_PORT_NOT);
+
+  sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_NOREPT);
+
+  CuAssertTrue(tc, NULL == portlist_tcp);
+
+  sh_portchk_add_to_list ("tcp",  8000, haddr_local, NULL, SH_PORT_REQ, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",  8001, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",  8002, haddr_local, NULL, SH_PORT_REQ, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",  8003, haddr_local, NULL, SH_PORT_NOT, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",    -1, haddr_local, "foo1", SH_PORT_NOT, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",    -1, haddr_local, "foo2", SH_PORT_REQ, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",    -1, haddr_local, "foo3", SH_PORT_NOT, SH_PORT_UNKN);
+  sh_portchk_add_to_list ("tcp",    -1, haddr_local, "foo4", SH_PORT_REQ, SH_PORT_UNKN);
+
+  sh_portchk_check_list (&portlist_tcp, "tcp", SH_PORT_NOREPT);
+
+  CuAssertPtrNotNull(tc, portlist_tcp);
+
+  portent = sh_portchk_get_from_list("tcp",  8000, haddr_local, NULL);
+  CuAssertPtrNotNull(tc, portent);
+
+  portent = sh_portchk_get_from_list("tcp",  8001, haddr_local, NULL);
+  CuAssertTrue(tc, NULL == portent);
+
+  portent = sh_portchk_get_from_list("tcp",  8002, haddr_local, NULL);
+  CuAssertPtrNotNull(tc, portent);
+
+  portent = sh_portchk_get_from_list("tcp",  8003, haddr_local, NULL);
+  CuAssertTrue(tc, NULL == portent);
+
+  portent = sh_portchk_get_from_list("tcp",  8000, haddr_local, "foo1");
+  CuAssertTrue(tc, NULL == portent);
+
+  portent = sh_portchk_get_from_list("tcp",  8000, haddr_local, "foo2");
+  CuAssertPtrNotNull(tc, portent);
+  CuAssertTrue(tc, 0 == strcmp(portent->service, "foo2"));
+
+  portent = sh_portchk_get_from_list("tcp",  8000, haddr_local, "foo3");
+  CuAssertTrue(tc, NULL == portent);
+
+  portent = sh_portchk_get_from_list("tcp",  8000, haddr_local, "foo4");
+  CuAssertPtrNotNull(tc, portent);
+  CuAssertTrue(tc, 0 == strcmp(portent->service, "foo4"));
+#else
+  (void) tc; /* fix compiler warning */
+#endif
+  return;
+}
+#endif
+
+#ifdef TEST_ONLY
+
+void usage (char * pname)
+{
+  printf ("%s [-r|--required interface:portlist][-o|--optional interface:portlist][--no-udp][-d|--debug] hostname\n\n", pname);
+  printf ("   Check local host for open ports; Version %s\n\n", PORTCHK_VERSION);
+  printf ("   Interface: Numeric address for an interface, e.g. 127.0.0.1\n");
+  printf ("   Portlist:  List of ports or services, e.g. 22/tcp,nfs/udp,nlockmgr/udp\n");
+  printf ("     required -> must be open\n");
+  printf ("     optional ->  may be open or closed\n");
+  printf ("   RPC services must be specified with service **name**, others with **port number**\n\n");
+  printf ("   Example:\n");
+  printf ("      %s --required 192.168.1.2:22/tcp,nfs/udp,nlockmgr/udp\n\n", pname);
+  return;
+}
+
+int main(int argc, char *argv[])
+{
+  char * pname = argv[0];
+
+
+  /* 
+  test_lists();
+
+  portlist_tcp = sh_portchk_kill_list (portlist_tcp);
+  portlist_udp = sh_portchk_kill_list (portlist_udp);
+  */
+
+  // sh_portchk_add_required ("127.0.0.1 : nlockmgr/tcp, 5308/tcp, nfs/tcp");
+
+  while (argc > 1 && argv[1][0] == '-')
+    {
+      if (0 == strcmp(argv[1], "--help") || 0 == strcmp(argv[1], "-h"))
+	{
+	  usage(pname);
+	  exit (0);
+	}
+      else if (0 == strcmp(argv[1], "--required") || 0 == strcmp(argv[1], "-r"))
+	{
+	  if (argc < 3)
+	    {
+	      usage(pname);
+	      exit (1);
+	    }
+	  sh_portchk_add_required (argv[2]);
+	  --argc; ++argv;
+	}
+      else if (0 == strcmp(argv[1], "--optional") || 0 == strcmp(argv[1], "-o"))
+	{
+	  if (argc < 3)
+	    {
+	      usage(pname);
+	      exit (1);
+	    }
+	  sh_portchk_add_optional (argv[2]);
+	  --argc; ++argv;
+	}
+      else if (0 == strcmp(argv[1], "--no-udp"))
+	{
+	  sh_portchk_check_udp = 0;
+	}
+      else if (0 == strcmp(argv[1], "--debug") || 0 == strcmp(argv[1], "-d"))
+	{
+	  portchk_debug = 1;
+	}
+      else
+	{
+	  usage(pname);
+	  exit (1);
+	}
+      --argc; ++argv;
+    }
+
+  if (argc < 2)
+    {
+      usage(pname);
+      exit (1);
+    }
+
+  portchk_hostname = argv[1];
+      
+  if (0 != sh_portchk_init ())
+    {
+      usage(pname);
+      exit (1);
+    }
+
+  sh_portchk_check();
+
+  /*
+  sleep(10);
+
+  sh_portchk_check();
+  */
+
+  return 0;
+}
+#endif
Index: trunk/src/sh_processcheck.c
===================================================================
--- trunk/src/sh_processcheck.c	(revision 67)
+++ trunk/src/sh_processcheck.c	(revision 67)
@@ -0,0 +1,1207 @@
+/* SAMHAIN file system integrity testing                                   */
+/* Copyright (C) 2006 Rainer Wichmann                                      */
+/*                                                                         */
+/*  This program is free software; you can redistribute it                 */
+/*  and/or modify                                                          */
+/*  it under the terms of the GNU General Public License as                */
+/*  published by                                                           */
+/*  the Free Software Foundation; either version 2 of the License, or      */
+/*  (at your option) any later version.                                    */
+/*                                                                         */
+/*  This program is distributed in the hope that it will be useful,        */
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*  GNU General Public License for more details.                           */
+/*                                                                         */
+/*  You should have received a copy of the GNU General Public License      */
+/*  along with this program; if not, write to the Free Software            */
+/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
+
+/***************************************************************************
+ *
+ * This file provides a module for samhain to check for hidden/faked/missing
+ * processes on the host.
+ *
+ */
+
+#include "config_xor.h"
+
+#define _XOPEN_SOURCE 500
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <unistd.h>
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+#include <sched.h>
+#endif
+
+#ifdef HAVE_GETPRIORITY
+#include <errno.h>
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+#include "samhain.h"
+#include "sh_modules.h"
+#include "sh_processcheck.h"
+#include "sh_utils.h"
+#include "sh_error.h"
+#include "sh_extern.h"
+
+#ifdef SH_USE_PROCESSCHECK
+
+#define FIL__  _("sh_processcheck.c")
+
+#ifdef __linux__
+#define HAVE_THREADS
+#endif
+
+/* We won't want to build this into yule 
+ */
+#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
+
+/* sh_prochk_maxpid is one more than the largest pid
+ */
+static  size_t  sh_prochk_minpid = 0x0001;
+static  size_t  sh_prochk_maxpid = 0x8000;
+static  size_t  sh_prochk_size   = 0;
+
+static  int     ShProchkActive  = S_TRUE;
+static  short * sh_prochk_res   = NULL; 
+
+static  char  * sh_prochk_pspath = NULL;
+static  char  * sh_prochk_psarg  = NULL;
+
+#define SH_PROCHK_INTERVAL 300
+static time_t   sh_prochk_interval = SH_PROCHK_INTERVAL;
+static int      sh_prochk_severity = SH_ERR_SEVERE;
+
+
+static int sh_prochk_set_maxpid  (const char * str);
+static int sh_prochk_set_minpid  (const char * str);
+static int sh_prochk_set_active  (const char *str);
+static int sh_prochk_add_process (const char *str);
+static int sh_prochk_set_pspath  (const char *str);
+static int sh_prochk_set_psarg   (const char *str);
+static int sh_prochk_set_interval(const char *str);
+static int sh_prochk_set_severity(const char *str);
+
+sh_rconf sh_prochk_table[] = {
+    {
+        N_("severityprocesscheck"),
+        sh_prochk_set_severity,
+    },
+    {
+        N_("processcheckexists"),
+        sh_prochk_add_process,
+    },
+    {
+        N_("processcheckactive"),
+        sh_prochk_set_active,
+    },
+    {
+        N_("processcheckminpid"),
+        sh_prochk_set_minpid,
+    },
+    {
+        N_("processcheckmaxpid"),
+        sh_prochk_set_maxpid,
+    },
+    {
+        N_("processcheckpspath"),
+        sh_prochk_set_pspath,
+    },
+    {
+        N_("processcheckpsarg"),
+        sh_prochk_set_psarg,
+    },
+    {
+        N_("processcheckinterval"),
+        sh_prochk_set_interval,
+    },
+    {
+        NULL,
+        NULL
+    }
+};
+
+#define    SH_PROC_MISSING 1
+#define    SH_PROC_FAKED   2
+#define    SH_PROC_HIDDEN  4
+#define    SH_PROC_EXISTS  8
+
+#ifndef HAVE_LSTAT
+#define lstat(x,y) stat(x,y)
+#endif /* HAVE_LSTAT */
+
+static const short SH_PR_PS       = 0x0001;
+
+static const short SH_PR_GETSID   = 0x0002;
+static const short SH_PR_KILL     = 0x0004;
+static const short SH_PR_GETPGID  = 0x0008;
+
+static const short SH_PR_LSTAT    = 0x0010;
+static const short SH_PR_OPENDIR  = 0x0020;
+static const short SH_PR_CHDIR    = 0x0040;
+static const short SH_PR_SCHED    = 0x0080;
+
+static const short SH_PR_PRIORITY = 0x0100;
+
+static const short SH_PR_PS2      = 0x0200;
+static const short SH_PR_PS_ANY   = 0x0400;
+static const short SH_PR_ALL      = 0x0800;
+static const short SH_PR_ANY      = 0x1000;
+
+struct watchlist {
+  char        * str;
+  unsigned long pid;
+#ifdef HAVE_REGEX_H
+  regex_t       preg;
+#endif
+  int           seen;
+
+  struct watchlist *next;
+};
+
+static struct watchlist * process_check = NULL;
+
+static struct watchlist * list_missing  = NULL;
+static struct watchlist * list_fake     = NULL;
+static struct watchlist * list_hidden   = NULL;
+
+/* recursively remove all list entries
+ */
+static void kill_list (struct watchlist * head)
+{
+  if (head->next)
+    kill_list (head->next);
+
+  if (head->str)
+    SH_FREE(head->str);
+  SH_FREE(head);
+
+  return;
+}
+
+  
+/* check the list for old entries; clean out old entries; reset others
+ * Return number of non-obsolete entries
+ */
+static size_t clean_list (struct watchlist ** head_ptr)
+{
+  size_t count = 0;
+  struct watchlist * ptr = *head_ptr;
+  struct watchlist * pre = *head_ptr;
+
+  while (ptr)
+    {
+      if (ptr->seen == S_FALSE) /* obsolete entry */
+	{
+	  if (ptr == pre)       /* at head        */
+	    {
+	      ptr       = pre->next;
+	      *head_ptr = pre->next;
+	      if (pre->str) 
+		SH_FREE(pre->str);
+	      SH_FREE(pre);
+	      pre       = ptr;
+	    }
+	  else
+	    {
+	      pre->next = ptr->next;
+	      if (ptr->str) 
+		SH_FREE(ptr->str);
+	      SH_FREE(ptr);
+	      ptr       = pre->next;
+	    }
+	}
+      else
+	{
+	  ++count;
+	  ptr->seen = S_FALSE; /* reset status */
+	  pre = ptr;
+	  ptr = ptr->next;
+	}
+    }
+  return count;
+}
+
+/* check if process is in list; if not, add it and return false
+ */
+static int  is_in_list (struct watchlist ** head_ptr, 
+			char * str, unsigned long pid)
+{
+  struct watchlist * ptr = *head_ptr;
+
+  if (str)
+    {
+      while (ptr)
+	{
+	  if (ptr->str && (0 == strcmp(str, ptr->str)))
+	    {
+	      ptr->seen = S_TRUE;
+	      return S_TRUE;
+	    }
+	  ptr = ptr->next;
+	}
+    }
+  else
+    {
+      while (ptr)
+	{
+	  if (ptr->pid == pid)
+	    {
+	      ptr->seen = S_TRUE;
+	      return S_TRUE;
+	    }
+	  ptr = ptr->next;
+	}
+    }
+
+  ptr = SH_ALLOC(sizeof(struct watchlist));
+
+  if (str)
+    {
+      ptr->str = sh_util_strdup(str);
+    }
+  else
+    {
+      ptr->str = NULL;
+      ptr->pid = pid;
+    }
+  ptr->next = *head_ptr;
+  ptr->seen = S_TRUE;
+  *head_ptr = ptr;
+
+  return S_FALSE;
+}
+
+static int is_in_watchlist (const char *str, unsigned long num)
+{
+  struct watchlist * list = process_check;
+
+  while (list) 
+    {
+#ifdef HAVE_REGEX_H
+      if (0 == regexec(&(list->preg), str, 0, NULL, 0))
+	{
+	  list->seen = S_TRUE;
+	  list->pid  = num;
+	  return S_TRUE;
+	}
+#else
+      if (strstr(str, list->str)) 
+	{
+	  list->seen = S_TRUE;
+	  list->pid  = num;
+	  return S_TRUE;
+	}
+#endif
+      list = list->next;
+    }
+  return S_FALSE;
+} 
+
+static void check_watchlist (short * res)
+{
+  struct watchlist * list = process_check;
+  char * tmp;
+  size_t indx;
+
+  while (list) 
+    {
+      if (list->seen == S_FALSE)
+	{
+	  /* avoid repetition of messages
+	   */
+	  if (S_FALSE == is_in_list(&list_missing, list->str, 0))
+	    {
+	      sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0, 
+			      MSG_PCK_MISS,
+			      list->str);
+	    }
+	}
+      else
+	{
+	  indx = list->pid - sh_prochk_minpid;
+
+	  if (list->pid < sh_prochk_maxpid && list->pid >= sh_prochk_minpid && 
+	      ((res[indx] & SH_PR_ANY) == 0) && /* not found         */
+	      ((res[indx] & SH_PR_PS)  != 0) && /* seen in first ps  */ 
+	      ((res[indx] & SH_PR_PS2) != 0))   /* seen in second ps */
+	    {
+	      /* fake process, thus considered missing
+	       */
+	      if (S_FALSE == is_in_list(&list_missing, list->str, 0))
+		{
+		  tmp = sh_util_safe_name (list->str);
+		  sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0, 
+				  MSG_PCK_MISS, 
+				  tmp);
+		  SH_FREE(tmp);
+		}
+	    }
+	  list->seen = S_FALSE;
+	}
+      list = list->next;
+    }
+}
+
+/* Add 'str' to the list of watched processes for which
+ * existence should be checked.
+ */
+int sh_prochk_add_process (const char *str) 
+{
+  struct watchlist *new;
+  int               status;
+  char              errbuf[256];
+    
+  SL_ENTER(_("sh_prochk_add_process"));
+
+  if( str == NULL )
+    SL_RETURN(-1, _("sh_prochk_add_process") );
+
+  new       = SH_ALLOC(sizeof(struct watchlist));
+  new->next = process_check;
+  new->str  = sh_util_strdup(str);
+#ifdef HAVE_REGEX_H
+  status = regcomp(&(new->preg), str, REG_NOSUB|REG_EXTENDED);
+  if (status != 0)
+    {
+      regerror(status, &(new->preg), errbuf, sizeof(errbuf));
+      sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN, 
+		      errbuf, _("sh_processes_runps"));
+      SH_FREE(new->str);
+      SH_FREE(new);
+      SL_RETURN(-1, _("sh_prochk_add_process") );
+    }
+#endif
+  new->pid  = 0;
+  new->seen = S_FALSE;
+
+  process_check = new;
+  SL_RETURN(0, _("sh_prochk_add_process") );
+}
+
+/* severity
+ */
+int sh_prochk_set_severity  (const char * c)
+{
+  char tmp[32];
+  tmp[0] = '='; tmp[1] = '\0';
+  sl_strlcat (tmp, c, 32);
+  return sh_error_set_level (tmp, &sh_prochk_severity);
+}
+
+
+
+/* Path to ps
+ */
+int sh_prochk_set_pspath(const char *str) 
+{
+  SL_ENTER(_("sh_prochk_set_pspath"));
+
+  if (!str && ('/' != str[0]))
+    SL_RETURN((-1), _("sh_prochk_set_psarg"));
+  if (sh_prochk_pspath)
+    SH_FREE(sh_prochk_pspath);
+  sh_prochk_pspath = sh_util_strdup (str);
+
+  SL_RETURN((0), _("sh_prochk_set_pspath"));
+
+}
+
+/* argument for ps
+ */
+int sh_prochk_set_psarg(const char *str) 
+{
+  SL_ENTER(_("sh_prochk_set_psarg"));
+
+  if (sh_prochk_psarg)
+    SH_FREE(sh_prochk_psarg);
+  sh_prochk_psarg = sh_util_strdup (str);
+
+  SL_RETURN((0), _("sh_prochk_set_psarg"));
+
+}
+
+
+/* Decide if we're active.
+ */
+int sh_prochk_set_active(const char *str) 
+{
+  int value;
+    
+  SL_ENTER(_("sh_prochk_set_active"));
+
+  value = sh_util_flagval(str, &ShProchkActive);
+
+  SL_RETURN((value), _("sh_prochk_set_active"));
+}
+
+/* Minimum PID
+ */
+int sh_prochk_set_minpid(const char * str)
+{
+  size_t  value;
+  char * foo;
+  int    retval = 0;
+
+  SL_ENTER(_("sh_prochk_set_minpid"));
+
+  value = (size_t) strtoul(str, &foo, 0);
+  if (*foo != '\0')
+    retval = -1;
+  else
+    sh_prochk_minpid = value;
+
+  SL_RETURN((retval), _("sh_prochk_set_minpid"));
+}
+
+/* Maximum PID
+ */
+static int userdef_maxpid = 0;
+
+int sh_prochk_set_maxpid(const char * str)
+{
+  size_t  value;
+  char * foo;
+  int    retval = 0;
+
+  SL_ENTER(_("sh_prochk_set_maxpid"));
+
+  value = (size_t) strtoul(str, &foo, 0);
+  if (*foo != '\0') {
+    retval = -1;
+  } else {
+    sh_prochk_maxpid = value + 1;
+    userdef_maxpid   = 1;
+  }
+
+  SL_RETURN((retval), _("sh_prochk_set_maxpid"));
+}
+
+int sh_prochk_set_interval (const char * c)
+{
+  int retval = 0;
+  long val;
+
+  SL_ENTER(_("sh_prochk_set_interval"));
+  val = strtol (c, (char **)NULL, 10);
+  if (val <= 0)
+    {
+      sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
+		       _("process check interval"), c);
+      retval = -1;
+    }
+
+  val = (val <= 0 ? 60 : val);
+
+  sh_prochk_interval = (time_t) val;
+  SL_RETURN(0, _("sh_prochk_set_interval"));
+}
+
+
+
+/* Recurse to the end of the list and then free the data as we return
+ * back up towards the start, making sure to free any strdupped strings
+ */
+static void sh_prochk_free_list(struct watchlist *head) 
+{
+  if ( head != NULL ) 
+    {
+      sh_prochk_free_list(head->next);
+      if (head->str)
+	SH_FREE(head->str);
+#ifdef HAVE_REGEX_H
+      regfree(&(head->preg));
+#endif
+      SH_FREE(head);
+    }
+  return;
+}
+
+#if defined(__linux__)
+#define PROC_PID_MAX _("/proc/sys/kernel/pid_max")
+
+static int proc_max_pid (size_t * procpid)
+{
+  unsigned long  pid;
+  FILE * fd;
+  char   str[128];
+  char * ptr;
+
+  SL_ENTER(_("proc_max_pid"));
+
+  if (userdef_maxpid != 0)
+    SL_RETURN((-1), _("proc_max_pid"));
+    
+  if (0 == access(PROC_PID_MAX, R_OK))
+    {
+      if (NULL != (fd = fopen(PROC_PID_MAX, "r")))
+	{
+	  str[0] = '\0';
+	  fgets(str, 128, fd);
+	  if (*str != '\0')
+	    {
+	      pid = strtoul(str, &ptr, 0);
+	      if (*ptr == '\0' || *ptr == '\n')
+		{
+		  fclose(fd);
+		  *procpid = (size_t) pid;
+		  SL_RETURN(0, _("proc_max_pid"));
+		}
+	    }
+	  fclose(fd);
+	}
+    }
+  SL_RETURN((-1), _("proc_max_pid"));
+}
+#else
+static int proc_max_pid(size_t * dummy)
+{
+  (void) dummy;
+  return -1;
+}
+#endif
+
+static void sh_processes_tlist (char * list, size_t len, short res)
+{
+  if (res & SH_PR_PS)       sl_strlcat(list, _(" ps(initial)"), len);
+  if (res & SH_PR_CHDIR)    sl_strlcat(list, _(" chdir"), len);
+  if (res & SH_PR_OPENDIR)  sl_strlcat(list, _(" opendir"), len);
+  if (res & SH_PR_LSTAT)    sl_strlcat(list, _(" lstat"), len);
+  if (res & SH_PR_PRIORITY) sl_strlcat(list, _(" getpriority"), len);
+  if (res & SH_PR_SCHED)    sl_strlcat(list, _(" sched_getparam"), len);
+  if (res & SH_PR_GETSID)   sl_strlcat(list, _(" getsid"), len);
+  if (res & SH_PR_GETPGID)  sl_strlcat(list, _(" getpgid"), len);
+  if (res & SH_PR_KILL)     sl_strlcat(list, _(" kill"), len);
+  if (res & SH_PR_PS2)      sl_strlcat(list, _(" ps(final)"), len);
+  return;
+}
+
+static short sh_processes_check (pid_t pid, short res)
+{
+  int  have_checks = 0;
+  int  need_checks = 0;
+#ifdef HAVE_PROCFS
+  char path[128];
+  struct stat buf;
+  DIR * dir;
+#endif
+
+#if !defined(sun) && !defined(__sun) && !defined(__sun__)
+#ifdef _POSIX_PRIORITY_SCHEDULING
+  struct sched_param p;
+#endif
+#endif
+
+  if (0 == kill(pid, 0))
+    { 
+      res |= SH_PR_KILL;    res |= SH_PR_ANY; ++have_checks;
+      ++need_checks;
+    }
+  else if (errno != EPERM)
+    {
+      ++need_checks;
+    }
+
+#ifdef HAVE_GETPGID
+  if ((pid_t)-1 != getpgid(pid))
+    { 
+      res |= SH_PR_GETPGID; res |= SH_PR_ANY; ++have_checks;
+    }
+  ++need_checks;
+#endif
+
+#ifdef HAVE_GETSID
+  if ((pid_t)-1 != getsid(pid))
+    { 
+      res |= SH_PR_GETSID;  res |= SH_PR_ANY; ++have_checks;
+    }
+  ++need_checks;
+#endif
+
+  /* sched_getparam() is broken on solaris 10, may segfault in librt
+   */
+#if !defined(sun) && !defined(__sun) && !defined(__sun__)
+#ifdef _POSIX_PRIORITY_SCHEDULING
+  if (0 == sched_getparam (pid, &p))
+    { 
+      res |= SH_PR_SCHED;   res |= SH_PR_ANY; ++have_checks;
+    }
+  ++need_checks;
+#endif
+#endif
+
+#ifdef HAVE_GETPRIORITY
+  errno = 0;
+  if (((-1) == getpriority (PRIO_PROCESS, (int) pid)) && (errno == ESRCH));
+  else
+    { 
+      res |= SH_PR_PRIORITY; res |= SH_PR_ANY; ++have_checks;
+    }
+  ++need_checks;
+#endif
+
+#ifdef HAVE_PROCFS
+  sl_snprintf (path, sizeof(path), "/proc/%ld", (unsigned long) pid);
+
+  if (0 == lstat (path, &buf))
+    { 
+      res |= SH_PR_LSTAT;   res |= SH_PR_ANY; ++have_checks;
+    }
+  ++need_checks;
+
+  if (NULL != (dir = opendir(path)))
+    {
+      res |= SH_PR_OPENDIR; res |= SH_PR_ANY; ++have_checks;
+      closedir(dir);
+    }
+  ++need_checks;
+
+#if !defined(SH_PROFILE)
+  if (0 == chdir(path))
+    {
+      res |= SH_PR_CHDIR;   res |= SH_PR_ANY; ++have_checks;
+      retry_aud_chdir(FIL__, __LINE__, "/");
+    }
+  ++need_checks;
+#endif
+#endif
+
+  if (have_checks == need_checks)
+    {
+      res |= SH_PR_ALL;
+    }
+  return res;
+}
+
+static int sh_processes_readps (FILE * in, short * res, 
+				char * str, size_t len, 
+				short flag, pid_t pid)
+{
+  int  cc; 
+  unsigned int  lnum   = 0;
+  unsigned long num    = 0;
+  char c;
+  unsigned int  pos = 0;
+  char tstr[256];
+  enum { SKIP_WS, SKIP_WS2, GET_NUM, SKIP_END, GET_NUM2 } line;
+
+  SL_ENTER(_("sh_processes_readps"));
+
+  if (!in) {
+    SL_RETURN((-1), _("sh_processes_readps"));
+  }
+
+  tstr[(sizeof(tstr)-1)] = '\0';
+  tstr[0]                = '\0';
+  line = SKIP_END;		/* Skip 1st line */
+
+  do
+    {
+      cc = fgetc(in);
+
+      if (EOF == cc) 
+	{
+	  if (feof(in))
+	    {
+	      break;
+	    }
+	  else if (errno == EAGAIN)
+	    {
+	      continue;
+	    }
+	  else
+	    {
+	      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, errno, MSG_E_SUBGEN,
+			      sh_error_message(errno),
+			      _("sh_processes_readps"));
+	      break;
+	    }
+	}
+
+      c = (char) cc;
+
+      if (pos < (sizeof(tstr)-1))
+	{ 
+	  tstr[pos] = c; ++pos; 
+	}
+
+      switch(line)
+	{
+	case SKIP_END:
+	  if (c == '\n')
+	    { 
+	      tstr[pos-1] = '\0';
+	      /* fprintf(stderr, "<%ld> %s\n", num, tstr); */
+	      line = SKIP_WS; pos = 0;
+	      if (str != NULL && num == (unsigned long) pid)
+		sl_strlcpy(str, tstr, len);
+	      if (lnum != 0)
+		is_in_watchlist (tstr, num);
+	      ++lnum;
+	    }
+	  break;
+	case SKIP_WS:
+	  if (isspace(cc))
+	    break;
+	  num  = 0;
+	  line = GET_NUM;
+	  /* fallthrough */
+	case GET_NUM:
+	  if (isdigit(cc))
+	    {
+	      num = num * 10 + (c - '0');
+	      break;
+	    }
+#ifdef HAVE_THREADS
+	  num  = 0;
+	  line = SKIP_WS2;
+#else
+	  if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
+	    {
+	      res[num - sh_prochk_minpid] |= flag;
+	    }
+	  line = SKIP_END;
+#endif
+	  break;
+	case SKIP_WS2:
+	  if (isspace(cc))
+	    break;
+	  num  = 0;
+	  line = GET_NUM2;
+	  /* fallthrough */
+	case GET_NUM2:
+	  if (isdigit(cc))
+	    {
+	      num = num * 10 + (c - '0');
+	      break;
+	    }
+	  if (num < sh_prochk_maxpid && num >= sh_prochk_minpid)
+	    {
+	      res[num - sh_prochk_minpid] |= flag;
+	    }
+	  line = SKIP_END;
+	  break;
+	default:
+	  SL_RETURN ((-1), _("sh_processes_readps"));
+	}
+    } while (1);
+
+  if (ferror(in))
+    {
+      SL_RETURN ((-1), _("sh_processes_readps"));
+    }
+
+  SL_RETURN ((0), _("sh_processes_readps"));
+}
+
+static int sh_processes_runps (short * res, char * str, size_t len, 
+			       short flag, pid_t pid)
+{
+  sh_tas_t task;
+
+  int    status = 0;
+  char * p;
+  struct  sigaction  new_act;
+  struct  sigaction  old_act;
+  int retval = 0;
+
+  SL_ENTER(_("sh_processes_runps"));
+
+  sh_ext_tas_init(&task);
+  p = sh_unix_getUIDdir (SH_ERR_ERR, task.run_user_uid);
+  if (p)
+    {
+      (void) sh_ext_tas_add_envv (&task, _("HOME"), p);
+    }
+  (void) sh_ext_tas_add_envv (&task, _("SHELL"), 
+			      _("/bin/sh")); 
+  (void) sh_ext_tas_add_envv (&task, _("PATH"),  
+			      _("/sbin:/usr/sbin:/bin:/usr/bin")); 
+  if (sh.timezone != NULL)
+    {
+      (void) sh_ext_tas_add_envv(&task,  "TZ", sh.timezone);
+    }
+
+  if (!sh_prochk_pspath)
+    sh_ext_tas_command(&task,  PSPATH);
+  else
+    sh_ext_tas_command(&task,  sh_prochk_pspath);
+
+  (void) sh_ext_tas_add_argv(&task,  _("ps"));
+
+  if (!sh_prochk_psarg)
+    {
+#ifdef HAVE_THREADS
+      (void) sh_ext_tas_add_argv(&task,  _("-eT"));
+#else
+      (void) sh_ext_tas_add_argv(&task,  PSARG);
+#endif
+    }
+  else
+    {
+      (void) sh_ext_tas_add_argv(&task,  sh_prochk_psarg);
+    }
+
+  task.rw = 'r';
+  task.fork_twice = S_FALSE;
+
+  status = sh_ext_popen(&task);
+  if (status != 0)
+    {
+      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN, 
+		      _("Could not open pipe"), _("sh_processes_runps"));
+      SL_RETURN ((-1), _("sh_processes_runps"));
+    }
+
+  /* ignore SIGPIPE (instead get EPIPE if connection is closed)
+   */
+  new_act.sa_handler = SIG_IGN;
+  (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
+
+  /* read from the open pipe
+   */
+  if (task.pipe != NULL)
+    {
+      retval = sh_processes_readps (task.pipe, res, str, len, flag, pid);
+    }
+
+  /* restore old signal handler
+   */
+  (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
+
+  /* close pipe and return exit status
+   */
+  (void) sh_ext_pclose(&task);
+  SL_RETURN ((retval), _("sh_processes_runps"));
+}
+
+static int sh_process_check_int (short * res)
+{
+  size_t i, j;
+  char  tests[512];
+
+  pid_t this_pid;
+
+  SL_ENTER(_("sh_process_check_int"));
+
+  this_pid = getpid();
+
+  if (!res)
+    {
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+		      _("Internal error: NULL argument"), 
+		      _("sh_process_check_int"));
+      SL_RETURN ((-1), _("sh_process_check_int"));
+    }
+
+  sh_processes_runps (res, NULL, 0, SH_PR_PS, 0);
+  for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
+    {
+      j      = i - sh_prochk_minpid; 
+      res[j] = sh_processes_check ((pid_t) i, res[j]);
+    }
+  sh_processes_runps (res, NULL, 0, SH_PR_PS2, 0);
+
+  /* Evaluate results
+   */
+  for (i = sh_prochk_minpid; i != sh_prochk_maxpid; ++i)
+    {
+      /* don't check the current process
+       */
+      if (i == (size_t) this_pid)
+	continue;
+
+      j      = i - sh_prochk_minpid;
+
+      if (((res[j] & SH_PR_PS) != 0) || ((res[j] & SH_PR_PS2) != 0))
+	{
+	  res[j] |= SH_PR_PS_ANY;
+	}
+      else
+	{
+	  res[j] &= ~SH_PR_PS_ANY;
+	}
+
+      tests[0] = '\0';
+
+      if ((res[j] & SH_PR_ANY) || (res[j] & SH_PR_PS_ANY))
+	{
+	  sh_processes_tlist (tests, sizeof(tests), res[j]);
+	  /* 
+	   * case 1: in ps and found 
+	   */
+	  if ((res[j] & SH_PR_PS_ANY) && (res[j] & SH_PR_ANY))
+	    {
+	      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_OK, 
+			      (unsigned long) i, tests);
+	    }
+
+	  /* 
+	   * case 2: not in ps and found
+	   */
+	  else if ((res[j] & SH_PR_PS_ANY) == 0) 
+	    {
+	      res[j] = sh_processes_check ((pid_t) i, 0);
+	      /*
+	       * if still there, it is real and hidden
+	       */
+	      if (res[j] & SH_PR_ANY)
+		{
+		  if (S_FALSE == is_in_list(&list_hidden, NULL, i))
+		    {
+		      sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0, 
+				      MSG_PCK_HIDDEN,
+				      (unsigned long) i, tests);
+		    }
+		}
+	    }
+
+	  /*
+	   * case 3: in ps, but not found
+	   */
+	  else
+	    {
+	      if (((res[j] & SH_PR_PS) != 0) && ((res[j] & SH_PR_PS2) != 0))
+		{
+		  if (S_FALSE == is_in_list(&list_fake, NULL, i))
+		    {
+		      sh_error_handle(sh_prochk_severity, FIL__, __LINE__, 0, 
+				      MSG_PCK_FAKE, 
+				      (unsigned long) i, tests);
+		    }
+		}
+	    }
+	}
+    } /* loop end */
+
+  check_watchlist (res);
+
+  SL_RETURN (0, _("sh_process_check_int"));
+}
+
+/* Initialise. 
+ */
+int sh_prochk_init(void) 
+{
+  SL_ENTER(_("sh_prochk_init"));
+
+  (void) proc_max_pid (&sh_prochk_maxpid);
+
+  if (sh_prochk_minpid > sh_prochk_maxpid)
+    ShProchkActive = S_FALSE;
+
+  /* We need to free anything allocated by the configuration functions if
+   * we find that the module is to be left inactive - otherwise _reconf()
+   * won't quite work. 
+   */
+  if( ShProchkActive == S_FALSE ) 
+    {
+      sh_prochk_free_list(process_check);
+      process_check = NULL;
+      SL_RETURN(-1, _("sh_prochk_init"));
+    }
+
+  sh_prochk_size = sh_prochk_maxpid - sh_prochk_minpid;
+
+  sh_prochk_res  = SH_ALLOC(sizeof(short) * sh_prochk_size);
+  memset (sh_prochk_res, 0, sizeof(short) * sh_prochk_size);
+  
+  SL_RETURN(0, _("sh_prochk_init"));
+}
+
+
+int sh_prochk_timer(time_t tcurrent) 
+{
+  static time_t lastcheck = 0;
+
+  SL_ENTER(_("sh_prochk_timer"));
+  if ((time_t) (tcurrent - lastcheck) >= sh_prochk_interval)
+    {
+      lastcheck  = tcurrent;
+      SL_RETURN((-1), _("sh_prochk_timer"));
+    }
+  SL_RETURN(0, _("sh_prochk_timer"));
+}
+
+int sh_prochk_check(void) 
+{
+  int status = 0;
+
+  SL_ENTER(_("sh_prochk_check"));
+
+  if( ShProchkActive != S_FALSE )
+    {
+      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_PCK_CHECK, 
+		      (unsigned long) sh_prochk_minpid, 
+		      (unsigned long) (sh_prochk_maxpid-1));
+      status = sh_process_check_int(sh_prochk_res);
+      if (status != 0)
+	ShProchkActive = S_FALSE;
+
+      /* clean out old entries which are not marked 
+       * as missing/hidden/fake anymore
+       */
+      clean_list (&list_missing);
+      clean_list (&list_hidden);
+      clean_list (&list_fake);
+    }
+
+  SL_RETURN(status, _("sh_prochk_check"));
+}
+
+/* Free our lists and the associated memory 
+ */
+int sh_prochk_cleanup(void) 
+{
+  SL_ENTER(_("sh_prochk_cleanup"));
+
+  sh_prochk_reconf();
+
+  if (list_missing) {
+    kill_list(list_missing);
+    list_missing = NULL;
+  }
+  if (list_hidden) {
+    kill_list(list_hidden);
+    list_hidden  = NULL;
+  }
+  if (list_fake) {
+    kill_list(list_fake);
+    list_fake    = NULL;
+  }
+  
+  SL_RETURN(0, _("sh_prochk_cleanup"));
+}
+
+/* Free our lists and the associated memory 
+ */
+int sh_prochk_reconf(void) 
+{
+  SL_ENTER(_("sh_prochk_reconf"));
+
+  userdef_maxpid     = 0;
+  sh_prochk_maxpid   = 0x8000;
+  sh_prochk_minpid   = 0x0000;
+  sh_prochk_interval = SH_PROCHK_INTERVAL;
+
+  sh_prochk_free_list(process_check);
+  process_check = NULL;
+  if (sh_prochk_res != NULL)
+    SH_FREE(sh_prochk_res);
+  sh_prochk_res = NULL;
+
+  if (sh_prochk_psarg)
+    SH_FREE(sh_prochk_psarg);
+  sh_prochk_psarg = NULL;
+  if (sh_prochk_pspath)
+    SH_FREE(sh_prochk_pspath);
+  sh_prochk_pspath = NULL;
+
+  SL_RETURN(0, _("sh_prochk_reconf"));
+}
+
+/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
+#endif
+
+/* #ifdef SH_USE_PROCESSCHECK */
+#endif
+
+
+#ifdef SH_CUTEST
+#include "CuTest.h"
+
+void Test_processcheck_watchlist_ok (CuTest *tc) {
+#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
+  CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
+  CuAssertTrue(tc, 
+	       S_TRUE  == is_in_watchlist("    1 ?        00:00:00 init", 0));
+  CuAssertTrue(tc, 
+	       S_FALSE == is_in_watchlist("    1 ?        00:00:00 flix", 0));
+  CuAssertTrue(tc, 
+	       S_TRUE  == is_in_watchlist("25218 ?        SNs    0:01 /usr/sbin/init -k start -DSSL", 0));
+  CuAssertTrue(tc, 
+	       S_FALSE  == is_in_watchlist("25218 ?        SNs    0:01 /usr/sbin/apache2 -k start -DSSL", 0));
+
+
+  sh_prochk_free_list(process_check);
+  process_check = NULL;
+  CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
+
+  CuAssertTrue(tc, 0 == sh_prochk_add_process("init"));
+  CuAssertTrue(tc, 0 == sh_prochk_add_process("ssh"));
+  CuAssertTrue(tc, 0 == sh_prochk_add_process("syslog"));
+  CuAssertTrue(tc, S_TRUE  == is_in_watchlist("init", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_watchlist("ssh", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_watchlist("syslog", 0));
+
+  sh_prochk_free_list(process_check);
+  process_check = NULL;
+  CuAssertTrue(tc, S_FALSE == is_in_watchlist("init", 0));
+  CuAssertTrue(tc, S_FALSE == is_in_watchlist("ssh", 0));
+  CuAssertTrue(tc, S_FALSE == is_in_watchlist("syslog", 0));
+#else
+  (void) tc; /* fix compiler warning */
+#endif
+  return;
+}
+
+void Test_processcheck_listhandle_ok (CuTest *tc) {
+#if defined(SH_USE_PROCESSCHECK) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
+  CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "foobar", 0));
+
+  if (list_missing)
+    kill_list(list_missing);
+  list_missing = NULL;
+
+  CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "foobar", 0));
+
+  if (list_missing)
+    kill_list(list_missing);
+  list_missing = NULL;
+
+  CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_FALSE == is_in_list(&list_missing, "foobar", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "foobar", 0));
+
+  CuAssertTrue(tc, 2  == clean_list(&list_missing));
+  CuAssertPtrNotNull(tc, list_missing);
+
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "init", 0));
+  CuAssertTrue(tc, S_TRUE  == is_in_list(&list_missing, "foobar", 0));
+
+  CuAssertTrue(tc, 2  == clean_list(&list_missing));
+  CuAssertPtrNotNull(tc, list_missing);
+
+  CuAssertTrue(tc, 0  == clean_list(&list_missing));
+  CuAssertTrue(tc, NULL == list_missing);
+#else
+  (void) tc; /* fix compiler warning */
+#endif
+  return;
+}
+
+
+/* #ifdef SH_CUTEST */
+#endif
+
