Jay Lee skrev:

Tony and anyone else on the list using a RHEL4 or FC3 server,

Have you had any luck getting fam or gamin working with Enhanced IDLE support? Care to share how you did it? (Custom compile, vanilla sources, etc). I've been running for some time with fam support disabled without issue since 99% of my clients were squirrelmail but as more and more users find Thunderbird I'm itching to get Enhanced IDLE support working again in order to decrease my load. Thanks for any help/tips you can provide.

Hi Jay,

Yes, RHAS4, in production - works perfectly. I made my own famd spec and split up fam into a basic famd rpm package with docs and a devel package (latter which I don't install on RHAS4). This is because RH up2date on the production server complains if both gamin and gamin-dev aren't installed - and the fam libs and gamin libs conflict (they are basically the same) with the gamin-dev libs. Furthermore, the gamin daemon is useless, so I only run the fam daemon.

I posted my spec with a 3rd party patch for fam on Courier IMAP list a week or couple of weeks ago, but here it is again - maybe I changed it since then ...

Best,

--Tonni

--
Tony Earnshaw
Email: [EMAIL PROTECTED]
%define         release %(release="`rpm -q --queryformat='.%{VERSION}\\n' 
redhat-release | head -n 1 2>/dev/null`" ; echo "$release")
Summary:   fam 2.7.0
Name:      fam
Version:   2.7.0
Release:   3%{release}
Packager: Tony Earnshaw <[EMAIL PROTECTED]>
Vendor: Translucent Systems BV
License: SGI OSS
Group: Network System Environment/Daemons
Provides: fam
Requires: gcc, autoconf, automake, rpm >= 4
Source: http://gd.tuwien.ac.at/opsys/linux/gentoo/distfiles/fam-2.7.0.tar.gz
Patch1: 
http://www.linuxfromscratch.org/blfs/downloads/6.1/fam-2.7.0-dnotify-1.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root

%description
fam is a server that tracks changes to the filesystem and relays these
changes to interested applications.  Applications such as fm(1G) and
mailbox(1) present an up-to-date view of the filesystem.  In the absence
of fam, these applications and others like them are forced to poll the
filesystem to detect changes.  fam is more efficient.

%define _man_dir %{_prefix}/man
%define _sh_man_dir %{_prefix}/share/man
%define _doc_dir %{_prefix}/share/doc/%{name}-base-%{version}

%package base
Summary: Base fam daemon, conf and documentation
Group: Network System Environment/Daemons
%description base
fam is a server that tracks changes to the filesystem and relays these
changes to interested applications.  Applications such as fm(1G) and
mailbox(1) present an up-to-date view of the filesystem.  In the absence
of fam, these applications and others like them are forced to poll the
filesystem to detect changes.  fam is more efficient.

%package devel
Summary: Development header, libraries and docs for fam
Group: Network System Environment/Daemons
Requires: fam-base
%description devel
Development header and docs for the SGI fam daemon

%prep
%setup -q
%patch1 -p1 -b .orig

cat <<EOF>famd.sysv
#!/bin/sh
#
# This is a /etc/rc.d/init.d file for the standalone famd daemon
#
# chkconfig: 2345 80 30
# description: auxiliary for Courier-IMAP
#
#
#
. /etc/init.d/functions

case "\$1" in
start)
        # Courier needs famd ...
        daemon /usr/sbin/famd -T 0 &
        echo"";echo -n "Starting famd daemon"
        echo \`ps aux | grep "\/usr\/sbin\/famd" |grep -v grep | awk '{ print 
\$2 }'\` > /var/run/famd.pid;echo"";echo""
        ;;
stop)
        # Stop famd
       echo"";daemon kill \`cat /var/run/famd.pid\` && echo "Stopping famd 
daemon"
        ;;
restart)
        \$0 stop
        \$0 start
        ;;
esac
exit \$RETVAL
EOF

#%{_cat} << EOF > famd.xinetd
cat << EOF > famd.xinetd
{
  type            = RPC UNLISTED
  socket_type     = stream
  user            = root
  group           = nobody
  server          = /usr/sbin/famd
  wait            = yes
  protocol        = tcp
  rpc_version     = 2
  rpc_number      = 391002
  disable         = yes
}
exit \$RETVAL
EOF

%build
umask 022

chmod a+w configure

CXXFLAGS="-Wno-deprecated" \
./configure \
        --prefix=/
make

sed -i 's|\/usr\/local\/etc|\/etc|g' 
$RPM_BUILD_DIR/%{name}-%{version}/man/famd.conf.5
sed -i 's|\/usr\/local\/etc|\/etc|g' 
$RPM_BUILD_DIR/%{name}-%{version}/man/famd.8

%install
rm -fr $RPM_BUILD_ROOT
%makeinstall
%{__mkdir_p} -m0755 %{buildroot}%{_initrddir}
%{__install} -m0755 famd.sysv %{buildroot}%{_initrddir}/famd
%{__mkdir_p} -m0755 %{buildroot}%{_sysconfdir}/xinetd.d
%{__install} -m0755 famd.xinetd %{buildroot}%{_sysconfdir}/xinetd.d/famd
%{__mkdir_p} 
$RPM_BUILD_ROOT{%_sysconfdir,%_sbindir,%_includedir,%_libdir,%_man_dir/man3,%_man_dir/man5,%_man_dir/man8}
%{__install} -m 644 $RPM_BUILD_DIR/%{name}-%{version}/conf/fam.conf 
$RPM_BUILD_ROOT%{_sysconfdir}
%{__install} -m 644 $RPM_BUILD_DIR/%{name}-%{version}/include/fam.h 
$RPM_BUILD_ROOT%{_includedir}
cp -p $RPM_BUILD_DIR/%{name}-%{version}/lib/.libs/* $RPM_BUILD_ROOT%{_libdir}
rm $RPM_BUILD_ROOT%{_libdir}/libfam.la
cp -p $RPM_BUILD_DIR/%{name}-%{version}/lib/libfam.la $RPM_BUILD_ROOT%{_libdir}
cd $RPM_BUILD_ROOT%{_libdir}
ln -s libfam.0.0.0 libfam.so.0
ln -s libfam.0.0.0 libfam.so
cd -
%{__install} -m 644 $RPM_BUILD_DIR/%{name}-%{version}/man/*.3 
$RPM_BUILD_ROOT%_man_dir/man3
%{__install} -m 644 $RPM_BUILD_DIR/%{name}-%{version}/man/*.5 
$RPM_BUILD_ROOT%_man_dir/man5
%{__install} -m 644 $RPM_BUILD_DIR/%{name}-%{version}/man/*.8 
$RPM_BUILD_ROOT%_man_dir/man8
%{__install} -m 755 $RPM_BUILD_DIR/%{name}-%{version}/src/famd 
$RPM_BUILD_ROOT%{_sbindir}

cat <<EOF>> $RPM_BUILD_DIR/%{name}-%{version}/INSTALL

---------------------------------

The default way of running famd is by using xinetd. To enable this, cd
/etc/xinetd.d and edit fam. Replace "disable = yes" with "disable = no"
and 'kill -HUP \`cat /var/run/xinetd.pid\`'

If you'd rather that famd runs as a standalone daemon, start it by doing
'/usr/sbin/famd -T 0 &' (that's -T null). To enable famd on system startup,
instead (you obviously can't have both) you'll have to have a script in
/etc/rc.d/init.d that does this. That script is included and installed
with this rpm, but not enabled by default. To enable it, enter:
"chckconfig famd on" as root.

EOF

%files base
%defattr(-,root,root)
%doc AUTHORS ChangeLog COPYING INSTALL NEWS README TODO
%{_sbindir}/famd
%config(noreplace) %{_sysconfdir}/fam.conf
%config(noreplace) %{_initrddir}
%config(noreplace) %{_sysconfdir}/xinetd.d
%_man_dir/man5/*
%_man_dir/man8/*
%_sh_man_dir/man5/*
%_sh_man_dir/man8/*

%files devel
%defattr(-,root,root)
%{_libdir}/*
%{_includedir}/fam.h
%_man_dir/man3/*
%_sh_man_dir/man3/*

%post devel
cd %{_libdir}
if [ ! -h ./libfam.so.0 ]; then
  ln -s libfam.0.0.0 libfam.so.0
if [ ! -h ./libfam.so ]; then
  ln -s libfam.0.0.0 libfam.so
fi
fi

ldconfig

%clean
rm -rf $RPM_BUILD_ROOT %{_tmppath}/rpm-tmp*

%changelog
* Sun Feb 26 2006 Tony Earnshaw <[EMAIL PROTECTED]>
- redesigned and reimplemented base and devel packages to allow gamin base and 
devel
- if gamin-devel is installed, fam-devel should *NOT* be installed
* Thu Dec 29 2005 Tony Earnshaw <[EMAIL PROTECTED]>
- fam-2.7.0-dnotify-1.patch, thanks Jonathan Chong <[EMAIL PROTECTED]>
  and of course Jim Gifford (jim at linuxfromscratch dot org), whose patch it is
* Thu Jun 23 2005 Tony Earnshaw <[EMAIL PROTECTED]> 2.7.0-1
- Initial package
Submitted By: Jim Gifford (jim at linuxfromscratch dot org)
Date: 2004-03-15
Initial Package Version: Jim Gifford
Origin: Debian Maintainer of FAM
Description: Dnotify Patch for FAM 2.7.0
 
$LastChangedBy: bdubbs $
$Date: 2005-08-01 13:29:19 -0600 (Mon, 01 Aug 2005) $

diff -Naur fam-2.7.0.orig/config.h.in fam-2.7.0/config.h.in
--- fam-2.7.0.orig/config.h.in  2003-01-20 00:40:15.000000000 +0000
+++ fam-2.7.0/config.h.in       2004-03-15 21:31:34.553059530 +0000
@@ -180,3 +180,6 @@
 
 /* Define to `int' if <sys/types.h> doesn't define. */
 #undef uid_t
+
+/* Define to 1 if you have F_NOTIFY fcntl */
+#undef USE_DNOTIFY
diff -Naur fam-2.7.0.orig/configure.ac fam-2.7.0/configure.ac
--- fam-2.7.0.orig/configure.ac 2003-11-26 19:47:59.000000000 +0000
+++ fam-2.7.0/configure.ac      2004-03-15 21:31:34.556058981 +0000
@@ -33,7 +33,26 @@
 AC_HEADER_DIRENT
 AC_CHECK_HEADERS([fcntl.h limits.h linux/imon.h netinet/in.h rpc/rpc.h 
rpcsvc/mount.h stddef.h stdlib.h string.h syslog.h sys/imon.h sys/param.h 
sys/select.h sys/statvfs.h sys/syssgi.h sys/time.h sys/types.h sys/un.h 
unistd.h])
 
-if test "$have_sys_imon_h"; then
+# Test for the linux dnotify fcntl
+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)
+
+if test "$have_dnotify"; then
+        MONITOR_FUNCS=IMonNone
+        AC_DEFINE([USE_DNOTIFY], [], [Use dnotify])
+       use_dnotify=true
+elif test "$have_sys_imon_h"; then
        MONITOR_FUNCS=IMonIRIX
 elif test "$have_linux_imon_h"; then
        MONITOR_FUNCS=IMonLinux
@@ -41,6 +60,7 @@
        MONITOR_FUNCS=IMonNone
 fi
 AC_SUBST(MONITOR_FUNCS)
+AM_CONDITIONAL(USE_DNOTIFY, $use_dnotify)
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_HEADER_STDBOOL
diff -Naur fam-2.7.0.orig/src/DNotify.c++ fam-2.7.0/src/DNotify.c++
--- fam-2.7.0.orig/src/DNotify.c++      1970-01-01 00:00:00.000000000 +0000
+++ fam-2.7.0/src/DNotify.c++   2004-03-15 21:31:34.542061543 +0000
@@ -0,0 +1,582 @@
+//  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 <memory>
+
+
+int DNotify::pipe_write_fd = -2;
+int DNotify::pipe_read_fd = -2;
+volatile sig_atomic_t DNotify::queue_overflowed = 0;
+volatile sig_atomic_t DNotify::queue_changed = 0;
+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; // The DirWatch.watches list
+    FileWatch *hash_link;
+};
+
+struct DNotify::DirWatch
+{
+    int fd;
+    dev_t dir_dev;
+    ino_t dir_ino;
+    
+    DirWatch *hash_link;
+    FileWatch *watches;
+};
+
+struct DNotify::ChangeEventData
+{
+    dev_t file_dev;
+    ino_t file_ino;
+};    
+
+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::overflow_signal_handler(int sig, siginfo_t *si, void *data)
+{
+  char c = 'x';
+
+  {
+    char *str = "*************** overflow sigqueue ***********************\n";
+    write (STDERR_FILENO, str, strlen(str));
+  }
+
+  if (!queue_overflowed)
+  {
+      queue_overflowed = 1;
+      // Trigger the read handler
+      write(pipe_write_fd, &c, 1);
+  }
+}
+
+void
+DNotify::signal_handler(int sig, siginfo_t *si, void *data)
+{
+  int left;
+  char c = 'x';
+
+  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_overflowed = 1;
+      {
+       char *str = "*************** overflow famqueue ****************\n";
+       write (STDERR_FILENO, str, strlen(str));
+      }
+  }
+  else
+  {
+      change_queue[queue_tail] = si->si_fd;
+      queue_tail = (queue_tail + 1) % QUEUESIZE;
+  }
+  
+  if (!queue_changed)
+  {
+      queue_changed = 1;
+      // 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);
+
+           // When the RT queue overflows we get a SIGIO
+           act.sa_sigaction = overflow_signal_handler;
+           sigemptyset(&act.sa_mask);
+           sigaction(SIGIO, &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 *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);
+      if (fcntl (dwatch->fd, F_NOTIFY,
+                 (DN_MODIFY|DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB) 
+                 | DN_MULTISHOT) == -1) {
+             return BAD;
+      }
+      hash_dirwatch (dwatch);
+    }
+
+  fw = lookup_filewatch (file_dev, file_ino);
+  if (fw && fw->dir_watch == dwatch)
+       return OK;
+  
+  // No old FileWatch, need to add one:
+  Log::debug("New FileWatch for %x %x\n", (int)file_dev, (int)file_ino);
+  fw = new FileWatch;
+  fw->next = dwatch->watches;
+  dwatch->watches = fw;
+  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 (::lstat(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);
+    if (fwatch == NULL)
+       return BAD;
+    
+    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::all_watches_changed(void)
+{
+  int i;
+  FileWatch *fw;
+
+  for (i=0; i<FILE_HASHSIZE; i++)
+  {
+      fw = file_hash[i];
+      while (fw)
+      {
+         (*ehandler)(fw->file_dev, fw->file_ino, CHANGE);
+
+         fw = fw->hash_link;
+      }
+  }
+}
+
+
+void
+DNotify::read_handler(int fd, void *)
+{
+    static char readbuf[5000];
+    DirWatch *dw;
+    FileWatch *fw;
+    int snap_queue_tail;
+    int last_fd;
+
+    int rc = read(fd, readbuf, sizeof readbuf);
+    queue_changed = 0;
+    if (rc < 0)
+        Log::perror("pipe read");
+    else if (queue_overflowed)
+    {
+         // There is a *slight* race condition here. Between reading
+         // the queue_overflow flag and resetting it. But it doesn't
+         // matter, since I'm gonna handle the overflow after reseting
+         // anyway.
+         queue_overflowed = false;
+
+         // We're soon gonna check all watches anyway, so
+         // get rid of the current queue
+         queue_head = queue_tail;
+         
+         all_watches_changed ();
+    }
+    else
+    {
+       // Don't read events that happen later than
+       // the initial read. (Otherwise skipping fd's
+       // might miss some changes).
+       snap_queue_tail = queue_tail;
+       last_fd = -1;
+       while (queue_head != snap_queue_tail)
+       {
+           fd = change_queue[queue_head];
+           queue_head = (queue_head + 1) % QUEUESIZE;
+
+           // Skip multiple changes to the same fd
+           if (fd != last_fd)
+           {
+               dw = lookup_dirwatch (fd);
+               if (dw)
+               {
+                   int n_watches, i;
+                   ChangeEventData *data;
+                   
+                   Log::debug("dnotify said dev %d/%d, ino %ld changed",
+                              major(dw->dir_dev), minor(dw->dir_dev), 
dw->dir_ino);
+                   
+                   n_watches = 0;
+                   for (fw=dw->watches; fw; fw=fw->next)
+                       n_watches++;
+                   
+                   data = new ChangeEventData[n_watches];
+
+                   i = 0;
+                   for (fw=dw->watches; fw; fw=fw->next)
+                   {
+                       data[i].file_dev = fw->file_dev;
+                       data[i].file_ino = fw->file_ino;
+                       i++;
+                   }
+
+                   for (i = 0; i < n_watches; i++)
+                   {
+                       (*ehandler)(data[i].file_dev, data[i].file_ino, CHANGE);
+                   }
+                   
+                   delete[] data;
+               }
+           }
+           last_fd = fd;
+       }
+    }
+}
+
diff -Naur fam-2.7.0.orig/src/DNotify.h fam-2.7.0/src/DNotify.h
--- fam-2.7.0.orig/src/DNotify.h        1970-01-01 00:00:00.000000000 +0000
+++ fam-2.7.0/src/DNotify.h     2004-03-15 21:31:34.546060811 +0000
@@ -0,0 +1,98 @@
+//  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;
+    struct ChangeEventData;
+  
+    //  Class Variables
+    enum { QUEUESIZE = 1024 };
+    static int pipe_write_fd;
+    static int pipe_read_fd;
+    static int change_queue[QUEUESIZE];
+    static volatile sig_atomic_t DNotify::queue_overflowed;
+    static volatile sig_atomic_t DNotify::queue_changed;
+    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 overflow_signal_handler(int sig, siginfo_t *si, void *data);
+    static void signal_handler(int sig, siginfo_t *si, void *data);
+    static void read_handler(int fd, void *closure);
+ 
+    enum { DIR_HASHSIZE = 367 };
+    static DirWatch *dir_hash[DIR_HASHSIZE];
+    enum { FILE_HASHSIZE = 823 };
+    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);
+
+    static void all_watches_changed(void);
+    
+    DNotify(const DNotify&);                   // Do not copy
+    DNotify & operator = (const DNotify&);     //  or assign.
+};
+
+#endif /* !IMon_included */
+
+
diff -Naur fam-2.7.0.orig/src/IMon.h fam-2.7.0/src/IMon.h
--- fam-2.7.0.orig/src/IMon.h   2003-01-18 14:18:12.000000000 +0000
+++ fam-2.7.0/src/IMon.h        2004-03-15 21:31:34.558058615 +0000
@@ -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 -Naur fam-2.7.0.orig/src/Interest.c++ fam-2.7.0/src/Interest.c++
--- fam-2.7.0.orig/src/Interest.c++     2003-01-18 14:18:12.000000000 +0000
+++ fam-2.7.0/src/Interest.c++  2004-03-15 21:31:34.550060079 +0000
@@ -42,12 +42,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)
@@ -60,10 +69,10 @@
       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)
