[Nautilus-list] FAM and dnotify



I spent a couple of hours this weekend patching up FAM to use the new
linux dnotify fcntl instead of imon, patch attached. Having a working FAM
would be a very good thing for the Nautilus file manager.

Is there still a maintainer for oss FAM? There hasn't been a release for a
very long time.

I'd appreciate if some people could take a look at it. It seems to
basically work, but due to the strange interfaces (FAM and dnotify uses
filenames, the imon interface uses dev:inode) I'm unable to convince
myself that there are no race conditions involving renaming files across
directories.

I'm also unsure about the security issues of having FAM installed. (I
worry especially about it's handling of groups.) Does anyone know if Irix
has had any security problems with FAM?

/ Alex


diff -ur /tmp/foo/fam-oss-2.6.4/acconfig.h fam-oss-2.6.4/acconfig.h
--- /tmp/foo/fam-oss-2.6.4/acconfig.h	Sat May 20 06:46:31 2000
+++ fam-oss-2.6.4/acconfig.h	Sun Jun 17 16:03:54 2001
@@ -17,6 +17,9 @@
 /*  Define if the system has imon and IMONIOC_ ioctl flags.  */
 #undef HAVE_IMON
 
+/*  Define if the system has the dnotify fcntl and it's gonna be used.  */
+#undef USE_DNOTIFY
+
 /*  Define if the system has the struct revokdi and the IMONIOC_REVOKDI
 **  ioctl flag.  (IRIX 5.3 doesn't.)
 */
diff -ur /tmp/foo/fam-oss-2.6.4/configure.in fam-oss-2.6.4/configure.in
--- /tmp/foo/fam-oss-2.6.4/configure.in	Sat May 20 07:23:17 2000
+++ fam-oss-2.6.4/configure.in	Sun Jun 17 17:15:21 2001
@@ -39,6 +39,9 @@
 dnl  Put configuration #defines in config.h.
 AM_CONFIG_HEADER(config.h)
 
+dnl Initialize libtool
+AM_PROG_LIBTOOL
+
 dnl
 dnl  We want to include our header in $(top_srcdir)/include
 dnl
@@ -79,14 +82,14 @@
 dnl I don't know why, but automake wants to use ./mkinstalldirs.
 dnl AC_PATH_PROG(MKINSTALLDIRS, mkinstalldirs)
 AC_PROG_MAKE_SET
+
+
 dnl AC_PROG_AWK
 dnl AC_PATH_PROG(LIBTOOL, libtool)
-AM_PROG_LIBTOOL
 dnl  autoconf/automake expects GNU tar, so find it on IRIX.
 AC_PATH_PROG(TAR, tar, tar, /usr/freeware/bin:$PATH)
 AC_PATH_PROG(LDCONFIG, ldconfig)
 
-
 dnl
 dnl  Checks for header files.
 dnl
@@ -96,6 +99,21 @@
 dnl AC_CHECK_HEADERS(fcntl.h limits.h sys/time.h syslog.h unistd.h)
 
 dnl
+dnl  Test for the linux dnotify fcntl
+dnl
+AC_MSG_CHECKING([for dnotify fcntl support])
+AC_TRY_COMPILE([
+#define _GNU_SOURCE  
+#include <fcntl.h>
+#include <unistd.h>
+],
+[ int fd = 1;
+  fcntl (fd, F_NOTIFY, (DN_MODIFY|DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB)|DN_MULTISHOT);
+], have_dnotify=yes, have_dnotify=no)
+use_dnotify=false
+AC_MSG_RESULT($have_dnotify)
+
+dnl
 dnl  See if imon is available; if so, is it IRIX or Linux?
 dnl
 if test `uname` = 'IRIX' || test `uname` = 'IRIX64'; then
@@ -118,11 +136,17 @@
 if test "$have_imon" != "yes"; then
     have_imon=no
     AC_DEFINE(HAVE_IMON, 0)
+    if test "$have_dnotify" = "yes"; then
+        AC_DEFINE(USE_DNOTIFY)
+	use_dnotify=true
+    fi
     IMON_FUNCS=IMonNone
 fi
+AM_CONDITIONAL(USE_DNOTIFY, $use_dnotify)
 AC_SUBST(IMON_FUNCS)
 echo "Using imon support module $IMON_FUNCS"
 
