Index: trunk/src/samhain.c
===================================================================
--- trunk/src/samhain.c	(revision 366)
+++ trunk/src/samhain.c	(revision 367)
@@ -64,8 +64,8 @@
 #include "samhain.h"
 #include "sh_pthread.h"
-#include "sh_files.h"
 #include "sh_utils.h"
 #include "sh_error.h"
 #include "sh_unix.h"
+#include "sh_files.h"
 #include "sh_getopt.h"
 #include "sh_readconf.h"
@@ -393,4 +393,5 @@
   sh.flag.hidefile        = S_FALSE;
   sh.flag.loop            = S_FALSE;
+  sh.flag.inotify         = 0;
 
 #ifdef MKB_09
@@ -1710,5 +1711,4 @@
   (void) sh_files_test_setup();
 
-
   /* --------  NICE LEVEL   ---------
    */
@@ -1843,4 +1843,5 @@
 	      (void) sh_files_setrec();
 	      (void) sh_files_test_setup();
+
 	      if (0 != sh.flag.nice)
 		{
@@ -1965,4 +1966,5 @@
        */
       if      (sh.flag.checkSum == SH_CHECK_INIT ||
+	       (sh.flag.inotify & SH_INOTIFY_DOSCAN) != 0 ||
 	       (sh.flag.checkSum == SH_CHECK_CHECK &&
 		(sh.flag.isdaemon == S_FALSE && sh.flag.loop == S_FALSE)))
@@ -2060,4 +2062,5 @@
 	  flag_check_2 = 0;
 	  check_done   = 1;
+	  sh.flag.inotify &= ~SH_INOTIFY_DOSCAN;
 
 	  (void) sh_prelink_run (NULL, NULL, 0);
Index: trunk/src/sh_files.c
===================================================================
--- trunk/src/sh_files.c	(revision 366)
+++ trunk/src/sh_files.c	(revision 367)
@@ -69,8 +69,11 @@
 #include "sh_hash.h"
 #include "sh_ignore.h"
+#include "sh_inotify.h"
 #include "zAVLTree.h"
 
 #undef  FIL__
 #define FIL__  _("sh_files.c")
+
+extern sh_watches sh_file_watches;
 
 static char * sh_files_C_dequote (char * s, size_t * length)
@@ -356,4 +359,10 @@
 	      sh_error_handle ((-1),  FIL__, __LINE__, 0, 
 			       MSG_FI_CHK, pstr, tmp);
+	    }
+
+	  if ((sh.flag.inotify & SH_INOTIFY_DOSCAN) != 0)
+	    {
+	      sh_inotify_add_watch_later(ptr->name, &sh_file_watches, NULL,
+					 ptr->class, ptr->check_mask);
 	    }
 
@@ -1843,4 +1852,10 @@
     }
 
+  if ((sh.flag.inotify & SH_INOTIFY_DOSCAN) != 0)
+    {
+      sh_inotify_add_watch_later(iname, &sh_file_watches, &status,
+				 iclass, check_mask);
+    }
+   
   /* ---- stat the directory ----
    */
@@ -2500,4 +2515,45 @@
 
 /* -----------------------------------
+ * Routines required for inotify 
+ * -----------------------------------
+ */
+int sh_files_search_file(char * name, int * class, unsigned long *check_mask, int *reported)
+{
+  dirstack_t * item = zAVLSearch(zfileList, name);
+
+  if (item)
+    {
+      *check_mask = item->check_mask;
+      *class      = item->class;
+      *reported   = item->is_reported;
+      return 1;
+    }
+  return 0;
+}
+
+void sh_files_set_file_reported(char * name)
+{
+  dirstack_t * item = zAVLSearch(zfileList, name);
+
+  if (item)
+    {
+      if (sh.flag.reportonce == S_TRUE)
+	SET_SH_FFLAG_REPORTED(item->is_reported);
+    }
+  return;
+}
+
+void sh_files_clear_file_reported(char * name)
+{
+  dirstack_t * item = zAVLSearch(zfileList, name);
+
+  if (item)
+    {
+      CLEAR_SH_FFLAG_REPORTED(item->is_reported);
+    }
+  return;
+}
+
+/* -----------------------------------
  * 
  *  The following two routines serve to
Index: trunk/src/sh_getopt.c
===================================================================
--- trunk/src/sh_getopt.c	(revision 366)
+++ trunk/src/sh_getopt.c	(revision 367)
@@ -30,4 +30,5 @@
 #include "sh_error.h"
 #include "sh_getopt.h"
+#include "sh_unix.h"
 #include "sh_files.h"
 #include "sh_utils.h"
Index: trunk/src/sh_hash.c
===================================================================
--- trunk/src/sh_hash.c	(revision 366)
+++ trunk/src/sh_hash.c	(revision 367)
@@ -434,5 +434,5 @@
   char * str;
   char hashbuf[KEYBUF_SIZE];
-  int  retval;
+  volatile int  retval;
 
   /* --------  find the entry for the file ----------------       */