+    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);
@@ -100,7 +109,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()
@@ -128,7 +137,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::Status s = IMon::BAD;
-       s = imon.express(name(), NULL);
+       s = monitor->express(name(), NULL);
         if (s != IMon::OK) {
             return true;
         }
@@ -248,23 +257,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 -Naur fam-2.7.0.orig/src/Interest.h fam-2.7.0/src/Interest.h
--- fam-2.7.0.orig/src/Interest.h       2003-01-18 14:18:12.000000000 +0000
+++ fam-2.7.0/src/Interest.h    2004-03-15 21:31:34.560058249 +0000
@@ -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);
 
@@ -121,7 +121,7 @@
 
     //  Class Variables
 
-    static IMon imon;
+    static Monitor *monitor;
     static Interest *hashtable[HASHSIZE];
     static bool xtab_verification;
 
diff -Naur fam-2.7.0.orig/src/Makefile.am fam-2.7.0/src/Makefile.am
--- fam-2.7.0.orig/src/Makefile.am      2003-01-19 12:00:17.000000000 +0000
+++ fam-2.7.0/src/Makefile.am   2004-03-15 21:31:34.562057882 +0000
@@ -71,7 +71,11 @@
   main.c++ \
   timeval.c++ \
   timeval.h \
-  @[EMAIL PROTECTED]
+  Monitor.h \
+  DNotify.h \
+  DNotify.c++ \
+  @[EMAIL PROTECTED] 
 
-EXTRA_famd_SOURCES = IMonIrix.c++ IMonLinux.c++ IMonNone.c++
+EXTRA_famd_SOURCES = IMonIrix.c++ IMonLinux.c++ IMonNone.c++ DNotify.c++ \
+  DNotify.h Monitor.h
 
diff -Naur fam-2.7.0.orig/src/Monitor.h fam-2.7.0/src/Monitor.h
--- fam-2.7.0.orig/src/Monitor.h        1970-01-01 00:00:00.000000000 +0000
+++ fam-2.7.0/src/Monitor.h     2004-03-15 21:31:34.564057516 +0000
@@ -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 */

Reply via email to