+
 AC_CHECK_HEADER(sys/statvfs.h, [AC_DEFINE(HAVE_STATVFS, 1) have_statvfs="yes"], [AC_DEFINE(HAVE_STATVFS, 0) have_statvfs="no"])
 AC_CHECK_HEADER(sys/syssgi.h,  AC_DEFINE(HAVE_SYSSGI,  1), AC_DEFINE(HAVE_SYSSGI,  0))
 AC_CHECK_HEADER(sys/fs/nfs_clnt.h, AC_DEFINE(HAVE_SYS_FS_NFS_CLNT_H, 1), AC_DEFINE(HAVE_SYS_FS_NFS_CLNT_H, 0))
@@ -560,7 +584,7 @@
 dnl 
 dnl  fam is a good deal less interesting without imon.
 dnl 
-if test "$have_imon" != 'yes'; then
+if test "$have_imon" != 'yes' -a "$have_dnotify" != 'yes'; then
     cat << EOF
 
   ******************************************************************
diff -ur /tmp/foo/fam-oss-2.6.4/fam/IMon.h fam-oss-2.6.4/fam/IMon.h
--- /tmp/foo/fam-oss-2.6.4/fam/IMon.h	Sat May 20 06:46:31 2000
+++ fam-oss-2.6.4/fam/IMon.h	Sat Jun 16 19:36:27 2001
@@ -24,10 +24,7 @@
 #define IMon_included
 
 #include "config.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include "Boolean.h"
+#include "Monitor.h"
 
 struct stat;
 
@@ -41,25 +38,18 @@
 //
 //  The user of the IMon object is the Interest class.
 
-class IMon {
+class IMon : public Monitor {
 
 public:
-
-    enum Status { OK = 0, BAD = -1 };
-    enum Event { EXEC, EXIT, CHANGE };
-
-    typedef void (*EventHandler)(dev_t, ino_t, int event);
-
     IMon(EventHandler h);
     ~IMon();
 
     static bool is_active();
 
-    Status express(const char *name, struct stat *stat_return);
-    Status revoke(const char *name, dev_t dev, ino_t ino);
+    virtual Status express(const char *name, struct stat *stat_return);
+    virtual Status revoke(const char *name, dev_t dev, ino_t ino);
 
 private:
-
     //  Class Variables
 
     static int imonfd;
diff -ur /tmp/foo/fam-oss-2.6.4/fam/Interest.c++ fam-oss-2.6.4/fam/Interest.c++
--- /tmp/foo/fam-oss-2.6.4/fam/Interest.c++	Sat May 20 06:46:31 2000
+++ fam-oss-2.6.4/fam/Interest.c++	Sun Jun 17 16:02:24 2001
@@ -41,12 +41,21 @@
 #include "Event.h"
 #include "FileSystem.h"
 #include "IMon.h"
+#include "DNotify.h"
 #include "Log.h"
 #include "Pollster.h"
 #include "timeval.h"
 
 Interest *Interest::hashtable[];
-IMon      Interest::imon(imon_handler);
+
+#ifdef USE_DNOTIFY 
+static DNotify dnotify(Interest::monitor_handler);
+Monitor * Interest::monitor = &dnotify;
+#else
+static IMon imon(Interest::monitor_handler);
+Monitor * Interest::monitor = &imon;
+#endif
+
 bool      Interest::xtab_verification = true;
 
 Interest::Interest(const char *name, FileSystem *fs, in_addr host, ExportVerification ev)
@@ -58,11 +67,11 @@
       myhost(host),
       mypath_exported_to_host(ev == NO_VERIFY_EXPORTED)
 {
-    memset(&old_stat, 0, sizeof(old_stat)); 
-    IMon::Status s = IMon::BAD;
-
-    s = imon.express(name, &old_stat);
-    if (s != IMon::OK)
+    memset(&old_stat, 0, sizeof(old_stat));
+    
+    Monitor::Status s = Monitor::BAD;
+    s = monitor->express(name, &old_stat);
+    if (s != Monitor::OK)
     {   int rc = lstat(name, &old_stat);
 	if (rc < 0)
 	{   Log::info("can't lstat %s", name);
@@ -95,7 +104,7 @@
     }
 #endif
 
-    if (exported_to_host()) fs->ll_monitor(this, s == IMon::OK);
+    if (exported_to_host()) fs->ll_monitor(this, s == Monitor::OK);
 }
 
 Interest::~Interest()
@@ -123,7 +132,7 @@
 		pp = &p->hashlink;	// move to next element
 	    }
 	if (!found_same)
-	    (void) imon.revoke(name(), dev, ino);
+	  (void) monitor->revoke(name(), dev, ino);
     }
 }
 