@@ -459,6 +459,9 @@
   str = all_items(theFile, fileHash, 0);
   tmp = sh_util_safe_name(fullpath);
+
+  SH_MUTEX_LOCK(mutex_thread_nolog);
   sh_error_handle (level, FIL__, __LINE__, 0, 
 		   MSG_FI_MISS2, tmp, str);
+  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 
   SH_FREE(tmp);
Index: trunk/src/sh_inotify.c
===================================================================
--- trunk/src/sh_inotify.c	(revision 366)
+++ trunk/src/sh_inotify.c	(revision 367)
@@ -51,4 +51,8 @@
 
 #if defined(HAVE_PTHREAD)
+
+SH_MUTEX_STATIC(mutex_list_dormant, PTHREAD_MUTEX_INITIALIZER);
+SH_MUTEX_STATIC(mutex_watches,      PTHREAD_MUTEX_INITIALIZER);
+
 static pthread_key_t  inotify_key;
 static pthread_once_t inotify_key_once = PTHREAD_ONCE_INIT;
@@ -122,4 +126,6 @@
   int    watch;
   int    flag;
+  int    class;
+  unsigned long check_mask;
   char * file;
 } sh_watch;
@@ -190,10 +196,29 @@
 #define SH_INOTIFY_MODIFY 1
 
-static void sh_inotify_init(sh_watches * watches)
-{
+void sh_inotify_init(sh_watches * watches)
+{
+  SH_MUTEX_LOCK_UNSAFE(mutex_watches);
   watches->list_of_watches = NULL;
   watches->count           = 0;
   watches->max_count       = 0;
+  SH_MUTEX_UNLOCK_UNSAFE(mutex_watches);
+
+  SH_MUTEX_LOCK_UNSAFE(mutex_list_dormant);
+  watches->dormant_watches = NULL;
+  SH_MUTEX_UNLOCK_UNSAFE(mutex_list_dormant);
+
   return;
+}
+
+ssize_t sh_inotify_read(char * buffer, size_t count)
+{
+  ssize_t len;
+  int     ifd = sh_inotify_getfd();
+
+  do {
+    len = read (ifd, &buffer, count);
+  } while (len < 0 || errno == EINTR);
+
+  return len;
 }
 
@@ -226,4 +251,14 @@
 };
 
+static void sh_inotify_listitem_destroy(struct sh_inotify_litem * this)
+{
+  if (this)
+    SH_FREE(this);
+  return;
+}
+
+/* No Mutex in the list cursor functions, must be in the caller
+ * function...
+ */
 typedef struct {
   struct sh_inotify_litem *prenode;
@@ -231,18 +266,9 @@
 } sh_inotify_listCursor;
 
-static sh_watch * sh_inotify_list_first(sh_inotify_listCursor * listcursor, sh_watches * watches)
+static sh_watch * sh_inotify_list_first(sh_inotify_listCursor * listcursor, 
+					sh_watches * watches)
 {
   listcursor->prenode = watches->dormant_watches;
   listcursor->curnode = watches->dormant_watches;
-
-  return listcursor->curnode->watch;
-}
-
-static sh_watch * sh_inotify_list_next(sh_inotify_listCursor * listcursor, sh_watches * watches)
-{
-  (void) watches;
-
-  listcursor->prenode = listcursor->curnode;
-  listcursor->curnode = listcursor->curnode->next;
 
   if (listcursor->curnode)
@@ -251,11 +277,22 @@
 }
 
-static void sh_inotify_listitem_destroy(struct sh_inotify_litem * this)
-{
-  SH_FREE(this);
-  return;
-}
-
-static sh_watch * sh_inotify_list_del_current(sh_inotify_listCursor * listcursor, sh_watches * watches)
+static sh_watch * sh_inotify_list_next(sh_inotify_listCursor * listcursor, 
+				       sh_watches * watches)
+{
+  (void) watches;
+
+  listcursor->prenode = listcursor->curnode;
+
+  if (listcursor->curnode)
+    {
+      listcursor->curnode = listcursor->curnode->next;
+      return listcursor->curnode->watch;
+    }
+
+  return NULL;
+}
+
+static sh_watch * sh_inotify_list_del_cur(sh_inotify_listCursor * listcursor, 
+					  sh_watches * watches)
 {
   sh_watch * ret = NULL;
@@ -277,5 +314,8 @@
 	  listcursor->curnode       = this->next;
 	}
-      ret = listcursor->curnode->watch;
+      if (listcursor->curnode)
+	ret = listcursor->curnode->watch;
+      else
+	ret = NULL;
       sh_inotify_listitem_destroy(this);
     }
