Index: trunk/src/sh_inotify.c
===================================================================
--- trunk/src/sh_inotify.c	(revision 261)
+++ trunk/src/sh_inotify.c	(revision 261)
@@ -0,0 +1,416 @@
+/* SAMHAIN file system integrity testing                                   */
+/* Copyright (C) 2009 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.              */
+
+#include "config_xor.h"
+
+#if defined(HAVE_SYS_INOTIFY_H)
+
+#undef  FIL__
+#define FIL__  _("sh_inotify.c")
+
+/* printf */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_calls.h"
+#include "sh_inotify.h"
+#include "sh_mem.h"
+#include "slib.h"
+
+/**************************************************
+ *
+ * Make the inotify fd thread-specific by 
+ * encapsulating it in get/set functions:
+ * sh_get_inotify_fd() / sh_set_inotify_fd()
+ *
+ **************************************************/
+
+#if defined(HAVE_PTHREAD)
+static pthread_key_t  inotify_key;
+static pthread_once_t inotify_key_once = PTHREAD_ONCE_INIT;
+
+static void make_inotify_key()
+{
+    (void) pthread_key_create(&inotify_key, free);
+}
+
+static int sh_get_inotify_fd()
+{
+  void * ptr;
+  int  * fp;
+
+  (void) pthread_once(&inotify_key_once, make_inotify_key);
+ 
+  if ((ptr = pthread_getspecific(inotify_key)) == NULL) 
+    {
+      ptr = malloc(sizeof(int));
+      if (ptr)
+	{
+	  fp  = (int*) ptr;
+	  *fp = -1;
+	  (void) pthread_setspecific(inotify_key, ptr);
+	}
+      else
+	{
+	  return -1;
+	}
+    }
+  else 
+    {
+      fp  = (int*) ptr;
+    }
+  return *fp;
+}
+
+static void sh_set_inotify_fd(int fd)
+{
+  int  * fp;
+
+  fp = (int*) pthread_getspecific(inotify_key);
+  if (fp)
+    *fp = fd;
+  return;
+}
+
+/* !defined(HAVE_PTHREAD) */
+#else
+
+static int sh_inotify_fd = -1;
+
+static inline int sh_get_inotify_fd()
+{
+  return sh_inotify_fd;
+}
+
+static inline void sh_set_inotify_fd(int fd)
+{
+  sh_inotify_fd = fd;
+}
+
+#endif
+
+/*--- nothing thread-related below this point --- */
+
+
+/**************************************************
+ *
+ * Get inotify fd, initialize inotify if necessary
+ *
+ **************************************************/
+#define SH_INOTIFY_FAILED -2
+
+static int sh_inotify_getfd()
+{
+  int ifd = sh_get_inotify_fd();
+
+  if (ifd >= 0)
+    {
+      return ifd;
+    }
+
+  else if (ifd == SH_INOTIFY_FAILED)
+    {
+      return -1;
+    }
+
+  else /* if (ifd == -1) */
+    {
+#if defined(HAVE_INOTIFY_INIT1)
+      ifd = inotify_init1(IN_CLOEXEC);
+#else
+      ifd = inotify_init();
+      if (ifd >= 0)
+	{
+	  long sflags;
+
+	  sflags = retry_fcntl(FIL__, __LINE__, fd, F_GETFD, 0);
+	  retry_fcntl(FIL__, __LINE__, fd, F_SETFD, sflags|FD_CLOEXEC);
+	}
+#endif
+
+      if (ifd < 0)
+	{
+	  sh_set_inotify_fd(SH_INOTIFY_FAILED);
+	  return -1;
+	}
+
+      sh_set_inotify_fd(ifd);
+      return ifd;
+    }
+}
+
+/**************************************************
+ *
+ * Public function:
+ *  int sh_inotify_wait_for_change(char * filename,
+ *                                 int watch,
+ *                                 int * errnum,
+ *                                 int   waitsec);
+ * Returns: watch, if nonnegative
+ *          -1 on error or reopen required
+ *             (check errnum != 0)
+ *
+ * Caller needs to keep track of watch descriptor
+ *
+ **************************************************/
+
+#define SH_INOTIFY_REOPEN 0
+#define SH_INOTIFY_MODIFY 1
+
+void sh_inotify_remove(sh_watches * watches)
+{
+  int     i;
+  int     ifd = sh_inotify_getfd();
+
+  for (i = 0; i < watches->count; ++i)
+    {
+      if (watches->file[i])
+	{
+	  SH_FREE (watches->file[i]);
+	  watches->file[i] = 0;
+	}
+      watches->watch[i] = 0;
+      watches->flag[i] = 0;
+    }
+  watches->count = 0;
+  if (ifd >= 0)
+    close(ifd);
+  sh_set_inotify_fd(-1);
+
+  return;
+}
+
+static int index_watched_file(char * filename, sh_watches * watches)
+{
+  int i;
+
+  for (i = 0; i < watches->count; ++i)
+    {
+      if (0 == strcmp(filename, watches->file[i]))
+	return i;
+    }
+  return -1;
+}
+
+/* This function is idempotent; it will add the watch only once 
+ */
+int sh_inotify_add_watch(char * filename, sh_watches * watches, int  * errnum)
+{
+  size_t len;
+  *errnum = 0;
+
+  if (filename)
+    {
+      int nwatch;
+      int index = index_watched_file(filename, watches);
+      
+      if (index < 0)
+	{
+	  int     ifd = sh_inotify_getfd();
+
+	  if (watches->count == SH_INOTIFY_MAX)
+	    {
+#ifdef EMFILE
+	      *errnum = EMFILE;
+#else
+	      *errnum = 24;
+#endif
+	      return -1;
+	    }
+
+	  nwatch = inotify_add_watch (ifd, filename, 
+				      IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
+	  if (nwatch < 0)
+	    {
+	      *errnum = errno;
+	      return -1;
+	    }
+	  
+	  watches->watch[watches->count] = nwatch;
+	  watches->flag[watches->count]  = 0;
+
+	  len = strlen(filename) + 1;
+	  watches->file[watches->count] = SH_ALLOC(len);
+	  sl_strlcpy(watches->file[watches->count], filename, len);
+
+	  ++(watches->count);
+	}
+    }
+  return 0;
+}
+
+int sh_inotify_wait_for_change(char * filename, sh_watches * watches, 
+			       int  * errnum, int waitsec)
+{
+  int     ifd = sh_inotify_getfd();
+  
+  *errnum = 0;
+
+ start_it:
+
+  if (ifd >= 0)
+    {
+      ssize_t len = -1;
+      ssize_t  i  = 0;
+      int  flag = 0;
+      char buffer[1024];
+
+      /* -- Add watch if required 
+       */
+      if (filename)
+	{
+	  if (sh_inotify_add_watch(filename, watches, errnum) < 0)
+	    {
+	      retry_msleep(waitsec, 0);
+	      return -1;
+	    }
+	}
+
+      for (i = 0; i < watches->count; ++i)
+	{
+	  if (watches->watch[i] == -1)
+	    watches->watch[i] = inotify_add_watch (ifd, watches->file[i], 
+					 IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT);
+	}
+
+
+      /* -- Blocking read on inotify file descriptor
+       */
+      do {
+	len = read (ifd, &buffer, sizeof(buffer));
+      } while (len < 0 || errno == EINTR);
+
+      fprintf (stderr, "FIXME buflen=%ld\n", (long int) len);
+
+      if (len > 0)
+	{
+	  int j;
+	  struct inotify_event *event;
+
+	  i = 0;
+	  
+	  while (i < len) {
+
+	    event = (struct inotify_event *) &buffer[i];
+
+	    fprintf (stderr, "FIXME wd=%d mask=%u cookie=%u len=%u\n",
+		    event->wd, event->mask,
+		    event->cookie, event->len);
+
+	    if (event->len > 0)
+	      fprintf (stderr, "FIXME name=%s\n", event->name);
+
+	    for (j = 0; j < watches->count; ++j)
+	      {
+		fprintf (stderr, "FIXME %d watch=%d file=%s\n", 
+			 j, watches->watch[j], watches->file[j]);
+
+		if (watches->watch[j] == event->wd)
+		  {
+		    
+		    fprintf (stderr, "FIXME wd=%d mask=%u\n",
+			     event->wd, event->mask);
+
+		    if (event->mask & IN_MODIFY)
+		      {
+			watches->flag[j] |= SH_INOTIFY_MODIFY;
+			flag |= SH_INOTIFY_MODIFY;
+		      }
+		    else if (event->mask & IN_DELETE_SELF || 
+			event->mask & IN_UNMOUNT     || 
+			event->mask & IN_MOVE_SELF   )
+		      {
+			watches->flag[j] |= SH_INOTIFY_REOPEN;
+			(void) inotify_rm_watch(ifd, watches->watch[j]);
+			watches->watch[j] = -1;
+			flag |= SH_INOTIFY_REOPEN;
+		      }
+		  }
+	      }
+	    i += sizeof (struct inotify_event) + event->len;
+	  }
+	}
+      else if (len == -1)
+	{
+	  *errnum = errno;
+	  retry_msleep(waitsec, 0);
+
+	  return -1;
+	}
+
+      if (flag & SH_INOTIFY_REOPEN)
+	{
+	  if (flag & SH_INOTIFY_MODIFY)
+	    return 0;
+	  else
+	    goto start_it;
+	}
+
+      return 0;
+    }
+
+  /* Inotify not working, sleep
+   */
+  retry_msleep(waitsec, 0);
+
+  *errnum = 0;
+  return -1;
+}
+
+/* !defined(HAVE_SYS_INOTIFY_H) */
+#else
+
+#include "sh_calls.h"
+
+void sh_inotify_remove(sh_watches * watches)
+{
+  (void) watches;
+  return;
+}
+
+int sh_inotify_wait_for_change(char * filename, sh_watches * watches,
+			       int *  errnum, int waitsec)
+{
+  (void) filename;
+  (void) watches;
+
+  /* Inotify not working, sleep for waitsec seconds
+   */
+  retry_msleep(waitsec, 0);
+
+  *errnum = 0;
+  return -1;
+}
+
+int sh_inotify_add_watch(char * filename, sh_watches * watches, int  * errnum)
+{
+  (void) filename;
+  (void) watches;
+  *errnum = 0;
+  return 0;
+}
+
+#endif