@@ -147,7 +156,7 @@
 
 	// Express interest.
 
-	imon.express(name(), NULL);
+	monitor->express(name(), NULL);
     }
     else
 	hashlink = NULL;
@@ -221,23 +230,23 @@
 }
 
 void
-Interest::imon_handler(dev_t device, ino_t inumber, int event)
+Interest::monitor_handler(dev_t device, ino_t inumber, int event)
 {
     assert(device || inumber);
 
     for (Interest *p = *hashchain(device, inumber), *next = p; p; p = next)
     {	next = p->hashlink;
 	if (p->ino == inumber && p->dev == device)
-	{   if (event == IMon::EXEC)
+	  {   if (event == Monitor::EXEC)
 	    {   p->cur_exec_state = EXECUTING;
 		(void) p->report_exec_state();
 	    }
-	    else if (event == IMon::EXIT)
+	    else if (event == Monitor::EXIT)
 	    {   p->cur_exec_state = NOT_EXECUTING;
 		(void) p->report_exec_state();
 	    }
 	    else
-	    {   assert(event == IMon::CHANGE);
+	    {   assert(event == Monitor::CHANGE);
 		p->scan();
 	    }
 	}
diff -ur /tmp/foo/fam-oss-2.6.4/fam/Interest.h fam-oss-2.6.4/fam/Interest.h
--- /tmp/foo/fam-oss-2.6.4/fam/Interest.h	Sat May 20 06:46:31 2000
+++ fam-oss-2.6.4/fam/Interest.h	Sat Jun 16 19:26:51 2001
@@ -32,7 +32,7 @@
 
 class Event;
 class FileSystem;
-class IMon;
+class Monitor;
 struct stat;
 
 //  Interest -- abstract base class for filesystem entities of interest.
@@ -74,7 +74,7 @@
 
     //  Public Class Method
 
-    static void imon_handler(dev_t, ino_t, int event);
+    static void monitor_handler(dev_t, ino_t, int event);
 
     static void enable_xtab_verification(bool enable);
 
@@ -120,7 +120,7 @@
 
     //  Class Variables
 
-    static IMon imon;
+    static Monitor *monitor;
     static Interest *hashtable[HASHSIZE];
     static bool xtab_verification;
 
diff -ur /tmp/foo/fam-oss-2.6.4/fam/Makefile.am fam-oss-2.6.4/fam/Makefile.am
--- /tmp/foo/fam-oss-2.6.4/fam/Makefile.am	Sat May 20 06:46:31 2000
+++ fam-oss-2.6.4/fam/Makefile.am	Sun Jun 17 16:32:04 2001
@@ -3,6 +3,12 @@
 bin_PROGRAMS = fam
 sysconf_DATA = fam.conf
 
+if USE_DNOTIFY
+DNOTIFY_FILES = DNotify.c++
+else
+DNOTIFY_FILES =
+endif
+
 fam_SOURCES = \
   Activity.c++ \
   Activity.h \
@@ -20,6 +26,7 @@
   Directory.h \
   DirectoryScanner.c++ \
   DirectoryScanner.h \
+  DNotify.h \
   Event.c++ \
   Event.h \
   File.c++ \
@@ -48,6 +55,7 @@
   NFSFileSystem.h \
   NetConnection.c++ \
   NetConnection.h \
+  Monitor.h \
   Pollster.c++ \
   Pollster.h \
   Request.h \
@@ -72,9 +80,10 @@
   main.c++ \
   timeval.c++ \
   timeval.h \
-  @IMON_FUNCS  c++
+  @IMON_FUNCS  c++ \
+  $(DNOTIFY_FILES)
 
-EXTRA_fam_SOURCES = IMonIrix.c++ IMonLinux.c++ IMonNone.c++
+EXTRA_fam_SOURCES = IMonIrix.c++ IMonLinux.c++ IMonNone.c++ DNotify.c++
 
 fam_LDADD = -lrpcsvc $(top_srcdir)/support/libsupport.a
 
diff -ur /tmp/foo/fam-oss-2.6.4/include/BTree.h fam-oss-2.6.4/include/BTree.h
--- /tmp/foo/fam-oss-2.6.4/include/BTree.h	Sat May 20 06:46:32 2000
+++ fam-oss-2.6.4/include/BTree.h	Sat Jun 16 02:04:46 2001
@@ -271,7 +271,7 @@
     n += that->n + 1;
     link[n] = that->link[that->n];
     that->n = 0;
-    that->link[0] = NULL;
+    that->link[0] = 0;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -280,7 +280,7 @@
 
 template <class K, class V>
 BTree<K, V>::BTree()
-    : root(NULL), npairs(0)
+    : root(0), npairs(0)
 {
     assert(!(fanout % 2));
 }
@@ -407,7 +407,7 @@
 BTree<Key, Value>::Closure
 BTree<Key, Value>::insert(Node *p, const Key& key, const Value& value)
 {
-    if (!p) return Closure(key, value, NULL);
+    if (!p) return Closure(key, value, 0);
     //  If you're running Purify on a client linking with libfam, and it says
     //  that line is causing a 3-byte UMR for BTree<int, bool>::insert() in
     //  FAMNextEvent() ("Reading 8 bytes from 0x... on the stack (3 bytes at
@@ -475,7 +475,7 @@
     case UNDER:
 	if (root->n == 0)
 	{   Node *nr = root->link[0];
-	    root->link[0] = NULL;	// don't delete subtree
+	    root->link[0] = 0;	// don't delete subtree
 	    delete root;
 	    root = nr;
 	}
@@ -507,8 +507,8 @@
     Node *cp = p->link[i];
     assert(cp);
     
-    Node *rp = i < p->n ? p->link[i + 1] : NULL;
-    Node *lp = i > 0    ? p->link[i - 1] : NULL;
+    Node *rp = i < p->n ? p->link[i + 1] : 0;
+    Node *lp = i > 0    ? p->link[i - 1] : 0;
     assert(!rp || rp->n >= fanout / 2);
     assert(!lp || lp->n >= fanout / 2);
 
diff -ur /tmp/foo/fam-oss-2.6.4/test/test.c++ fam-oss-2.6.4/test/test.c++
--- /tmp/foo/fam-oss-2.6.4/test/test.c++	Sat May 20 06:46:32 2000
+++ fam-oss-2.6.4/test/test.c++	Sat Jun 16 02:13:00 2001
@@ -5,6 +5,7 @@
 #include <signal.h>
 #include <errno.h>
 #include <stdarg.h>
+#include <stdlib.h>
 //#include <fcntl.h>
 #include "fam.h"
 //#include "Boolean.h"

--- /dev/null	Sat Mar 24 05:37:44 2001
+++ fam-oss-2.6.4/fam/Monitor.h	Sat Jun 16 19:19:06 2001
@@ -0,0 +1,57 @@
+//  Copyright (C) 2001 Red Hat, Inc.  All Rights Reserved.
+//  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
+//  
+//  This program is free software; you can redistribute it and/or modify it
+//  under the terms of version 2 of the GNU General Public License as
+//  published by the Free Software Foundation.
+//
+//  This program is distributed in the hope that it would be useful, but
+//  WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
+//  license provided herein, whether implied or otherwise, is limited to
+//  this program in accordance with the express provisions of the GNU
+//  General Public License.  Patent licenses, if any, provided herein do not
+//  apply to combinations of this program with other product or programs, or
+//  any other product whatsoever.  This program is distributed without any
+//  warranty that the program is delivered free of the rightful claim of any
+//  third person by way of infringement or the like.  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 the Free Software Foundation, Inc., 59
+//  Temple Place - Suite 330, Boston MA 02111-1307, USA.
+
+#ifndef Monitor_included
+#define Monitor_included
+
+#include "config.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+
+struct stat;
+
+//  Monitor is an abstract baseclass for differend file monitoring
+//  systems. The original system used was IMon, and the Montor API
+//  is heavily influenced by that.
+//  There can only be one instantiation of the Monitor object.
+//
+//  The user of this object uses express() and revoke() to
+//  express/revoke interest in a file to imon.  There is also
+//  a callback, the EventHandler.  When an event comes in,
+//  the EventHandler is called.
+//
+//  The main implementers of the Monitor class is IMon and DNotify
+
+class Monitor {
+public:
+
+    enum Status { OK = 0, BAD = -1 };
+    enum Event { EXEC, EXIT, CHANGE };
+
+    typedef void (*EventHandler)(dev_t, ino_t, int event);
+
+    virtual Status express(const char *name, struct stat *stat_return) = 0;
+    virtual Status revoke(const char *name, dev_t dev, ino_t ino) = 0;
+};
+
+#endif /* !Monitor_included */
--- /dev/null	Sat Mar 24 05:37:44 2001
+++ fam-oss-2.6.4/fam/DNotify.h	Sat Jun 16 20:21:11 2001
@@ -0,0 +1,92 @@
+//  Copyright (C) 2001 Red Hat, Inc.  All Rights Reserved.
+//  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
+//
+//  This program is free software; you can redistribute it and/or modify it
+//  under the terms of version 2 of the GNU General Public License as
+//  published by the Free Software Foundation.
+//
+//  This program is distributed in the hope that it would be useful, but
+//  WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
+//  license provided herein, whether implied or otherwise, is limited to
+//  this program in accordance with the express provisions of the GNU
+//  General Public License.  Patent licenses, if any, provided herein do not
+//  apply to combinations of this program with other product or programs, or
+//  any other product whatsoever.  This program is distributed without any
+//  warranty that the program is delivered free of the rightful claim of any
+//  third person by way of infringement or the like.  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 the Free Software Foundation, Inc., 59
+//  Temple Place - Suite 330, Boston MA 02111-1307, USA.
+
+#ifndef DNotify_included
+#define DNotify_included
+
+#include "config.h"
+#include "Monitor.h"
+#include <signal.h>
+
+//  DNotify is an object encapsulating the dnotify linux fcntl.
+//  It "emulates" the IMon interface.
+//  There can only be one instantiation of the DNotify object.
+//
+//  The user of this object uses express() and revoke() to
+//  express/revoke interest in a file.  There is also
+//  a callback, the EventHandler.  When an dnotify event comes in,
+//  the EventHandler is called.
+//
+//  The user of the DNotify object is the Interest class.
+
+class DNotify : public Monitor {
+public:
+    DNotify(EventHandler h);
+    ~DNotify();
+
+    static bool is_active();
+
+    virtual Status express(const char *name, struct stat *stat_return);
+    virtual Status revoke(const char *name, dev_t dev, ino_t ino);
+
+private:
+    struct FileWatch;
+    struct DirWatch;
+  
+    //  Class Variables
+    enum { QUEUESIZE = 1024 };
+    static int pipe_write_fd;
+    static int pipe_read_fd;
+    static int change_queue[QUEUESIZE];
+    static volatile int queue_head; // Only modified by read handler
+    static volatile int queue_tail; // Only modified by signal handler
+    static EventHandler ehandler;
+    static void signal_handler(int sig, siginfo_t *si, void *data);
+    static void read_handler(int fd, void *closure);
+
+    enum { DIR_HASHSIZE = 257 };
+    static DirWatch *dir_hash[DIR_HASHSIZE];
+    enum { FILE_HASHSIZE = 257 };
+    static FileWatch *file_hash[FILE_HASHSIZE];
+
+    static DirWatch **dir_hashchain(int fd)
+			  { return &dir_hash[(unsigned) (fd) % DIR_HASHSIZE]; }
+    static FileWatch **file_hashchain(dev_t d, ino_t i)
+			  { return &file_hash[(unsigned) (d+i) % FILE_HASHSIZE]; }
+
+    static DirWatch *lookup_dirwatch (int fd);
+    static DirWatch *lookup_dirwatch (dev_t dir_dev, ino_t dir_ino);
+    static FileWatch *lookup_filewatch (dev_t file_dev, ino_t file_ino);
+    static void hash_dirwatch(DirWatch *w);
+    static void hash_filewatch(FileWatch *w);
+    static void unhash_dirwatch(DirWatch *w);
+    static void unhash_filewatch(FileWatch *w);
+    static Status watch_dir(const char *notify_dir, dev_t file_dev, ino_t file_ino);
+
+    DNotify(const DNotify&);			// Do not copy
+    DNotify & operator = (const DNotify&);	//  or assign.
+};
+
+#endif /* !IMon_included */
+
+
--- /dev/null	Sat Mar 24 05:37:44 2001
+++ fam-oss-2.6.4/fam/DNotify.c++	Sat Jun 16 20:23:41 2001
@@ -0,0 +1,474 @@
+//  Copyright (C) 2001 Red Hat, Inc.  All Rights Reserved.
+//  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
+//  
+//  This program is free software; you can redistribute it and/or modify it
+//  under the terms of version 2 of the GNU General Public License as
+//  published by the Free Software Foundation.
+//
+//  This program is distributed in the hope that it would be useful, but
+//  WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
+//  license provided herein, whether implied or otherwise, is limited to
+//  this program in accordance with the express provisions of the GNU
+//  General Public License.  Patent licenses, if any, provided herein do not
+//  apply to combinations of this program with other product or programs, or
+//  any other product whatsoever.  This program is distributed without any
+//  warranty that the program is delivered free of the rightful claim of any
+//  third person by way of infringement or the like.  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 the Free Software Foundation, Inc., 59
+//  Temple Place - Suite 330, Boston MA 02111-1307, USA.
+
+#define _GNU_SOURCE  
+#include <fcntl.h>
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libgen.h>
+
+#include "DNotify.h"
+
+#include "Interest.h"
+#include "Log.h"
+#include "Scheduler.h"
+#include "alloc.h"
+
+
+int DNotify::pipe_write_fd = -2;
+int DNotify::pipe_read_fd = -2;
+int DNotify::change_queue[QUEUESIZE];
+volatile int DNotify::queue_head = 0; // Only modified by read handler
+volatile int DNotify::queue_tail = 0; // Only modified by signal handler
+DNotify::EventHandler DNotify::ehandler;
+
+DNotify::DirWatch *DNotify::dir_hash[DIR_HASHSIZE];
+DNotify::FileWatch *DNotify::file_hash[FILE_HASHSIZE];
+
+struct DNotify::FileWatch
+{
+  DirWatch *dir_watch;
+  dev_t file_dev;
+  ino_t file_ino;
+  FileWatch *next;
+  FileWatch *hash_link;
+};
+
+struct DNotify::DirWatch
+{
+  int fd;
+  dev_t dir_dev;
+  ino_t dir_ino;
+  
+  DirWatch *hash_link;
+  FileWatch *watches;
+};
+
+DNotify::DNotify(EventHandler h)
+{
+    assert(ehandler == NULL);
+    ehandler = h;
+}
+
+DNotify::~DNotify()
+{
+    if (pipe_read_fd >= 0)
+    {
+	//  Tell the scheduler.
+
+	(void) Scheduler::remove_read_handler(pipe_read_fd);
+
+	//  Close the pipe.
+
+	if (close(pipe_read_fd) < 0)
+	    Log::perror("can't pipe read end");
+	else
+	    Log::debug("closed pipe read end");
+	
+	if (close(pipe_write_fd) < 0)
+	    Log::perror("can't pipe write end");
+	else
+	    Log::debug("closed pipe write end");
+	pipe_read_fd = -1;
+    }
+    ehandler = NULL;
+}
+
+
+void
+DNotify::signal_handler(int sig, siginfo_t *si, void *data)
+{
+  int left;
+  char c;
+
+  if (queue_head <= queue_tail)
+    left = (QUEUESIZE + queue_head) - queue_tail;
+  else 
+    left = queue_head - queue_tail;
+  
+  // Must leave at least one item unused to see difference
+  // Betweeen empty and full
+  if (left <= 1)
+    {
+      // Queue overflow!
+      return;
+    }
+    
+  change_queue[queue_tail] = si->si_fd;
+  queue_tail = (queue_tail + 1) % QUEUESIZE;
+
+  // Trigger the read handler
+  write(pipe_write_fd, &c, 1);
+}
+
+bool
+DNotify::is_active()
+{
+    if (pipe_read_fd == -2)
+    {
+        int filedes[2];
+	int res;
+	
+	res = pipe (filedes);
+	if (res >= 0)
+	{   Log::debug("opened pipe");
+   	    pipe_read_fd = filedes[0];
+   	    pipe_write_fd = filedes[1];
+
+	    // Setup signal handler:
+	    struct sigaction act;
+	    
+	    act.sa_sigaction = signal_handler;
+	    sigemptyset(&act.sa_mask);
+	    act.sa_flags = SA_SIGINFO;
+	    sigaction (SIGRTMIN, &act, NULL);
+	    
+	    (void) Scheduler::install_read_handler(pipe_read_fd, read_handler, NULL);
+	}
+    }
+    return pipe_read_fd >= 0;
+}
+
+DNotify::DirWatch *
+DNotify::lookup_dirwatch (int fd)
+{
+  DirWatch **p;
+  DirWatch *w;
+
+  p = dir_hashchain (fd);
+
+  while (*p)
+    {
+      w = *p;
+
+      if (w->fd == fd)
+	return w;
+
+      p = &w->hash_link;
+    }
+  
+  return *p;
+}
+
+// This colud be made faster by using another hash table.
+// But it's not that bad, since it is only used by express/revoke
+DNotify::DirWatch *
+DNotify::lookup_dirwatch (dev_t dir_dev, ino_t dir_ino)
+{
+  DirWatch *p;
+  int i;
+
+  for (i=0;i<DIR_HASHSIZE;i++)
+    {
+      p = dir_hash[i];
+      
+      while (p)
+	{
+	  if (p->dir_dev == dir_dev && p->dir_ino == dir_ino)
+	    return p;
+	  
+	  p = p->hash_link;
+	}
+    }
+  
+  return NULL;
+}
+
+DNotify::FileWatch *
+DNotify::lookup_filewatch (dev_t dev, ino_t ino)
+{
+  FileWatch **p;
+  FileWatch *w;
+
+  p = file_hashchain (dev, ino);
+
+  while (*p)
+    {
+      w = *p;
+
+      if (w->file_dev == dev && w->file_ino == ino)
+	return w;
+
+      p = &w->hash_link;
+    }
+  
+  return *p;
+}
+
+// Make sure w is not already in the hash table before calling
+// this function.
+void
+DNotify::hash_dirwatch(DirWatch *w)
+{
+  DirWatch **p;
+  p = dir_hashchain (w->fd);
+  w->hash_link = *p;
+  *p = w;
+}
+
+// Make sure w is not already in the hash table before calling
+// this function.
+void
+DNotify::hash_filewatch(FileWatch *w)
+{
+  FileWatch **p;
+  p = file_hashchain (w->file_dev, w->file_ino);
+  w->hash_link = *p;
+  *p = w;
+}
+
+void
+DNotify::unhash_dirwatch(DirWatch *w)
+{
+  DirWatch **p;
+  
+  p = dir_hashchain (w->fd);
+  
+  while (*p)
+    {
+      if (*p == w)
+	{
+	  *p = w->hash_link;
+	  break;
+	}
+      p = &(*p)->hash_link;
+    }
+  w->hash_link = NULL;
+}
+
+void
+DNotify::unhash_filewatch(FileWatch *w)
+{
+  FileWatch **p;
+  
+  p = file_hashchain (w->file_dev, w->file_ino);
+  
+  while (*p)
+    {
+      if (*p == w)
+	{
+	  *p = w->hash_link;
+	  break;
+	}
+      p = &(*p)->hash_link;
+    }
+  w->hash_link = NULL;
+}
+
+DNotify::Status
+DNotify::watch_dir(const char *notify_dir, dev_t file_dev, ino_t file_ino)
+{
+  struct stat stat;
+  dev_t dir_dev;
+  ino_t dir_ino;
+  DirWatch *dwatch;
+  FileWatch **p;
+  FileWatch *fw;
+    
+  if (lstat (notify_dir, &stat) == -1)
+      return BAD;
+  
+  dwatch = lookup_dirwatch(stat.st_dev, stat.st_ino);
+  if (!dwatch)
+    {
+      Log::debug ("New DirWatch for %s (%x %x)\n",
+		  notify_dir, (int)stat.st_dev, (int)stat.st_ino);
+      dwatch = new DirWatch;
+      dwatch->watches = NULL;
+      dwatch->hash_link = NULL;
+      dwatch->dir_dev = stat.st_dev;
+      dwatch->dir_ino = stat.st_ino;
+      
+      dwatch->fd = open(notify_dir, O_RDONLY);
+      fcntl (dwatch->fd, F_SETSIG, SIGRTMIN);
+      fcntl (dwatch->fd, F_NOTIFY,
+	     (DN_MODIFY|DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB) | DN_MULTISHOT);
+      hash_dirwatch (dwatch);
+    }
+
+  for (p=&dwatch->watches; *p; p=&(*p)->next)
+    {
+      fw = *p;
+      if (fw->file_dev == file_dev && fw->file_ino == file_ino)
+	return OK;
+    }
+  
+  // No old FileWatch, need to add one:
+  Log::debug("New FileWatch for %x %x\n", (int)file_dev, (int)file_ino);
+  *p = new FileWatch;
+  fw = *p;
+  fw->next = NULL;
+  fw->file_dev = file_dev;
+  fw->file_ino = file_ino;
+  fw->dir_watch = dwatch;
+  hash_filewatch(fw);
+  return OK;
+}
+
+char *
+dirname_dup (const char *name)
+{
+  char *copy = strdup(name);
+  char *res = dirname(copy);
+  res = strdup(res);
+  free (copy);
+  return res;
+}
+
+DNotify::Status
+DNotify::express(const char *name, struct stat *status)
+{
+    struct stat stat;
+    char *notify_dir;
+    int res;
+    Status s;
+    dev_t dev;
+    ino_t ino;
+
+    Log::debug("express() name: %s\n", name);
+
+    if (!is_active())
+	return BAD;
+
+    if (::lstat (name, &stat) == -1)
+      return BAD;
+
+    dev = stat.st_dev;
+    ino = stat.st_ino;
+    
+    if ((stat.st_mode & S_IFMT) != S_IFDIR)
+	notify_dir = dirname_dup (name);
+    else
+	notify_dir = (char *)name;
+
+    s = watch_dir (notify_dir, dev, ino);
+    if (notify_dir != name)
+        free (notify_dir);
+    if (s)
+      return s;
+
+    // Check for a race condition; if someone removed or changed the
+    // file at the same time that we are expressing interest in it,
+    // revoke the interest so we don't get notifications about changes
+    // to a recycled inode that we don't otherwise care about.
+    //
+    struct stat st;
+    if (status == NULL) {
+	status = &st;
+    }
+    if (::stat(name, status) == -1) {
+	Log::perror("stat on \"%s\" failed", name);
+	revoke(name, stat.st_dev, stat.st_ino);
+	return BAD;
+    }
+    if (status->st_dev != stat.st_dev
+	|| status->st_ino != stat.st_ino) {
+	Log::error("File \"%s\" changed between express and stat",
+		   name);
+	revoke(name, stat.st_dev, stat.st_ino);
+	return BAD;
+    }	
+
+    Log::debug("told dnotify to monitor \"%s\" = dev %d/%d, ino %d", name,
+	       major(status->st_dev), minor(status->st_dev),
+	       status->st_ino);
+    return OK;
+}
+
+DNotify::Status
+DNotify::revoke(const char *name, dev_t dev, ino_t ino)
+{
+    FileWatch *fwatch;
+    DirWatch *dwatch;
+    
+    Log::debug("revoke() name: %s, dev: %x, ino: %x\n", name, dev, ino);
+
+    if (!is_active())
+	return BAD;
+
+    // Lookup FileWatch by dev:ino, and its DirWatch.
+    fwatch = lookup_filewatch (dev, ino);
+    dwatch = fwatch->dir_watch;
+    
+    // delete FileWatch, if last FileWatch: close fd, delete DirWatch
+    Log::debug ("Destroying FileWatch for (%x %x)\n",
+		(int)fwatch->file_dev, (int)fwatch->file_ino);
+    FileWatch **p;
+    for (p=&dwatch->watches; *p; p=&(*p)->next)
+    {
+      if (*p == fwatch)
+	{
+	  *p = (*p)->next;
+	  break;
+	}
+    }
+    unhash_filewatch(fwatch);
+    delete fwatch;
+    if (dwatch->watches == NULL)
+      {
+	Log::debug ("Destroying DirWatch for (%x %x)\n",
+		    (int)dwatch->dir_dev, (int)dwatch->dir_ino);
+	close(dwatch->fd);
+	unhash_dirwatch(dwatch);
+	delete dwatch;
+      }
+  
+    return OK;
+}
+
+
+void
+DNotify::read_handler(int fd, void *)
+{
+    char readbuf[128];
+    DirWatch *dw;
+    FileWatch *fw;
+
+    int rc = read(fd, readbuf, sizeof readbuf);
+    if (rc < 0)
+        Log::perror("pipe read");
+    else
+      {
+	while (queue_head != queue_tail)
+	  {
+	    fd = change_queue[queue_head];
+	    queue_head = (queue_head + 1) % QUEUESIZE;
+	    
+	    dw = lookup_dirwatch (fd);
+	    if (dw)
+	      {
+		Log::debug("dnotify said dev %d/%d, ino %ld changed",
+			   major(dw->dir_dev), minor(dw->dir_dev), dw->dir_ino);
+		for (fw=dw->watches; fw; fw=fw->next)
+		  {
+		    (*ehandler)(fw->file_dev, fw->file_ino, CHANGE);
+		  }
+	      }
+	  }
+      }
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]