@@ -285,5 +325,8 @@
 static int sh_inotify_add_dormant(sh_watches * watches, sh_watch * item)
 {
-  struct sh_inotify_litem * this = SH_ALLOC(sizeof(struct sh_inotify_litem));
+  struct sh_inotify_litem * this;
+
+  SH_MUTEX_LOCK(mutex_list_dormant);
+  this = SH_ALLOC(sizeof(struct sh_inotify_litem));
 
   this->watch = item;
@@ -291,5 +334,61 @@
   
   watches->dormant_watches = this;
+  SH_MUTEX_UNLOCK(mutex_list_dormant);
   return 0;
+}
+
+static void * sh_dummy_popret = NULL;
+
+char * sh_inotify_pop_dormant(sh_watches * watches, 
+			      int * class, unsigned long * check_mask)
+{
+  char * popret = NULL;
+  struct sh_inotify_litem * this;
+
+  /* Take the address to keep gcc from putting it into a register. 
+   * Avoids the 'clobbered by longjmp' warning. 
+   */
+  sh_dummy_popret = (void *) &popret;
+
+  SH_MUTEX_LOCK(mutex_list_dormant);
+
+  this = (struct sh_inotify_litem *) watches->dormant_watches;
+
+  if (this)
+    {
+      *class = this->watch->class;
+      *check_mask = this->watch->check_mask;
+      popret = sh_util_strdup(this->watch->file);
+
+      watches->dormant_watches = this->next;
+
+      sh_inotify_free_watch(this->watch);
+      SH_FREE(this);
+    }
+  SH_MUTEX_UNLOCK(mutex_list_dormant);
+
+  return popret;
+}
+
+void sh_inotify_purge_dormant(sh_watches * watches)
+{
+  struct sh_inotify_litem * this;
+
+  SH_MUTEX_LOCK(mutex_list_dormant);
+  this = (struct sh_inotify_litem *) watches->dormant_watches;
+
+  watches->dormant_watches = NULL;
+
+  while (this)
+    {
+      struct sh_inotify_litem * cur = this;
+      
+      this = this->next;
+
+      sh_inotify_free_watch(cur->watch);
+      SH_FREE(cur);
+    }
+  SH_MUTEX_UNLOCK(mutex_list_dormant);
+  return;
 }
 
@@ -308,16 +407,19 @@
 {
   int     ifd = sh_inotify_getfd();
-  zAVLTree   * all_watches = (zAVLTree *)(watches->list_of_watches);
+  zAVLTree   * all_watches;
+
+  SH_MUTEX_LOCK(mutex_watches);
+  all_watches = (zAVLTree *)(watches->list_of_watches);
 
   if (all_watches)
     zAVLFreeTree(all_watches, sh_inotify_free_watch);
 
-  sh_inotify_init(watches);
+  watches->list_of_watches = NULL;
+  watches->count = 0;
+  SH_MUTEX_UNLOCK(mutex_watches);
 
   if (ifd >= 0)
     close(ifd);
   sh_set_inotify_fd(-1);
-
-  watches->count = 0;
 
   return;
@@ -345,8 +447,117 @@
 }
 
+#define SH_INOTIFY_FILEFLAGS \
+  (IN_ATTRIB|IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)
+#define SH_INOTIFY_DIRFLAGS \
+  (SH_INOTIFY_FILEFLAGS|IN_DELETE|IN_CREATE|IN_MOVED_FROM|IN_MOVED_TO)
+
+#define SH_INOTIFY_FLAGS (SH_INOTIFY_FILEFLAGS|SH_INOTIFY_DIRFLAGS)
+
+/* Create an item and put it on the 'dormant' list for later watch creation 
+ */
+int sh_inotify_add_watch_later(char * filename, sh_watches * watches, 
+			       int * errnum,
+			       int class, unsigned long check_mask)
+{
+  sh_watch   * item;
+
+  item = sh_inotify_create_watch(filename, -1, /* flag */ 0);
+
+  item->class      = class;
+  item->check_mask = check_mask;
+
+  sh_inotify_add_dormant(watches, item);
+  if (errnum)
+    *errnum = 0;
+
+  return 0;
+}
+	  
+int sh_inotify_rm_watch (sh_watches * watches, sh_watches * save, int wd)
+{
+  int ifd = sh_get_inotify_fd();
+
+  if (watches)
+    {
+      sh_watch   * item;
+  
+      SH_MUTEX_LOCK(mutex_watches);
+      item = zAVLSearch(watches->list_of_watches, &wd);
+      
+      if (item)
+	{
+	  zAVLDelete(watches->list_of_watches, &wd);
+	  if (save) /* optionally save the item */
+	    {
+	      item->watch = -1;
+	      sh_inotify_add_dormant(save, item);
+	    }
+	  else
+	    {
+	      sh_inotify_free_watch(item);
+	    }
+	}
+      SH_MUTEX_UNLOCK(mutex_watches);
+    }
+  return inotify_rm_watch(ifd, wd);
+}
+
+#if (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)) 
+static void * sh_dummy_litem;
+
+int sh_inotify_recheck_watches (sh_watches * watches, sh_watches * save)
+{
+  sh_watch   * litem;
+  sh_inotify_listCursor listcursor;
+  int ifd = sh_get_inotify_fd();
+
+  extern void sh_fInotify_report_add(char * path, int class, unsigned long check_mask);
+
+  sh_dummy_litem = (void*) &litem;
+
+  /* -- Check dormant watches for reopening.
+   */
+  SH_MUTEX_LOCK(mutex_list_dormant);
+  
+  for (litem = sh_inotify_list_first(&listcursor, save); litem;
+       litem = sh_inotify_list_next(&listcursor, save))
+    {
+    have_next:
+
+      /* sh_inotify_list_del_cur may return NULL */
+      if (litem && litem->file && litem->watch == -1)
+	{
+	  litem->watch = inotify_add_watch (ifd, litem->file, 
+					    SH_INOTIFY_FLAGS);
+	  
+	  if (litem->watch >= 0)
+	    {
+	      SH_MUTEX_LOCK(mutex_watches);
+	      if (watches->list_of_watches)
+		zAVLInsert(watches->list_of_watches, litem);
+	      SH_MUTEX_UNLOCK(mutex_watches);
+
+	      sh_fInotify_report_add(litem->file, litem->class, litem->check_mask);
+
+	      litem = sh_inotify_list_del_cur(&listcursor, save);
+	      
+	      goto have_next;
+	    }
+	}
+    }
+  SH_MUTEX_UNLOCK(mutex_list_dormant);
+  return 0;
+}
+#endif
+
 /* This function is idempotent; it will add the watch only once 
  */
