Index: trunk/src/sh_inotify.c
===================================================================
--- trunk/src/sh_inotify.c	(revision 364)
+++ 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