-int sh_inotify_add_watch(char * filename, sh_watches * watches, int  * errnum)
-{
+int sh_inotify_add_watch(char * filename, sh_watches * watches, int * errnum,
+			 int class, unsigned long check_mask)
+{
+  volatile int retval = 0;
+
+  SH_MUTEX_LOCK(mutex_watches);
+
   *errnum = 0;
 
@@ -375,29 +586,39 @@
 
 	  nwatch = inotify_add_watch (ifd, filename, 
-				      IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
+				      SH_INOTIFY_FLAGS);
 	  if (nwatch < 0)
 	    {
 	      *errnum = errno;
-	      return -1;
+	      retval = -1;
+	      goto retpoint;
 	    }
 
 	  item = sh_inotify_create_watch(filename, nwatch, /* flag */ 0);
+
+	  item->class      = class;
+	  item->check_mask = check_mask;
 	  
 	  if (NULL == watches->list_of_watches)
-	    watches->list_of_watches = zAVLAllocTree (sh_inotify_getkey, zAVL_KEY_INT);
+	    watches->list_of_watches = zAVLAllocTree (sh_inotify_getkey, 
+						      zAVL_KEY_INT);
  
 	  if (watches->list_of_watches)
 	    {
-	      *errnum =  zAVLInsert((zAVLTree *)(watches->list_of_watches), item);
+	      *errnum =  zAVLInsert((zAVLTree *)(watches->list_of_watches), 
+				    item);
+
 	      if (*errnum != 0)
 		{
+		  (*errnum == -1) ? *errnum = ENOMEM : EEXIST;
 		  sh_inotify_free_watch(item);
-		  return -1;
+		  retval = -1;
+		  goto retpoint;
 		}
 	    }
 	  else
 	    {
-	      *errnum = -1;
-	      return -1;
+	      *errnum = ENOMEM;
+	      retval = -1;
+	      goto retpoint;
 	    }
 
@@ -405,14 +626,49 @@
 	}
     }
-  return 0;
-}
+ retpoint:
+  SH_MUTEX_UNLOCK(mutex_watches);
+  return retval;
+}
+
+static void * sh_dummy_sret = NULL;
+
+char * sh_inotify_search_item(sh_watches * watches, int watch, 
+			      int * class, unsigned long * check_mask)
+{
+  sh_watch * item;
+  char     * sret = NULL;
+
+  /* Take the address to keep gcc from putting it into a register. 
+   * Avoids the 'clobbered by longjmp' warning. 
+   */
+  sh_dummy_sret = (void *) &sret;
+
+  SH_MUTEX_LOCK(mutex_watches);
+  item = zAVLSearch(watches->list_of_watches, &watch);
+
+  if (item)
+    {
+      *class      = item->class;
+      *check_mask = item->check_mask;
+      sret = sh_util_strdup(item->file);
+    }
+  SH_MUTEX_UNLOCK(mutex_watches);
+  return sret;
+}
+
+static void * sh_dummy_litem = NULL;
 
 int sh_inotify_wait_for_change(char * filename, sh_watches * watches, 
 			       int  * errnum, int waitsec)
 {
-  sh_watch   * item;
-  zAVLTree   * all_watches = (zAVLTree *)(watches->list_of_watches);
+  sh_watch   * litem;
+  sh_watch   * zitem;
   int          ifd = sh_inotify_getfd();
-  
+
+  /* Take the address to keep gcc from putting it into a register. 
+   * Avoids the 'clobbered by longjmp' warning. 
+   */
+  sh_dummy_litem = (void*) &litem;
+
   *errnum = 0;
 
@@ -421,6 +677,6 @@
   if (ifd >= 0)
     {
+      volatile ssize_t  i  = 0;
       ssize_t len = -1;
-      ssize_t  i  = 0;
       int  flag = 0;
       char buffer[1024];
@@ -432,5 +688,5 @@
       if (filename)
 	{
-	  if (sh_inotify_add_watch(filename, watches, errnum) < 0)
+	  if (sh_inotify_add_watch(filename, watches, errnum, 0, 0) < 0)
 	    {
 	      retry_msleep(waitsec, 0);
@@ -441,27 +697,33 @@
       /* -- Check dormant watches for reopening.
        */
-      for (item = sh_inotify_list_first(&listcursor, watches); item;
-	   item = sh_inotify_list_next(&listcursor, watches))
+      SH_MUTEX_LOCK(mutex_list_dormant);
+
+      for (litem = sh_inotify_list_first(&listcursor, watches); litem;
+	   litem = sh_inotify_list_next(&listcursor, watches))
 	{
 	have_next:
-	  if (item->file && item->watch == -1)
+	  /* sh_inotify_list_del_cur may return NULL */
+	  if (litem && litem->file && litem->watch == -1)
 	    {
-	      item->watch = inotify_add_watch (ifd, item->file, 
-					       IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
-	      if (item->watch >= 0)
+	      litem->watch = inotify_add_watch (ifd, litem->file, 
+						SH_INOTIFY_FLAGS);
+
+	      if (litem->watch >= 0)
 		{
-		  zAVLInsert(all_watches, item);
-		  item = sh_inotify_list_del_current(&listcursor, watches);
+		  SH_MUTEX_LOCK(mutex_watches);
+		  if (watches->list_of_watches)
+		    zAVLInsert(watches->list_of_watches, litem);
+		  SH_MUTEX_UNLOCK(mutex_watches);
+		  litem = sh_inotify_list_del_cur(&listcursor, watches);
 		  goto have_next;
 		}
 	    }
 	}
+      SH_MUTEX_UNLOCK(mutex_list_dormant);
 
 
       /* -- Blocking read on inotify file descriptor
        */
-      do {
-	len = read (ifd, &buffer, sizeof(buffer));
-      } while (len < 0 || errno == EINTR);
+      sh_inotify_read(buffer, sizeof(buffer));
 
       if (len > 0)
@@ -475,11 +737,12 @@
 	    event = (struct inotify_event *) &buffer[i];
 
-	    item = zAVLSearch(all_watches, &(event->wd));
-
-	    if (item)
+	    SH_MUTEX_LOCK(mutex_watches);
+	    zitem = zAVLSearch(watches->list_of_watches, &(event->wd));
+
+	    if (zitem)
 	      {
 		if (event->mask & IN_MODIFY)
 		  {
-		    item->flag |= SH_INOTIFY_MODIFY;
+		    zitem->flag |= SH_INOTIFY_MODIFY;
 		    flag |= SH_INOTIFY_MODIFY;
 		  }
@@ -488,12 +751,13 @@
 			 event->mask & IN_MOVE_SELF   )
 		  {
-		    item->flag |= SH_INOTIFY_REOPEN;
-		    (void) inotify_rm_watch(ifd, item->watch);
-		    zAVLDelete(all_watches, item);
-		    sh_inotify_add_dormant(watches, item);
-		    item->watch    = -1;
+		    zitem->flag |= SH_INOTIFY_REOPEN;
+		    (void) inotify_rm_watch(ifd, zitem->watch);
+		    zAVLDelete(watches->list_of_watches, zitem);
+		    sh_inotify_add_dormant(watches, zitem);
+		    zitem->watch    = -1;
 		    flag |= SH_INOTIFY_REOPEN;
 		  }
 	      }
+	    SH_MUTEX_UNLOCK(mutex_watches);
 	    
 	    i += sizeof (struct inotify_event) + event->len;
@@ -527,4 +791,5 @@
 }
 
+
 /* !defined(HAVE_SYS_INOTIFY_H) */
 #else
@@ -553,11 +818,155 @@
 }
 
-int sh_inotify_add_watch(char * filename, sh_watches * watches, int  * errnum)
+int sh_inotify_add_watch(char * filename, sh_watches * watches, int  * errnum,
+			 int class, unsigned long check_mask)
 {
   (void) filename;
   (void) watches;
+  (void) class;
+  (void) check_mask;
   *errnum = 0;
   return 0;
 }
 
+int sh_inotify_add_watch_later(char * filename, sh_watches * watches, 
+			       int  * errnum,
+			       int class, unsigned long check_mask)
+{
+  (void) filename;
+  (void) watches;
+  (void) class;
+  (void) check_mask;
+  *errnum = 0;
+  return 0;
+}
+
 #endif
+
+#ifdef SH_CUTEST
+#include "CuTest.h"
+void Test_inotify(CuTest *tc) {
+#if defined(HAVE_SYS_INOTIFY_H) && (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
+
+  int          ret;
+  sh_watches   twatch = SH_INOTIFY_INITIALIZER;
+  sh_watch   * litem;
+  sh_inotify_listCursor listcursor;
+  char * p;
+  int class;
+  unsigned long check_mask;
+  int           nrun = 0;
+
+  sh_watch aw1 = { -1, 0, 1, 1, "a1" };
+  sh_watch aw2 = { -1, 0, 2, 1, "a2" };
+  sh_watch aw3 = {  2, 0, 3, 1, "a3" };
+  sh_watch aw4 = { -1, 0, 4, 1, "a4" };
+  sh_watch aw5 = {  5, 0, 5, 1, "a5" };
+
+  do {
+
+    int          count = 0;
+
+    sh_watch * w1 = SH_ALLOC(sizeof(sh_watch));
+    sh_watch * w2 = SH_ALLOC(sizeof(sh_watch));
+    sh_watch * w3 = SH_ALLOC(sizeof(sh_watch));
+    sh_watch * w4 = SH_ALLOC(sizeof(sh_watch));
+    sh_watch * w5 = SH_ALLOC(sizeof(sh_watch));
+
+    memcpy(w1, &aw1, sizeof(sh_watch));
+    w1->file = sh_util_strdup(aw1.file);
+    memcpy(w2, &aw2, sizeof(sh_watch));
+    w2->file = sh_util_strdup(aw2.file);
+    memcpy(w3, &aw3, sizeof(sh_watch));
+    w3->file = sh_util_strdup(aw3.file);
+    memcpy(w4, &aw4, sizeof(sh_watch));
+    w4->file = sh_util_strdup(aw4.file);
+    memcpy(w5, &aw5, sizeof(sh_watch));
+    w5->file = sh_util_strdup(aw5.file);
+    
+    ret = sh_inotify_add_dormant(&twatch, w1);
+    CuAssertIntEquals(tc, ret, 0);
+    ret = sh_inotify_add_dormant(&twatch, w2);
+    CuAssertIntEquals(tc, ret, 0);
+    ret = sh_inotify_add_dormant(&twatch, w3);
+    CuAssertIntEquals(tc, ret, 0);
+    ret = sh_inotify_add_dormant(&twatch, w4);
+    CuAssertIntEquals(tc, ret, 0);
+    ret = sh_inotify_add_dormant(&twatch, w5);
+    CuAssertIntEquals(tc, ret, 0);
+    
+    /* -- Check dormant watches for reopening.
+     */
+    for (litem = sh_inotify_list_first(&listcursor, &twatch); litem;
+	 litem = sh_inotify_list_next(&listcursor, &twatch))
+      {
+      have_next:
+	
+	/* sh_inotify_list_del_cur may return NULL */
+	if (litem)
+	  {
+	    ++count;
+	    
+	    if (litem->file && litem->watch == -1)
+	      {
+		
+		switch (litem->class)
+		  {
+		  case 1:
+		    CuAssertStrEquals(tc, litem->file, "a1");
+		    break;
+		  case 2:
+		    CuAssertStrEquals(tc, litem->file, "a2");
+		    break;
+		  case 3:
+		    CuAssertStrEquals(tc, litem->file, "deadbeef");
+		    break;
+		  case 4:
+		    CuAssertStrEquals(tc, litem->file, "a4");
+		    break;
+		  case 5:
+		    CuAssertStrEquals(tc, litem->file, "deadbeef");
+		    break;
+		  default:
+		    CuAssertStrEquals(tc, litem->file, "deadbeef");
+		  }
+		litem = sh_inotify_list_del_cur(&listcursor, &twatch);
+		goto have_next;
+	      }
+	    switch (litem->class)
+	      {
+	      case 3:
+		CuAssertStrEquals(tc, litem->file, "a3");
+		break;
+	      case 5:
+		CuAssertStrEquals(tc, litem->file, "a5");
+		break;
+	      default:
+		CuAssertStrEquals(tc, litem->file, "foobar");
+	      }      
+	  }
+      }
+    
+    CuAssertIntEquals(tc, count, 5);
+    
+    p = sh_inotify_pop_dormant(&twatch, &class, &check_mask);
+    CuAssertStrEquals(tc, p, "a5");
+    
+    p = sh_inotify_pop_dormant(&twatch, &class, &check_mask);
+    CuAssertStrEquals(tc, p, "a3");
+    CuAssertIntEquals(tc, class, 3);
+    
+    p = sh_inotify_pop_dormant(&twatch, &class, &check_mask);
+    CuAssertTrue(tc, NULL == p);
+    CuAssertTrue(tc, NULL == twatch.dormant_watches);
+
+    ++nrun;
+
+  } while (nrun < 100);
+
+#else
+  (void) tc;
+#endif
+
+  return;
+}
+#endif
Index: trunk/src/sh_modules.c
===================================================================
--- trunk/src/sh_modules.c	(revision 366)
+++ trunk/src/sh_modules.c	(revision 367)
@@ -18,4 +18,5 @@
 #include "sh_logmon.h"
 #include "sh_registry.h"
+#include "sh_fInotify.h"
 
 sh_mtype modList[] = {
@@ -173,4 +174,21 @@
 #endif
 
+#if defined(HAVE_SYS_INOTIFY_H)
+  {
+    N_("INOTIFY"),
+    -1,
+    SH_MODFL_NOTIMER,
+    sh_fInotify_init,
+    sh_fInotify_timer,
+    sh_fInotify_run,
+    sh_fInotify_cleanup,
+    sh_fInotify_reconf,
+
+    N_("[Inotify]"),
+    sh_fInotify_table,
+    PTHREAD_MUTEX_INITIALIZER,
+  },
+#endif
+
   {
     NULL,
Index: trunk/src/sh_readconf.c
===================================================================
--- trunk/src/sh_readconf.c	(revision 366)
+++ trunk/src/sh_readconf.c	(revision 367)
@@ -31,4 +31,5 @@
 #include "sh_error.h"
 #include "sh_extern.h"
+#include "sh_unix.h"
 #include "sh_files.h"
 #include "sh_forward.h"
@@ -46,5 +47,4 @@
 #include "sh_tiger.h"
 #include "sh_tools.h"
-#include "sh_unix.h"
 #include "sh_utils.h"
 #include "sh_restrict.h"
Index: trunk/src/sh_unix.c
===================================================================
--- trunk/src/sh_unix.c	(revision 366)
+++ trunk/src/sh_unix.c	(revision 367)
@@ -3585,4 +3585,9 @@
 #endif    
 
+
+static void * sh_dummy_filename;
+static void * sh_dummy_tmp;
+static void * sh_dummy_tmp2;
+
 int sh_unix_getinfo (int level, char * filename, file_type * theFile, 
 		     char * fileHash, int policy)
@@ -3593,6 +3598,6 @@
   struct stat   lbuf;
   struct stat   fbuf;
-  int           stat_return;
-  int           stat_errno = 0;
+  volatile int  stat_return;
+  volatile int  stat_errno = 0;
 
   ShFileType    type;
@@ -3602,15 +3607,15 @@
 
   char        * linknamebuf;
-  int           linksize;
+  volatile int  linksize;
 
   extern int get_the_fd (SL_TICKET ticket);
 
-  SL_TICKET     rval_open;
-  int           err_open = 0;
-
-  int           fd;
-  int           fstat_return;
-  int           fstat_errno = 0;
-  int           try         = 0;
+  volatile SL_TICKET     rval_open;
+  volatile int           err_open = 0;
+
+  volatile int           fd;
+  volatile int           fstat_return;
+  volatile int           fstat_errno = 0;
+  volatile int           try         = 0;
 
   sh_string   * content = NULL;
@@ -3622,9 +3627,16 @@
   char * path = NULL;
 
-  int alert_timeout   = 120;
+  volatile int alert_timeout   = 120;
 
   path = theFile->fullpath;
 
   SL_ENTER(_("sh_unix_getinfo"));
+
+  /* Take the address to keep gcc from putting it into a register. 
+   * Avoids the 'clobbered by longjmp' warning. 
+   */
+  sh_dummy_filename = (void *) &filename;
+  sh_dummy_tmp      = (void *) &tmp;
+  sh_dummy_tmp2     = (void *) &tmp2;
 
   /* --- Stat the file, and get checksum. ---
@@ -3656,6 +3668,8 @@
 	  if (stale)
 	    {
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, err_open, MSG_E_SUBGEN,
 			      stale, _("sh_unix_getinfo_open"));
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 
@@ -3688,6 +3702,8 @@
     {
       tmp2 = sh_util_safe_name (theFile->fullpath);
+      SH_MUTEX_LOCK(mutex_thread_nolog);
       sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_TOOLATE,
 		       (long)(tend - tstart), tmp2);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
       SH_FREE(tmp2);
     }
@@ -3707,7 +3723,9 @@
 	  if (stale)
 	    {
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, fstat_errno, 
 			      MSG_E_SUBGEN,
 			      stale, _("sh_unix_getinfo_fstat"));
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 
@@ -3742,4 +3760,5 @@
 	    (void) sl_get_euid(&euid);
 	    tmp2 = sh_util_safe_name (theFile->fullpath);
+	    SH_MUTEX_LOCK(mutex_thread_nolog);
 	    sh_error_handle (level, FIL__, __LINE__, stat_return, MSG_FI_STAT,
 			     _("lstat"),
@@ -3747,4 +3766,5 @@
 			     (long) euid,
 			     tmp2);
+	    SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    SH_FREE(tmp2);
 	  }
@@ -3894,4 +3914,5 @@
 	      (void) sl_get_euid(&euid);
 
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle (level, FIL__, __LINE__, stat_return, MSG_FI_STAT,
 			       _("fstat"),
@@ -3899,9 +3920,12 @@
 			       (long) euid,
 			       tmp2);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 	  else if (fd >= 0 && !S_ISREG(fbuf.st_mode))
 	    {
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle (level, FIL__, __LINE__, fstat_errno, 
 			       MSG_E_NOTREG, tmp2);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 	  else
@@ -3911,6 +3935,8 @@
 	      sl_strlcpy(errbuf, sl_error_string(rval_open), sizeof(errbuf));
 	      sh_error_message(err_open, errbuf2, sizeof(errbuf2));
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle (level, FIL__, __LINE__, err_open, 
 			       MSG_E_READ, errbuf, errbuf2, tmp2);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 	  SH_FREE(tmp2);
@@ -4004,13 +4030,17 @@
     if (policy == SH_LEVEL_ALLIGNORE)
       {
+	SH_MUTEX_LOCK(mutex_thread_nolog);
 	sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, ENOENT, 
 			 MSG_FI_NOGRP,
 			 (long) buf.st_gid, tmp2);
+	SH_MUTEX_UNLOCK(mutex_thread_nolog);
       }
     else
       {
+	SH_MUTEX_LOCK(mutex_thread_nolog);
 	sh_error_handle (ShDFLevel[SH_ERR_T_NAME], FIL__, __LINE__, ENOENT, 
 			 MSG_FI_NOGRP,
 			 (long) buf.st_gid, tmp2);
+	SH_MUTEX_UNLOCK(mutex_thread_nolog);
       }
     SH_FREE(tmp2);
@@ -4025,13 +4055,17 @@
     if (policy == SH_LEVEL_ALLIGNORE)
       {
+	SH_MUTEX_LOCK(mutex_thread_nolog);
 	sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, ENOENT, 
 			 MSG_FI_NOUSR,
 			 (long) buf.st_uid, tmp2);
+	SH_MUTEX_UNLOCK(mutex_thread_nolog);
       }
     else
       {
+	SH_MUTEX_LOCK(mutex_thread_nolog);
 	sh_error_handle (ShDFLevel[SH_ERR_T_NAME], FIL__, __LINE__, ENOENT, 
 			 MSG_FI_NOUSR,
 			 (long) buf.st_uid, tmp2);
+	SH_MUTEX_UNLOCK(mutex_thread_nolog);
       }
     SH_FREE(tmp2);
@@ -4046,4 +4080,5 @@
 				theFile->fullpath : filename);
       (void) sh_unix_time(theFile->mtime, timestr, sizeof(timestr));
+      SH_MUTEX_LOCK(mutex_thread_nolog);
       sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_LIST,
 		       theFile->c_mode,
@@ -4054,4 +4089,5 @@
 		       timestr,
 		       tmp2);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
       SH_FREE(tmp2);
     }
@@ -4076,6 +4112,8 @@
 	  linksize = errno;
 	  tmp2 = sh_util_safe_name (theFile->fullpath);
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle (level, FIL__, __LINE__, linksize, MSG_FI_RDLNK,
 			   sh_error_message (linksize, errbuf, sizeof(errbuf)), tmp2);
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	  SH_FREE(tmp2);
 	  SH_FREE(linknamebuf);
@@ -4130,4 +4168,5 @@
 
 	      (void) sl_get_euid(&euid);
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle (level, FIL__, __LINE__, stat_return, 
 			       MSG_FI_STAT,
@@ -4136,4 +4175,5 @@
 			       (long) euid,
 			       tmp2);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 	  else 
@@ -4141,6 +4181,8 @@
 	      /* a dangling link -- everybody seems to have plenty of them 
 	       */
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
 	      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_DLNK,
 			       tmp, tmp2);
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	    }
 	  theFile->linkisok = BAD;
@@ -4175,6 +4217,8 @@
 	{
 	  tmp2 = sh_util_safe_name (linknamebuf);      
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
 	  sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_FI_LLNK,
 			   theFile->link_c_mode, tmp2);
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
 	  SH_FREE(tmp2);
 	}
@@ -4213,7 +4257,9 @@
 				_("compressed file too large (%lu bytes)"),
 				clen);
+		    SH_MUTEX_LOCK(mutex_thread_nolog);
 		    sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, -1, 
 				     MSG_E_SUBGPATH, tmsg, 
 				     _("sh_unix_getinfo"), tpath);
+		    SH_MUTEX_UNLOCK(mutex_thread_nolog);
 		    SH_FREE(tpath);
 		  }
