[csw-devel] SF.net SVN: gar:[10217] csw/mgar/pkg

theferret at users.sourceforge.net theferret at users.sourceforge.net
Tue Jun 15 05:21:18 CEST 2010


Revision: 10217
          http://gar.svn.sourceforge.net/gar/?rev=10217&view=rev
Author:   theferret
Date:     2010-06-15 03:21:18 +0000 (Tue, 15 Jun 2010)

Log Message:
-----------
gamin: replacement for fam package. its what opensolaris uses

Added Paths:
-----------
    csw/mgar/pkg/gamin/
    csw/mgar/pkg/gamin/Makefile
    csw/mgar/pkg/gamin/trunk/
    csw/mgar/pkg/gamin/trunk/Makefile
    csw/mgar/pkg/gamin/trunk/README
    csw/mgar/pkg/gamin/trunk/copyright
    csw/mgar/pkg/gamin/trunk/depend
    csw/mgar/pkg/gamin/trunk/patchfile.preconf
    csw/mgar/pkg/gamin/trunk/pkginfo
    csw/mgar/pkg/gamin/trunk/prototype.i386
    csw/mgar/pkg/gamin/trunk/prototype.sparc

Added: csw/mgar/pkg/gamin/Makefile
===================================================================
--- csw/mgar/pkg/gamin/Makefile	                        (rev 0)
+++ csw/mgar/pkg/gamin/Makefile	2010-06-15 03:21:18 UTC (rev 10217)
@@ -0,0 +1,15 @@
+
+# This is somewhat of a "stub" Makefile, for 
+# directories managed by the "createpkg" TEMPLATES.
+#
+# Not all of targets listed here may actualy be supported by
+# the subdir in question. But they are a goal
+
+DEFAULT_DIR=trunk
+
+
+# The -C means you must use gmake, unfortunately.
+
+all package build garchive extract configure clean distclean reallyclean :
+	@echo Going to make $@ in $(DEFAULT_DIR)
+	$(MAKE) -C  $(DEFAULT_DIR) $@


Property changes on: csw/mgar/pkg/gamin/trunk
___________________________________________________________________
Added: svn:ignore
   + cookies
download
work
cswstage
build


Added: csw/mgar/pkg/gamin/trunk/Makefile
===================================================================
--- csw/mgar/pkg/gamin/trunk/Makefile	                        (rev 0)
+++ csw/mgar/pkg/gamin/trunk/Makefile	2010-06-15 03:21:18 UTC (rev 10217)
@@ -0,0 +1,228 @@
+# This file is from TEMPLATES/createpkg/Makefile.lib
+# You MUST build this with gnu make, unfortunately.
+# Mainly just because of the ARCH expansion, but also for "?="
+
+
+# If you do NOT want to build 64bit libs, remove the $(ARCH)-64 from
+# the "all:" target.
+
+# If you DO want to, then leave it in, and also customize your
+#  the build/.stage-$(ARCH)-64.done    target(s),
+# PLUS, customize your prototype file(s) as needed
+# Dont forget to handle any 64bit pkg-config .pc files properly!!
+
+# Organization: variable defintions up top,
+# targets in second half.
+# Note that assignments with "?=" mean "assign if not already assigned.
+
+# Where do we keep local downloads of software src files
+ARCHIVEDIR ?= /home/src
+ARCHIVENAME ?= gamin-0.1.9.tar.gz
+EXTRACTPROG ?= /usr/sfw/bin/gtar zfx
+
+# Only use ONE here. The name is used for compat with 'gar'.
+MASTER_SITES ?= http://people.gnome.org/~veillard/gamin/sources
+
+ARCH := $(shell /bin/uname -p)
+
+LDFLAGS_COMMON :=
+
+SRCDIR= build/*
+
+# DO NOT specify the arch-64 stuff such as -m64 here.
+# the individual targets will do that.
+
+CFLAGS_COMMON   ?= -mt -xnorunpath -xO2
+CXXFLAGS_COMMON ?= -mt -norunpath  -xO2
+CFLAGS_sparc    ?= -xarch=v8
+CFLAGS_i386     ?= -xarch=386
+
+# Keep this arch-neutral if possible. add arch-specific tweaks
+# to arch-specific target
+CONFIGURE=./configure --prefix=/opt/csw --enable-static=no
+
+LIBDIR64_sparc:=/opt/csw/lib/sparcv9
+LIBDIR64_i386 :=/opt/csw/lib/amd64
+
+CONFIGURE_64_$(ARCH) := --libdir=$(LIBDIR64_$(ARCH))
+CONFIGURE_64_EXTRAS  := $(CONFIGURE_64_$(ARCH))
+
+LDFLAGS_64_$(ARCH) := -R$(LIBDIR64_$(ARCH)) -L$(LIBDIR64_$(ARCH))
+
+
+
+CREATEPKG=createpkg -r `pwd`/build/*/cswstage
+
+
+all:	$(ARCH)-32 build/.stage1 $(ARCH)-64 package
+#all:	$(ARCH)-32 package
+
+# ARCH-XX handles configure, build, and stage for its own combo
+# 
+
+EXTRACTPROG ?= /bin/echo ERROR: need to set EXTRACTPROG ; exit 1 ;
+
+
+
+extract:	build/.extract.done
+
+build/.extract.done:
+	test -d build || mkdir build
+	@if test -f  $(ARCHIVEDIR)/$(ARCHIVENAME) ; then \
+		echo Extracting $(ARCHIVENAME) under build dir... ;\
+		( cd build && $(EXTRACTPROG) $(ARCHIVEDIR)/$(ARCHIVENAME) ) ; \
+	else echo Cannot extract - $(ARCHIVEDIR)/$(ARCHIVENAME) does not exist ;\
+		exit 1; \
+	fi
+	touch build/.extract.done
+	
+
+clean distclean:
+	@echo Calling $@ in $(SRCDIR)
+	$(MAKE) -C $(SRCDIR) $@
+
+# This is a bit of a hack.. it does a "make clean", 
+# only when we're between 32bit and 64bit builds, when doing "make all".
+# Once we're IN the 64bit builds, it should not be automatically called.
+build/.stage1:
+	@echo Doing inter 32-64 bit cleanup
+	$(MAKE) -C $(SRCDIR) distclean
+	touch $@
+	
+
+reallyclean:	distclean
+	@echo Removing any milestone files in build as well...
+	@rm -f build/.??*
+	@echo '(However, leaving src tree intact, with any patches in it)'
+	@echo '(If you want, you could rm -r build, then make extract once more)'
+
+garchive:
+	@echo Call the fetch target instead: it does the same thing.
+
+
+# Downloads the source code to the common ARCHIVEDIR
+fetch:	$(ARCHIVEDIR)/$(ARCHIVENAME)
+	@echo fetch done
+
+$(ARCHIVEDIR)/$(ARCHIVENAME):
+	wget -P $(ARCHIVEDIR) $(MASTER_SITES)/$(ARCHIVENAME)
+
+patch:
+	@echo patch: patching is handled automatically as part of the configure phase.
+
+package:
+	$(CREATEPKG)
+
+
+# split out generic $(ARCH) sections to platform-specific sections if needed.
+# but it usually should not be neccessary
+
+
+$(ARCH)-32 ::   configure-$(ARCH)-32 build-$(ARCH)-32 stage-$(ARCH)-32
+	@echo Completed $(ARCH)-32 configure, build and stage
+	touch build/.$(ARCH)-32.done
+
+
+$(ARCH)-64 ::  stage-$(ARCH)-32 configure-$(ARCH)-64 build-$(ARCH)-64 stage-$(ARCH)-64
+	@echo Completed $@ configure, build and stage
+	touch build/.$(ARCH)-64.done
+
+build/.$(ARCH)-32.done:	$(ARCH)-32
+
+build/.$(ARCH)-64.done:	$(ARCH)-64
+
+# Since we share the same src tree, we should only need to
+# pre-configure patching one time for all.
+build/.patch-preconf.done:	build/.extract.done
+	gpatch -d $(SRCDIR) -p0  <patchfile.preconf
+	libtoolize --copy --force
+	aclocal
+	autoconf --force
+	automake -a -c -f
+	touch $@
+
+
+#Note: I WANT to use LDFLAGS=-R$$ORIGIN, but stupid libtool
+# wont allow it! so force it at build time
+build/.configure-$(ARCH)-32.done:	build/.patch-preconf.done
+	@echo handling $@ target
+	(cd $(SRCDIR) ; \
+	  CFLAGS="$(CFLAGS_COMMON) $(CFLAGS_$(ARCH))" \
+	  CXXFLAGS="$(CXXFLAGS_COMMON)" \
+	  LDFLAGS='-R/opt/csw/lib -L/opt/csw/lib' \
+	  $(CONFIGURE) )
+	if test -f patchfile.postconf ; then \
+	 gpatch -d $(SRCDIR) -p0  <patchfile.postconf ; fi
+	touch $@
+
+build/.configure-$(ARCH)-64.done:	build/.patch-preconf.done
+	if test "$(ARCH)" = "i386" -a "`uname -r`" != "5.10" ; then \
+		echo Error: do 64bit for i386 on sol10 only ; exit 1 ; fi
+	@echo handling $@ target
+	(cd $(SRCDIR) ; \
+	  CFLAGS="$(CFLAGS_COMMON) -m64"  \
+	  CXXFLAGS="$(CXXFLAGS_COMMON) -m64" \
+	  LDFLAGS="$(LDFLAGS_64_$(ARCH))" \
+	  PKG_CONFIG_PATH=/opt/csw/lib/64/pkgconfig \
+	  $(CONFIGURE) $(CONFIGURE_64_EXTRAS)  )
+	if test -f patchfile.postconf ; then \
+	 gpatch -d $(SRCDIR) -p0  <patchfile.postconf ; fi
+	touch $@
+
+
+# See note about LDFLAGS and ORIGIN, higher up	
+build/.build-$(ARCH)-32.done:
+	@echo handling $@ target
+	LD_OPTIONS='-R$$ORIGIN' $(MAKE) -C $(SRCDIR) $(MFLAGS)
+	touch $@
+
+#special handling for 64bit becuase it bombs on the server bit, 
+#but we just dont care about the server bit for 64bit
+build/.build-$(ARCH)-64.done:
+	@echo handling $@ target
+	LD_OPTIONS='-R$$ORIGIN' $(MAKE) -C $(SRCDIR)/libgamin $(MFLAGS)
+	touch $@
+
+## NOTE: This is a DESTRUCTIVE target build!!
+## 'stagepkg'  will destroy any prior "cswstage" directory!!!
+## So, we remove all 64bit milestones
+build/.stage-$(ARCH)-32.done:
+	@echo Handling stage of $(ARCH)-32
+	(cd $(SRCDIR) ; MAKE=gmake stagepkg)
+	cp $(SRCDIR)/COPYING $(SRCDIR)/cswstage
+	@rm -f build/.*64.done
+	touch $@
+
+
+# Note: This is the tricky bit. we cannot just call 
+#  "stagepkg": we dont usually want EVERYTHING; just the
+# libs that are rebuilt in 64bit, and we need them to go to
+# the subdirs, not the regular place!
+# SOME things, will have a clean libdir, and respect --libdir,
+# and do the right thing easily. However, many will not!
+# SO, odds are you'll have to write a customized target
+build/.stage-$(ARCH)-64.done:	$(SRCDIR)/cswstage
+	@echo ""
+	rm -rf cd $(SRCDIR)/tmpdest
+	cd $(SRCDIR)/libgamin &&  $(MAKE) DESTDIR=`/bin/pwd`/tmpdest install
+	(cd $(SRCDIR)/libgamin/tmpdest; tar cf - ./$(LIBDIR64_$(ARCH)) )| \
+	    (cd $(SRCDIR)/cswstage; tar xvf -)
+	@echo ""
+	touch $@
+
+
+
+#These are the easy-to-type targets. They just trigger the real ones.
+configure-$(ARCH)-32:	build/.configure-$(ARCH)-32.done
+	@echo $@ done
+build-$(ARCH)-32:	build/.build-$(ARCH)-32.done
+	@echo $@ done
+stage-$(ARCH)-32:	build/.stage-$(ARCH)-32.done
+	@echo $@ done
+configure-$(ARCH)-64:	build/.configure-$(ARCH)-64.done
+	@echo $@ done
+build-$(ARCH)-64:	build/.build-$(ARCH)-64.done
+	@echo $@ done
+stage-$(ARCH)-64:	build/.stage-$(ARCH)-64.done
+	@echo $@ done
+

Added: csw/mgar/pkg/gamin/trunk/README
===================================================================
--- csw/mgar/pkg/gamin/trunk/README	                        (rev 0)
+++ csw/mgar/pkg/gamin/trunk/README	2010-06-15 03:21:18 UTC (rev 10217)
@@ -0,0 +1,27 @@
+work-in=progress, to replace ancient "fam" demon,e tc, with the more modern
+replacement.
+
+Based on porting work done by sun, at
+
+http://src.opensolaris.org/source/xref/jds/spec-files/trunk/base-specs/gamin.spec
+
+
+
+specific patchfiles are at:
+
+
+http://src.opensolaris.org/source/raw/jds/spec-files/branches/opensolaris-2010-03/patches/gamin-01-all.diff
+
+http://src.opensolaris.org/source/xref/jds/spec-files/branches/opensolaris-2010-03/patches/gamin-02-gamin.diff
+
+(althoug the second one seems kinda useless)
+
+
+Note that you MUST RUN the following non-normal steps, since the sun pages
+tweak what are normally upstream files:
+
+
+ libtoolize --copy --force
+ aclocal
+ autoconf --force
+ automake -a -c -f

Added: csw/mgar/pkg/gamin/trunk/copyright
===================================================================
--- csw/mgar/pkg/gamin/trunk/copyright	                        (rev 0)
+++ csw/mgar/pkg/gamin/trunk/copyright	2010-06-15 03:21:18 UTC (rev 10217)
@@ -0,0 +1,2 @@
+This library is licensed under GNU LGPL.
+Please see /opt/csw/share/doc/gamin/COPYING for full license text

Added: csw/mgar/pkg/gamin/trunk/depend
===================================================================
--- csw/mgar/pkg/gamin/trunk/depend	                        (rev 0)
+++ csw/mgar/pkg/gamin/trunk/depend	2010-06-15 03:21:18 UTC (rev 10217)
@@ -0,0 +1,2 @@
+P	CSWglib2
+P	CSWggettextrt

Added: csw/mgar/pkg/gamin/trunk/patchfile.preconf
===================================================================
--- csw/mgar/pkg/gamin/trunk/patchfile.preconf	                        (rev 0)
+++ csw/mgar/pkg/gamin/trunk/patchfile.preconf	2010-06-15 03:21:18 UTC (rev 10217)
@@ -0,0 +1,3473 @@
+Index: libgamin/gamin_sym.version
+===================================================================
+--- libgamin/gamin_sym.version	(revision 337)
++++ libgamin/gamin_sym.version	(working copy)
+@@ -2,8 +2,6 @@
+    global:
+        FAMCancelMonitor;
+        FAMClose;
+-       FAMDebugLevel;
+-       FAMDebug;
+        FamErrlist;
+        FAMErrno;
+        FAMMonitorCollection;
+Index: libgamin/Makefile.am
+===================================================================
+--- libgamin/Makefile.am	(revision 337)
++++ libgamin/Makefile.am	(working copy)
+@@ -39,13 +39,24 @@
+ 
+ libgamin_1_la_LIBADD =
+ 
++if ON_SOLARIS
++libgamin_1_la_LDFLAGS = -Wl,-M$(srcdir)/gamin_sym.version \
++                        -version-info @GAMIN_VERSION_INFO@ @THREAD_LIBS@
++else
+ libgamin_1_la_LDFLAGS = -Wl,--version-script=$(srcdir)/gamin_sym.version \
+                         -version-info @GAMIN_VERSION_INFO@ @THREAD_LIBS@
++endif
+ 
+ libfam_la_SOURCES = $(libgamin_1_la_SOURCES)
+ libfam_la_LIBADD = $(libgamin_1_la_LIBADD)
+-libfam_la_LDFLAGS = -Wl,--version-script=$(srcdir)/gamin_sym.version	\
++
++if ON_SOLARIS
++libfam_la_LDFLAGS = -Wl,-M$(srcdir)/gamin_sym.version	\
++                    -version-info @FAM_VERSION_INFO@ @THREAD_LIBS@
++else
++libfam_la_LDFLAGS = -Wl,--version-script=$(srcdir)/gamin_sym.version   \
+                     -version-info @FAM_VERSION_INFO@ @THREAD_LIBS@
++endif
+ 
+ #
+ # Compile a program locally to check
+Index: libgamin/fam.h
+===================================================================
+--- libgamin/fam.h	(revision 337)
++++ libgamin/fam.h	(working copy)
+@@ -214,6 +214,30 @@
+ #define FAM_DEBUG_VERBOSE 2
+ 
+ /**
++ * FAMDebugLevel:
++ *
++ * Currently unimplemented as in the SGI FAM.  Exists only for
++ * compatibility.
++ */
++extern int FAMDebugLevel (FAMConnection *fc,
++			  int level);
++/**
++ * FAM_DEBUG_OFF:
++ * Unused macro, compatibility for SGI FAM API.
++ */
++#define FAM_DEBUG_OFF 0
++/**
++ * FAM_DEBUG_ON:
++ * Unused macro, compatibility for SGI FAM API.
++ */
++#define FAM_DEBUG_ON  1
++/**
++ * FAM_DEBUG_VERBOSE:
++ * Unused macro, compatibility for SGI FAM API.
++ */
++#define FAM_DEBUG_VERBOSE 2
++
++/**
+  * FamErrList:
+  *
+  * In case FAMErrno is set, FAMErrlist is a global string array indexed
+Index: libgamin/gam_api.c
+===================================================================
+--- libgamin/gam_api.c	(revision 337)
++++ libgamin/gam_api.c	(working copy)
+@@ -14,6 +14,12 @@
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ #include <sys/uio.h>
++#if defined(sun)
++#include <string.h>
++#endif
++#if defined(HAVE_UCRED_H)
++#include <ucred.h>
++#endif defined(HAVE_UCRED_H)
+ #include "fam.h"
+ #include "gam_protocol.h"
+ #include "gam_data.h"
+@@ -660,6 +666,10 @@
+     } cmsg;
+ #endif
+ 
++#if defined(HAVE_GETPEERUCRED)
++    ucred_t *creds;
++#endif
++
+     s_uid = getuid();
+ 
+ #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+@@ -726,11 +736,25 @@
+                       fd, cr_len, (int) sizeof(cr));
+             goto failed;
+         }
++#elif defined(HAVE_GETPEERUCRED)
++        if ((creds = (ucred_t *)malloc(ucred_size()))==(ucred_t *)NULL){
++            GAM_DEBUG(DEBUG_INFO,"Malloc failed for ucreds");
++            goto failed;
++        }
++
++        if (getpeerucred(fd, &creds)!=0){
++            GAM_DEBUG(DEBUG_INFO,"getpeerucred call failed");
++            goto failed;
++        }
++        c_uid = ucred_getruid(creds);
++        c_gid = ucred_getrgid(creds);
++        c_pid = ucred_getpid(creds);
++        ucred_free(creds);
+ #elif defined(HAVE_CMSGCRED)
+         c_pid = cmsg.cred.cmcred_pid;
+         c_uid = cmsg.cred.cmcred_euid;
+         c_gid = cmsg.cred.cmcred_groups[0];
+-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
++#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEERUCRED */
+         GAM_DEBUG(DEBUG_INFO,
+                   "Socket credentials not supported on this OS\n");
+         goto failed;
+@@ -1340,6 +1364,7 @@
+     gamin_data_lock(conn);
+     if (gamin_data_event_ready(conn)) {
+ 	 gamin_data_unlock(conn);
++         GAM_DEBUG(DEBUG_INFO, "FAMPending()gamin_data_event_ready\n");
+ 	 return (1);
+     }
+ 
+@@ -1347,15 +1372,18 @@
+      * make sure we won't block if reading
+      */
+     ret = gamin_data_available(fc->fd);
++    GAM_DEBUG(DEBUG_INFO, "FAMPending() gamin_data_available ret = %d \n", ret);
+     if (ret < 0)
+         return (-1);
+     if (ret > 0) {
++        GAM_DEBUG(DEBUG_INFO, "FAMPending() ret >0 \n");
+         if (gamin_read_data(conn, fc->fd, 0) < 0) {
+ 	    gamin_try_reconnect(conn, fc->fd);
+ 	}
+     }
+ 
+     ret = (gamin_data_event_ready(conn));
++    GAM_DEBUG(DEBUG_INFO, "FAMPending() gamin_data_event_ready ret = %d \n", ret);
+     gamin_data_unlock(conn);
+ 
+     return ret;
+@@ -1545,4 +1573,20 @@
+ {
+        return(1);
+ }
++
++/**
++ * FAMDebugLevel:
++ * @fc: pointer to a connection structure.
++ * @level: level of debug
++ * 
++ * Entry point installed only for ABI compatibility with SGI FAM,
++ * doesn't do anything.
++ *
++ * Returns 1
++ */
++int
++FAMDebugLevel(FAMConnection *fc, int level)
++{
++       return(1);
++}
+ #endif
+Index: configure.in
+===================================================================
+--- configure.in	(revision 337)
++++ configure.in	(working copy)
+@@ -42,6 +42,12 @@
+ 	CFLAGS=""
+ fi
+ 
++dnl If the user set no CFLAGS, then don't assume the autotools defaults of
++dnl "-g -O2". We set default CFLAGS later based on the --disable-debug flag.
++if test -z "$ENV_CFLAGS"; then
++	CFLAGS=""
++fi
++
+ dnl for the spec file
+ RELDATE=`date +'%a %b %e %Y'`
+ AC_SUBST(RELDATE)
+@@ -279,6 +285,43 @@
+ 	backends="${backends}, kqueue"
+ fi
+ 
++case "$os" in
++    solaris*)
++	AM_CONDITIONAL(ON_SOLARIS, true)
++	AC_COMPILE_IFELSE([
++		#include <port.h>
++		#ifndef PORT_SOURCE_FILE
++		#error "Please upgrade to Nevada 72 or above to suppoert FEN"
++		#endif
++		int main() { return 0; }
++		],[have_fen=1],)
++	if test x$have_fen = x1 ; then
++            AC_ARG_ENABLE(fen,
++		AC_HELP_STRING([--disable-fen], [Disable the FEN backend]),
++                       [fen="${enableval}"], [fen=true])
++
++		if test x$fen = xyes; then
++                       fen=true
++		elif test x$fen = xno; then
++                       fen=false
++		elif test x$fen != xtrue; then
++                       AC_MSG_ERROR(bad value ${enableval} for --disable-fen)
++		fi
++	fi
++	break;
++	;;
++    *)
++	fen=false
++	break;
++	;;
++esac
++
++AM_CONDITIONAL(ENABLE_FEN, test x$fen = xtrue)
++if test x$fen = xtrue; then
++	AC_DEFINE(ENABLE_FEN,1,[Use Solaris FEN as backend])
++	backends="${backends}, FEN"
++fi
++
+ dnl pthread support for reentrance of the client library.
+ AC_ARG_WITH(threads,
+ [  --with-threads          add multithread support(on)])
+@@ -385,6 +428,14 @@
+     AC_DEFINE(HAVE_CMSGCRED,1,[Have cmsgcred structure])
+ fi
+ 
++dnl Check for getpeerucred support - Solaris
++
++AC_CHECK_HEADER(ucred.h,
++    AC_CHECK_LIB(c, getpeerucred,[
++        AC_DEFINE([HAVE_GETPEERUCRED],[],[Define if has getpeerucred])
++        AC_DEFINE([HAVE_UCRED_H],[],[Define if <ucred.h> exists])]))
++
++
+ #### Abstract sockets
+ 
+ AC_MSG_CHECKING(abstract socket namespace)
+@@ -529,6 +580,16 @@
+ AC_SUBST(PYTHON_INCLUDES)
+ AC_SUBST(PYTHON_SITE_PACKAGES)
+ 
++dnl Check for -lsocket -lnsl
++
++AC_CHECK_FUNC(gethostent, , AC_CHECK_LIB(nsl, gethostent))
++AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
++
++dnl Check for <sys/mnttab.h>
++
++AC_CHECK_HEADER(sys/mnttab.h,
++	AC_DEFINE([HAVE_SYS_MNTTAB_H], [], [Define if <sys/mnttab.h> is there]))
++
+ dnl After all config-related tweaking of CFLAGS, set it to its "build" value
+ 
+ AC_MSG_CHECKING(for more compiler warnings)
+Index: tests/testing.c
+===================================================================
+--- tests/testing.c	(revision 337)
++++ tests/testing.c	(working copy)
+@@ -1,3 +1,4 @@
++#include "config.h"
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+@@ -31,6 +32,11 @@
+ 
+ #define IS_BLANK(p) ((*(p) == ' ') || (*(p) == '\t') ||		\
+                      (*(p) == '\n') || (*(p) == '\r'))
++#ifdef ENABLE_FEN
++#define KILLCMD	"pkill"
++#else
++#define KILLCMD	"killall"
++#endif
+ 
+ static int
+ scanCommand(char *line, char **command, char **arg, char **arg2)
+@@ -268,7 +274,7 @@
+          * okay, it's heavy but that's the simplest way since we do not have
+          * the pid(s) of the servers running.
+          */
+-        ret = system("killall gam_server");
++        ret = system(KILLCMD" gam_server");
+         if (ret < 0) {
+             fprintf(stderr, "kill line %d: failed to killall gam_server\n",
+                     no);
+Index: server/gam_server.h
+===================================================================
+--- server/gam_server.h	(revision 337)
++++ server/gam_server.h	(working copy)
+@@ -16,7 +16,8 @@
+ 	GAMIN_K_INOTIFY = 2,
+ 	GAMIN_K_KQUEUE = 3,
+ 	GAMIN_K_MACH = 4,
+-	GAMIN_K_INOTIFY2 = 5
++	GAMIN_K_INOTIFY2 = 5,
++	GAMIN_K_FEN = 6
+ } GamKernelHandler;
+ 
+ typedef enum {
+Index: server/gam_fen.h
+===================================================================
+--- server/gam_fen.h	(revision 0)
++++ server/gam_fen.h	(revision 0)
+@@ -0,0 +1,40 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#ifndef __GAM_FEN_H__
++#define __GAM_FEN_H__
++
++#include <glib.h>
++#include "gam_subscription.h"
++
++G_BEGIN_DECLS
++
++gboolean gam_fen_init (void);
++gboolean gam_fen_add_subscription (GamSubscription *sub);
++gboolean gam_fen_remove_subscription (GamSubscription *sub);
++gboolean gam_fen_remove_all_for (GamListener *listener);
++
++G_END_DECLS
++
++#endif /* __GAM_FEN_H__ */
+Index: server/fen-dump.c
+===================================================================
+--- server/fen-dump.c	(revision 0)
++++ server/fen-dump.c	(revision 0)
+@@ -0,0 +1,100 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include <glib.h>
++#include <glib/gprintf.h>
++#include "fen-node.h"
++#include "fen-data.h"
++#include "fen-kernel.h"
++#include "fen-missing.h"
++#include "fen-dump.h"
++
++G_LOCK_EXTERN (fen_lock);
++
++/*-------------------- node ------------------*/
++static void
++dump_node (node_t* node, gpointer data)
++{
++    if (data && node->user_data) {
++        return;
++    }
++    g_printf ("[%s] < 0x%p : 0x%p > %s\n", __func__, node, node->user_data, NODE_NAME(node));
++}
++
++static gboolean
++dump_node_tree (node_t* node, gpointer user_data)
++{
++    node_op_t op = {dump_node, NULL, NULL, user_data};
++    GList* children;
++    GList* i;
++    if (G_TRYLOCK (fen_lock)) {
++        if (node) {
++            travel_nodes (node, &op);
++        }
++        G_UNLOCK (fen_lock);
++    }
++    return TRUE;
++}
++
++/* ------------------ fdata port hash --------------------*/
++void
++dump_hash_cb (gpointer key,
++  gpointer value,
++  gpointer user_data)
++{
++    g_printf ("[%s] < 0x%p : 0x%p >\n", __func__, key, value);
++}
++
++gboolean
++dump_hash (GHashTable* hash, gpointer user_data)
++{
++    if (G_TRYLOCK (fen_lock)) {
++        if (g_hash_table_size (hash) > 0) {
++            g_hash_table_foreach (hash, dump_hash_cb, user_data);
++        }
++        G_UNLOCK (fen_lock);
++    }
++    return TRUE;
++}
++
++/* ------------------ event --------------------*/
++void
++dump_event (fnode_event_t* ev, gpointer user_data)
++{
++    fdata* data = ev->user_data;
++    g_printf ("[%s] < 0x%p : 0x%p > [ %10s ] %s\n", __func__, ev, ev->user_data, _event_string (ev->e), FN_NAME(data));
++}
++
++void
++dump_event_queue (fdata* data, gpointer user_data)
++{
++    if (G_TRYLOCK (fen_lock)) {
++        if (data->eventq) {
++            g_queue_foreach (data->eventq, (GFunc)dump_event, user_data);
++        }
++        G_UNLOCK (fen_lock);
++    }
++}
++
+Index: server/fen-dump.h
+===================================================================
+--- server/fen-dump.h	(revision 0)
++++ server/fen-dump.h	(revision 0)
+@@ -0,0 +1,29 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#ifndef _FEN_DUMP_H_
++#define _FEN_DUMP_H_
++
++
++#endif /* _FEN_DUMP_H_ */
+Index: server/fen-helper.c
+===================================================================
+--- server/fen-helper.c	(revision 0)
++++ server/fen-helper.c	(revision 0)
+@@ -0,0 +1,336 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include <glib.h>
++#include "fen-data.h"
++#include "fen-helper.h"
++#include "fen-kernel.h"
++#ifdef GIO_COMPILATION
++#include "gfilemonitor.h"
++#else
++#include "gam_event.h"
++#include "gam_server.h"
++#include "gam_protocol.h"
++#endif
++
++#ifdef GIO_COMPILATION
++#define FH_W if (fh_debug_enabled) g_warning
++static gboolean fh_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FH_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_LOCK_EXTERN (fen_lock);
++
++static void default_emit_event_cb (fdata *f, int events);
++static void default_emit_once_event_cb (fdata *f, int events, gpointer sub);
++static int default_event_converter (int event);
++
++static void
++scan_children_init (node_t *f, gpointer sub)
++{
++	GDir *dir;
++	GError *err = NULL;
++    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
++    fdata* pdata;
++    
++    FH_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
++    pdata = node_get_data (f);
++
++    dir = g_dir_open (NODE_NAME(f), 0, &err);
++    if (dir) {
++        const char *basename;
++        
++        while ((basename = g_dir_read_name (dir)))
++        {
++            node_t *childf = NULL;
++            fdata* data;
++            GList *idx;
++
++            childf = children_find (f, basename);
++            if (childf == NULL) {
++                gchar *filename;
++            
++                filename = g_build_filename (NODE_NAME(f), basename, NULL);
++                childf = add_node (f, filename);
++                g_assert (childf);
++                g_free (filename);
++            }
++            if ((data = node_get_data (childf)) == NULL) {
++                data = fdata_new (childf, FALSE);
++            }
++            
++            if (is_monitoring (data)) {
++                /* Ignored */
++            } else if (/* !is_ported (data) && */
++                port_add (&data->fobj, &data->len, data)) {
++                /* Emit created to all other subs */
++                fdata_emit_events (data, FN_EVENT_CREATED);
++            }
++            /* Emit created to the new sub */
++#ifdef GIO_COMPILATION
++            /* fdata_emit_events_once (data, FN_EVENT_CREATED, sub); */
++#else
++            gam_server_emit_one_event (NODE_NAME(childf),
++              gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
++#endif
++        }
++        g_dir_close (dir);
++    } else {
++        FH_W (err->message);
++        g_error_free (err);
++    }
++}
++
++/**
++ * fen_add
++ * 
++ * Won't hold a ref, we have a timout callback to clean unused fdata.
++ * If there is no value for a key, add it and return it; else return the old
++ * one.
++ */
++void
++fen_add (const gchar *filename, gpointer sub, gboolean is_mondir)
++{
++    node_op_t op = {NULL, add_missing_cb, pre_del_cb, (gpointer)filename};
++	node_t* f;
++    fdata* data;
++    
++    g_assert (filename);
++    g_assert (sub);
++
++    G_LOCK (fen_lock);
++	f = find_node_full (filename, &op);
++    FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename);
++    g_assert (f);
++    data = node_get_data (f);
++    if (data == NULL) {
++        data = fdata_new (f, is_mondir);
++    }
++
++    if (is_mondir) {
++        data->mon_dir_num ++;
++    }
++    
++    /* Change to active */
++#ifdef GIO_COMPILATION
++    if (port_add (&data->fobj, &data->len, data) ||
++      g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) {
++        if (is_mondir) {
++            scan_children_init (f, sub);
++        }
++        fdata_sub_add (data, sub);
++    } else {
++        fdata_sub_add (data, sub);
++        fdata_adjust_deleted (data);
++    }
++#else
++    if (port_add (&data->fobj, &data->len, data) ||
++      g_file_test (FN_NAME(data), G_FILE_TEST_EXISTS)) {
++        gam_server_emit_one_event (FN_NAME(data),
++          gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1);
++        if (is_mondir) {
++            scan_children_init (f, sub);
++        }
++        gam_server_emit_one_event (FN_NAME(data),
++          gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
++        fdata_sub_add (data, sub);
++    } else {
++        fdata_sub_add (data, sub);
++        gam_server_emit_one_event (FN_NAME(data),
++          gam_subscription_is_dir (sub), GAMIN_EVENT_DELETED, sub, 1);
++        fdata_adjust_deleted (data);
++        gam_server_emit_one_event (FN_NAME(data),
++          gam_subscription_is_dir (sub), GAMIN_EVENT_ENDEXISTS, sub, 1);
++    }
++#endif
++    G_UNLOCK (fen_lock);
++}
++
++void
++fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir)
++{
++    node_op_t op = {NULL, add_missing_cb, pre_del_cb, (gpointer)filename};
++    node_t* f;
++    fdata* data;
++    
++    g_assert (filename);
++    g_assert (sub);
++
++    G_LOCK (fen_lock);
++	f = find_node (filename);
++    FH_W ("[ %s ] f[0x%p] sub[0x%p] %s\n", __func__, f, sub, filename);
++
++    g_assert (f);
++    data = node_get_data (f);
++    g_assert (data);
++    
++    if (is_mondir) {
++        data->mon_dir_num --;
++    }
++    fdata_sub_remove (data, sub);
++    if (FN_IS_PASSIVE(data)) {
++#ifdef GIO_COMPILATION
++        pending_remove_node (f, &op);
++#else
++        remove_node (f, &op);
++#endif
++    }
++    G_UNLOCK (fen_lock);
++}
++
++static gboolean
++fen_init_once_func (gpointer data)
++{
++    FH_W ("%s\n", __func__);
++    if (!node_class_init ()) {
++        FH_W ("node_class_init failed.");
++        return FALSE;
++    }
++    if (!fdata_class_init (default_emit_event_cb,
++          default_emit_once_event_cb,
++          default_event_converter)) {
++        FH_W ("fdata_class_init failed.");
++        return FALSE;
++    }
++    return TRUE;
++}
++
++gboolean
++fen_init ()
++{
++#ifdef GIO_COMPILATION
++    static GOnce fen_init_once = G_ONCE_INIT;
++    g_once (&fen_init_once, (GThreadFunc)fen_init_once_func, NULL);
++    return (gboolean)fen_init_once.retval;
++#else
++    return fen_init_once_func (NULL);
++#endif
++}
++
++static void
++default_emit_once_event_cb (fdata *f, int events, gpointer sub)
++{
++#ifdef GIO_COMPILATION
++    GFile* child;
++    fen_sub* _sub = (fen_sub*)sub;
++    child = g_file_new_for_path (FN_NAME(f));
++    g_file_monitor_emit_event (G_FILE_MONITOR (_sub->user_data),
++      child, NULL, events);
++    g_object_unref (child);
++#else
++    gam_server_emit_one_event (FN_NAME(f),
++      gam_subscription_is_dir (sub), events, sub, 1);
++#endif
++}
++
++static void
++default_emit_event_cb (fdata *f, int events)
++{
++    GList* i;
++    fdata* pdata;
++    
++#ifdef GIO_COMPILATION
++    GFile* child;
++    child = g_file_new_for_path (FN_NAME(f));
++    for (i = f->subs; i; i = i->next) {
++        fen_sub* sub = (fen_sub*)i->data;
++        gboolean file_is_dir = sub->is_mondir;
++        if ((events != G_FILE_MONITOR_EVENT_CHANGED &&
++              events != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) ||
++          !file_is_dir) {
++            g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
++              child, NULL, events);
++        }
++    }
++    if ((pdata = get_parent_data (f)) != NULL) {
++        for (i = pdata->subs; i; i = i->next) {
++            fen_sub* sub = (fen_sub*)i->data;
++            gboolean file_is_dir = sub->is_mondir;
++            g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
++              child, NULL, events);
++        }
++    }
++    g_object_unref (child);
++#else
++    for (i = f->subs; i; i = i->next) {
++        gboolean file_is_dir = gam_subscription_is_dir (i->data);
++        if (events != GAMIN_EVENT_CHANGED || !file_is_dir) {
++            gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1);
++        }
++    }
++    if ((pdata = get_parent_data (f)) != NULL) {
++        for (i = pdata->subs; i; i = i->next) {
++            gboolean file_is_dir = gam_subscription_is_dir (i->data);
++            gam_server_emit_one_event (FN_NAME(f), file_is_dir, events, i->data, 1);
++        }
++    }
++#endif
++}
++
++static int
++default_event_converter (int event)
++{
++#ifdef GIO_COMPILATION
++    switch (event) {
++    case FN_EVENT_CREATED:
++        return G_FILE_MONITOR_EVENT_CREATED;
++    case FILE_DELETE:
++    case FILE_RENAME_FROM:
++        return G_FILE_MONITOR_EVENT_DELETED;
++    case UNMOUNTED:
++        return G_FILE_MONITOR_EVENT_UNMOUNTED;
++    case FILE_ATTRIB:
++        return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
++    case MOUNTEDOVER:
++    case FILE_MODIFIED:
++    case FILE_RENAME_TO:
++        return G_FILE_MONITOR_EVENT_CHANGED;
++    default:
++        /* case FILE_ACCESS: */
++        g_assert_not_reached ();
++        return -1;
++    }
++#else
++    switch (event) {
++    case FN_EVENT_CREATED:
++        return GAMIN_EVENT_CREATED;
++    case FILE_DELETE:
++    case FILE_RENAME_FROM:
++        return GAMIN_EVENT_DELETED;
++    case FILE_ATTRIB:
++    case MOUNTEDOVER:
++    case UNMOUNTED:
++    case FILE_MODIFIED:
++    case FILE_RENAME_TO:
++        return GAMIN_EVENT_CHANGED;
++    default:
++        /* case FILE_ACCESS: */
++        g_assert_not_reached ();
++        return -1;
++    }
++#endif
++}
+Index: server/fen-helper.h
+===================================================================
+--- server/fen-helper.h	(revision 0)
++++ server/fen-helper.h	(revision 0)
+@@ -0,0 +1,36 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "fen-sub.h"
++
++#ifndef _FEN_HELPER_H_
++#define _FEN_HELPER_H_
++
++void fen_add (const gchar *filename, gpointer sub, gboolean is_mondir);
++void fen_remove (const gchar *filename, gpointer sub, gboolean is_mondir);
++
++/* FEN subsystem initializing */
++gboolean fen_init ();
++
++#endif /* _FEN_HELPER_H_ */
+Index: server/fen-node.c
+===================================================================
+--- server/fen-node.c	(revision 0)
++++ server/fen-node.c	(revision 0)
+@@ -0,0 +1,466 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include <errno.h>
++#include <strings.h>
++#include <glib.h>
++#include "fen-node.h"
++#include "fen-dump.h"
++
++#define	NODE_STAT(n)	(((node_t*)(n))->stat)
++
++struct _dnode {
++    gchar* filename;
++    node_op_t* op;
++    GTimeVal tv;
++};
++
++#ifdef GIO_COMPILATION
++#define FN_W if (fn_debug_enabled) g_warning
++static gboolean fn_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_LOCK_EXTERN (fen_lock);
++#define	PROCESS_DELETING_INTERVAL	900 /* in second */
++
++static node_t* _head = NULL;
++static GList *deleting_nodes = NULL;
++static guint deleting_nodes_id = 0;
++
++static node_t* node_new (node_t* parent, const gchar* basename);
++static void node_delete (node_t* parent);
++static gboolean remove_node_internal (node_t* node, node_op_t* op);
++static void children_add (node_t *p, node_t *f);
++static void children_remove (node_t *p, node_t *f);
++static guint children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data);
++static void children_foreach (node_t *f, GHFunc func, gpointer user_data);
++static gboolean children_remove_cb (gpointer key,
++  gpointer value,
++  gpointer user_data);
++
++static struct _dnode*
++_dnode_new (const gchar* filename, node_op_t* op)
++{
++    struct _dnode* d;
++
++    g_assert (op);
++    if ((d = g_new (struct _dnode, 1)) != NULL) {
++        d->filename = g_strdup (filename);
++        d->op = g_memdup (op, sizeof (node_op_t));
++        g_assert (d->op);
++        g_get_current_time (&d->tv);
++        g_time_val_add (&d->tv, PROCESS_DELETING_INTERVAL);
++    }
++    return d;
++}
++
++static void
++_dnode_free (struct _dnode* d)
++{
++    g_assert (d);
++    g_free (d->filename);
++    g_free (d->op);
++    g_free (d);
++}
++
++static gboolean
++g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
++{
++    if (val1->tv_sec < val2->tv_sec)
++        return TRUE;
++  
++    if (val1->tv_sec > val2->tv_sec)
++        return FALSE;
++  
++    /* val1->tv_sec == val2->tv_sec */
++    if (val1->tv_usec < val2->tv_usec)
++        return TRUE;
++  
++    return FALSE;
++}
++
++static gboolean
++scan_deleting_nodes (gpointer data)
++{
++    struct _dnode* d;
++    GTimeVal tv_now;
++    GList* i;
++    GList* deleted_list = NULL;
++    gboolean ret = TRUE;
++    node_t* node;
++
++    g_get_current_time (&tv_now);
++
++    if (G_TRYLOCK (fen_lock)) {
++        for (i = deleting_nodes; i; i = i->next) {
++            d = (struct _dnode*)i->data;
++            /* Time to free, try only once */
++            if (g_timeval_lt (&d->tv, &tv_now)) {
++                if ((node = find_node (d->filename)) != NULL) {
++                    remove_node_internal (node, d->op);
++                }
++                _dnode_free (d);
++                deleted_list = g_list_prepend (deleted_list, i);
++            }
++        }
++
++        for (i = deleted_list; i; i = i->next) {
++            deleting_nodes = g_list_remove_link (deleting_nodes,
++              (GList *)i->data);
++            g_list_free_1 ((GList *)i->data);
++        }
++        g_list_free (deleted_list);
++
++        if (deleting_nodes == NULL) {
++            deleting_nodes_id = 0;
++            ret = FALSE;
++        }
++        G_UNLOCK (fen_lock);
++    }
++    return ret;
++}
++
++gpointer
++node_get_data (node_t* node)
++{
++    g_assert (node);
++    return node->user_data;
++}
++
++gpointer
++node_set_data (node_t* node, gpointer user_data)
++{
++    gpointer data = node->user_data;
++    g_assert (node);
++    node->user_data = user_data;
++    return data;
++}
++
++void
++travel_nodes (node_t* node, node_op_t* op)
++{
++    GList* children;
++    GList* i;
++
++    if (node) {
++        if (op && op->hit) {
++            op->hit (node, op->user_data);
++        }
++    }
++    children = g_hash_table_get_values (node->children);
++    if (children) {
++        for (i = children; i; i = i->next) {
++            travel_nodes (i->data, op);
++        }
++        g_list_free (children);
++    }
++}
++
++static node_t*
++find_node_internal (node_t* node, const gchar* filename, node_op_t* op)
++{
++    gchar* str;
++    gchar* token;
++    gchar* lasts;
++    node_t* parent;
++    node_t* child;
++    
++    g_assert (filename && filename[0] == '/');
++    g_assert (node);
++    
++    parent = node;
++    str = g_strdup (filename + strlen (NODE_NAME(parent)));
++    
++    if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
++        do {
++            FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
++            child = children_find (parent, token);
++            if (child) {
++                parent = child;
++            } else {
++                if (op && op->add_missing) {
++                    child = op->add_missing (parent, op->user_data);
++                    goto L_hit;
++                }
++                break;
++            }
++        } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
++    } else {
++        /* It's the head */
++        g_assert (parent == _head);
++        child = _head;
++    }
++    
++    if (token == NULL && child) {
++    L_hit:
++        if (op && op->hit) {
++            op->hit (child, op->user_data);
++        }
++    }
++    g_free (str);
++    return child;
++}
++
++node_t*
++find_node (const gchar *filename)
++{
++    return find_node_internal (_head, filename, NULL);
++}
++
++node_t*
++find_node_full (const gchar* filename, node_op_t* op)
++{
++    return find_node_internal (_head, filename, op);
++}
++
++node_t*
++add_node (node_t* parent, const gchar* filename)
++{
++    gchar* str;
++    gchar* token;
++    gchar* lasts;
++    node_t* child = NULL;
++
++    g_assert (_head);
++    g_assert (filename && filename[0] == '/');
++
++    if (parent == NULL) {
++        parent = _head;
++    }
++    
++    str = g_strdup (filename + strlen (NODE_NAME(parent)));
++    
++    if ((token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts)) != NULL) {
++        do {
++            FN_W ("%s %s + %s\n", __func__, NODE_NAME(parent), token);
++            child = node_new (parent, token);
++            if (child) {
++                children_add (parent, child);
++                parent = child;
++            } else {
++                break;
++            }
++        } while ((token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) != NULL);
++    }
++    g_free (str);
++    if (token == NULL) {
++        return child;
++    } else {
++        return NULL;
++    }
++}
++
++/**
++ * delete recursively
++ */
++static gboolean
++remove_children (node_t* node, node_op_t* op)
++{
++    FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node));
++    if (children_num (node) > 0) {
++        children_foreach_remove (node, children_remove_cb,
++          (gpointer)op);
++    }
++    if (children_num (node) == 0) {
++        return TRUE;
++    }
++    return FALSE;
++}
++
++static gboolean
++remove_node_internal (node_t* node, node_op_t* op)
++{
++    node_t* parent = NULL;
++    /*
++     * If the parent is passive and doesn't have children, delete it.
++     * NOTE node_delete_deep is a depth first delete recursively.
++     * Top node is deleted in node_cancel_sub
++     */
++    g_assert (node);
++    g_assert (op && op->pre_del);
++    if (node != _head) {
++        if (remove_children (node, op)) {
++            if (node->user_data) {
++                if (!op->pre_del (node, op->user_data)) {
++                    return FALSE;
++                }
++            }
++            parent = node->parent;
++            children_remove (parent, node);
++            node_delete (node);
++            if (children_num (parent) == 0) {
++                remove_node_internal (parent, op);
++            }
++            return TRUE;
++        }
++        return FALSE;
++    }
++    return TRUE;
++}
++
++void
++pending_remove_node (node_t* node, node_op_t* op)
++{
++    struct _dnode* d;
++    GList* l;
++    
++    for (l = deleting_nodes; l; l=l->next) {
++        d = (struct _dnode*) l->data;
++        if (g_ascii_strcasecmp (d->filename, NODE_NAME(node)) == 0) {
++            return;
++        }
++    }
++    
++    d = _dnode_new (NODE_NAME(node), op);
++    g_assert (d);
++    deleting_nodes = g_list_prepend (deleting_nodes, d);
++    if (deleting_nodes_id == 0) {
++        deleting_nodes_id = g_timeout_add_seconds (PROCESS_DELETING_INTERVAL,
++          scan_deleting_nodes,
++          NULL);
++        g_assert (deleting_nodes_id > 0);
++    }
++}
++
++void
++remove_node (node_t* node, node_op_t* op)
++{
++    remove_node_internal (node, op);
++}
++
++static node_t*
++node_new (node_t* parent, const gchar* basename)
++{
++	node_t *f = NULL;
++
++    g_assert (basename && basename[0]);
++    if ((f = g_new0 (node_t, 1)) != NULL) {
++        if (parent) {
++            f->basename = g_strdup (basename);
++            f->filename = g_build_filename (G_DIR_SEPARATOR_S,
++              NODE_NAME(parent), basename, NULL);
++        } else {
++            f->basename = g_strdup (basename);
++            f->filename = g_strdup (basename);
++        }
++        f->children = g_hash_table_new_full (g_str_hash, g_str_equal,
++          NULL, (GDestroyNotify)node_delete);
++        FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
++    }
++	return f;
++}
++
++static void
++node_delete (node_t *f)
++{
++    FN_W ("[ %s ] 0x%p %s\n", __func__, f, NODE_NAME(f));
++    g_assert (g_hash_table_size (f->children) == 0);
++    g_assert (f->user_data == NULL);
++
++    g_hash_table_unref (f->children);
++    g_free (f->basename);
++    g_free (f->filename);
++    g_free (f);
++}
++
++static void
++children_add (node_t *p, node_t *f)
++{
++    FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
++    g_hash_table_insert (p->children, f->basename, f);
++    f->parent = p;
++}
++
++static void
++children_remove (node_t *p, node_t *f)
++{
++    FN_W ("%s [p] %8s [c] %8s\n", __func__, p->basename, f->basename);
++    g_hash_table_steal (p->children, f->basename);
++    f->parent = NULL;
++}
++
++guint
++children_num (node_t *f)
++{
++    return g_hash_table_size (f->children);
++}
++
++node_t *
++children_find (node_t *f, const gchar *basename)
++{
++    return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename);
++}
++
++/**
++ * depth first delete recursively
++ */
++static gboolean
++children_remove_cb (gpointer key,
++  gpointer value,
++  gpointer user_data)
++{
++    node_t* f = (node_t*)value;
++    node_op_t* op = (node_op_t*) user_data;
++    
++    g_assert (f->parent);
++
++    FN_W ("%s [p] %8s [c] %8s\n", __func__, f->parent->basename, f->basename);
++    if (remove_children (f, op)) {
++        if (f->user_data != NULL) {
++            return op->pre_del (f, op->user_data);
++        }
++        return TRUE;
++    }
++    return FALSE;
++}
++
++static guint
++children_foreach_remove (node_t *f, GHRFunc func, gpointer user_data)
++{
++    g_assert (f);
++    
++    return g_hash_table_foreach_remove (f->children, func, user_data);
++}
++
++static void
++children_foreach (node_t *f, GHFunc func, gpointer user_data)
++{
++    g_assert (f);
++    
++    g_hash_table_foreach (f->children, func, user_data);
++}
++
++gboolean
++node_class_init ()
++{
++    FN_W ("%s\n", __func__);
++    if (_head == NULL) {
++        _head = node_new (NULL, G_DIR_SEPARATOR_S);
++    }
++    return _head != NULL;
++}
+Index: server/gam_channel.c
+===================================================================
+--- server/gam_channel.c	(revision 337)
++++ server/gam_channel.c	(working copy)
+@@ -7,6 +7,12 @@
+ #include <sys/stat.h>
+ #include <sys/un.h>
+ #include <sys/uio.h>
++#if defined(sun)
++#include <string.h>
++#endif 
++#if defined(HAVE_UCRED_H)
++#include <ucred.h>
++#endif defined(HAVE_UCRED_H)
+ #include "gam_error.h"
+ #include "gam_connection.h"
+ #include "gam_channel.h"
+@@ -101,6 +107,10 @@
+     } cmsg;
+ #endif
+ 
++#if defined(HAVE_GETPEERUCRED)
++    ucred_t *creds;
++#endif
++
+     s_uid = getuid();
+ 
+ #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+@@ -167,11 +177,25 @@
+                       fd, cr_len, (int) sizeof(cr));
+             goto failed;
+         }
++#elif defined(HAVE_GETPEERUCRED)
++	if ((creds = (ucred_t *)malloc(ucred_size()))==(ucred_t *)NULL){
++            GAM_DEBUG(DEBUG_INFO,"Malloc failed for ucreds");
++	    goto failed;  
++	}
++
++	if (getpeerucred(fd, &creds)!=0){
++            GAM_DEBUG(DEBUG_INFO,"getpeerucred call failed");
++	    goto failed;
++	}
++	c_uid = ucred_getruid(creds);
++	c_gid = ucred_getrgid(creds);
++	c_pid = ucred_getpid(creds);
++	ucred_free(creds);
+ #elif defined(HAVE_CMSGCRED)
+ 	c_pid = cmsg.cred.cmcred_pid;
+ 	c_uid = cmsg.cred.cmcred_euid;
+ 	c_gid = cmsg.cred.cmcred_groups[0];
+-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
++#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEERUCRED */
+         GAM_DEBUG(DEBUG_INFO,
+                   "Socket credentials not supported on this OS\n");
+         goto failed;
+Index: server/fen-node.h
+===================================================================
+--- server/fen-node.h	(revision 0)
++++ server/fen-node.h	(revision 0)
+@@ -0,0 +1,73 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#ifndef _FEN_NODE_H_
++#define _FEN_NODE_H_
++
++typedef struct node node_t;
++
++struct node
++{
++    gchar *filename;
++    gchar *basename;
++    gint stat;
++    
++	/* the parent and children of node */
++    node_t *parent;
++    GHashTable *children; /* children in basename */
++
++    gpointer user_data;
++};
++
++#define	IS_TOPNODE(fp)	(((node_t *)(fp))->parent == NULL)
++#define NODE_NAME(fp)	(((node_t *)(fp))->filename)
++
++typedef struct node_op
++{
++    /* find */
++    void (*hit) (node_t* node, gpointer user_data);
++    node_t* (*add_missing) (node_t* parent, gpointer user_data);
++    /* delete */
++    gboolean (*pre_del) (node_t* node, gpointer user_data);
++	/* data */
++    gpointer user_data;
++} node_op_t;
++
++node_t* add_node (node_t* parent, const gchar* filename);
++void remove_node (node_t* node, node_op_t* op);
++void pending_remove_node (node_t* node, node_op_t* op);
++
++void travel_nodes (node_t* node, node_op_t* op);
++node_t* find_node_full (const gchar* filename, node_op_t* op);
++node_t* find_node (const gchar *filename);
++
++node_t* children_find (node_t *f, const gchar *basename);
++guint children_num (node_t *f);
++
++gpointer node_get_data (node_t* node);
++gpointer node_set_data (node_t* node, gpointer user_data);
++
++gboolean node_class_init ();
++
++#endif /* _FEN_NODE_H_ */
+Index: server/fen-missing.c
+===================================================================
+--- server/fen-missing.c	(revision 0)
++++ server/fen-missing.c	(revision 0)
+@@ -0,0 +1,121 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include <glib.h>
++#include "fen-data.h"
++#include "fen-missing.h"
++
++G_LOCK_EXTERN (fen_lock);
++#define SCAN_MISSING_INTERVAL 4000	/* in milliseconds */
++
++#ifdef GIO_COMPILATION
++#define FM_W if (fm_debug_enabled) g_warning
++gboolean fm_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FM_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++/* global data structure for scan missing files */
++static GList *missing_list = NULL;
++static guint scan_missing_source_id = 0;
++
++static gboolean scan_missing_list (gpointer data);
++
++static gboolean
++scan_missing_list (gpointer data)
++{
++    GList *existing_list = NULL;
++    GList *idx = NULL;
++    fdata *f;
++    gboolean ret = TRUE;
++
++    G_LOCK (fen_lock);
++    
++    for (idx = missing_list; idx; idx = idx->next) {
++        f = (fdata*)idx->data;
++        
++        if (port_add (&f->fobj, &f->len, f)) {
++            /* TODO - emit CREATE event */
++            fdata_emit_events (f, FN_EVENT_CREATED);
++            existing_list = g_list_prepend (existing_list, idx);
++        }
++    }
++    
++    for (idx = existing_list; idx; idx = idx->next) {
++        missing_list = g_list_remove_link (missing_list, (GList *)idx->data);
++        g_list_free_1 ((GList *)idx->data);
++    }
++    g_list_free (existing_list);
++
++    if (missing_list == NULL) {
++        scan_missing_source_id = 0;
++        ret = FALSE;
++    }
++
++    G_UNLOCK (fen_lock);
++    return ret;
++}
++
++/**
++ * missing_add
++ *
++ * Unsafe, need lock fen_lock.
++ */
++void
++missing_add (fdata *f)
++{
++    GList *idx;
++    
++    g_assert (!is_ported (f));
++
++    if (g_list_find (missing_list, f) != NULL) {
++        FM_W ("%s is ALREADY added %s\n", __func__, FN_NAME(f));
++        return;
++    }
++    FM_W ("%s is added %s\n", __func__, FN_NAME(f));
++    
++    missing_list = g_list_prepend (missing_list, f);
++    
++    /* if doesn't scan, then start */
++    if (scan_missing_source_id == 0) {
++        scan_missing_source_id = g_timeout_add (SCAN_MISSING_INTERVAL,
++          scan_missing_list,
++          NULL);
++        g_assert (scan_missing_source_id > 0);
++    }
++}
++
++/**
++ * missing_remove
++ *
++ * Unsafe, need lock fen_lock.
++ */
++void
++missing_remove (fdata *f)
++{
++    FM_W ("%s %s\n", __func__, FN_NAME(f));
++    missing_list = g_list_remove (missing_list, f);
++}
+Index: server/fen-missing.h
+===================================================================
+--- server/fen-missing.h	(revision 0)
++++ server/fen-missing.h	(revision 0)
+@@ -0,0 +1,38 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#ifndef __GAM_FEN_H__
++#define __GAM_FEN_H__
++
++#include "fen-data.h"
++
++G_BEGIN_DECLS
++
++extern void missing_add (fdata *f);
++extern void missing_remove (fdata *f);
++
++G_END_DECLS
++
++#endif /* __GAM_FEN_H__ */
++
+Index: server/gam_fs.c
+===================================================================
+--- server/gam_fs.c	(revision 337)
++++ server/gam_fs.c	(working copy)
+@@ -7,9 +7,20 @@
+ #include <string.h>
+ #include <errno.h>
+ #include <glib.h>
++#ifdef HAVE_SYS_MNTTAB_H
++#include <sys/mnttab.h>
++#endif
+ #include "gam_error.h"
+ #include "gam_fs.h"
+ 
++#ifdef HAVE_SYS_MNTTAB_H
++#define MTAB	MNTTAB
++#define MTABDEL	"\t"
++#else
++#define MTAB	"/etc/mtab"
++#define MTABDEL	"\t"
++#endif
++
+ #define DEFAULT_POLL_TIMEOUT 0
+ 
+ typedef struct _gam_fs_properties {
+@@ -119,7 +130,7 @@
+ 	gam_fs *fs = NULL;
+ 	int i;
+ 
+-	g_file_get_contents ("/etc/mtab", &contents, &len, NULL);
++	g_file_get_contents (MTAB, &contents, &len, NULL);
+ 	if (contents == NULL)
+ 		return;
+ 
+@@ -133,7 +144,7 @@
+ 			if (line[0] == '\0')
+ 				continue;
+ 
+-			words = g_strsplit (line, " ", 0);
++			words = g_strsplit (line, MTABDEL, 0);
+ 
+ 			if (words == NULL)
+ 				continue;
+@@ -176,19 +187,23 @@
+ 		gam_fs_set ("ext2", GFS_MT_DEFAULT, 0);
+ 		gam_fs_set ("reiser4", GFS_MT_DEFAULT, 0);
+ 		gam_fs_set ("reiserfs", GFS_MT_DEFAULT, 0);
++		gam_fs_set ("nfs", GFS_MT_DEFAULT, 0);
++		gam_fs_set ("zfs", GFS_MT_DEFAULT, 0);
++		gam_fs_set ("ufs", GFS_MT_DEFAULT, 0);
++		gam_fs_set ("vxfs", GFS_MT_DEFAULT, 0);
+ 		gam_fs_set ("novfs", GFS_MT_POLL, 30);
+-		gam_fs_set ("nfs", GFS_MT_POLL, 5);
+-		if (stat("/etc/mtab", &mtab_sbuf) != 0)
++
++		if (stat(MTAB, &mtab_sbuf) != 0)
+ 		{
+-			GAM_DEBUG(DEBUG_INFO, "Could not stat /etc/mtab\n");
++			GAM_DEBUG(DEBUG_INFO, "Could not stat %s\n",MTAB);
+ 		}
+ 		gam_fs_scan_mtab ();
+ 	} else {
+ 		struct stat sbuf;
+ 
+-		if (stat("/etc/mtab", &sbuf) != 0)
++		if (stat(MTAB, &sbuf) != 0)
+ 		{
+-			GAM_DEBUG(DEBUG_INFO, "Could not stat /etc/mtab\n");
++			GAM_DEBUG(DEBUG_INFO, "Could not stat %s\n",MTAB);
+ 		}
+ 
+ 		/* /etc/mtab has changed */
+Index: server/gam_fs.h
+===================================================================
+--- server/gam_fs.h	(revision 337)
++++ server/gam_fs.h	(working copy)
+@@ -8,6 +8,7 @@
+ #if !defined(ENABLE_DNOTIFY) && \
+     !defined(ENABLE_INOTIFY) && \
+     !defined(ENABLE_KQUEUE) && \
++    !defined(ENABLE_FEN) && \
+     !defined(ENABLE_HURD_MACH_NOTIFY)
+ 	GFS_MT_DEFAULT = GFS_MT_POLL,
+ #else
+Index: server/fen-data.c
+===================================================================
+--- server/fen-data.c	(revision 0)
++++ server/fen-data.c	(revision 0)
+@@ -0,0 +1,722 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include <port.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include <sys/stat.h>
++#include <errno.h>
++#include <glib.h>
++#include "fen-data.h"
++#include "fen-kernel.h"
++#include "fen-missing.h"
++#include "fen-dump.h"
++
++#define	PROCESS_EVENTQ_TIME	10	/* in milliseconds */
++#define PAIR_EVENTS_TIMEVAL	00000	/* in microseconds */
++#define	PAIR_EVENTS_INC_TIMEVAL	0000	/* in microseconds */
++#define SCAN_CHANGINGS_TIME	50	/* in milliseconds */
++#define	SCAN_CHANGINGS_MAX_TIME	(4*100)	/* in milliseconds */
++#define	SCAN_CHANGINGS_MIN_TIME	(4*100)	/* in milliseconds */
++#define	INIT_CHANGES_NUM	2
++#define	BASE_NUM	2
++
++#ifdef GIO_COMPILATION
++#define FD_W if (fd_debug_enabled) g_warning
++static gboolean fd_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FD_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_LOCK_EXTERN (fen_lock);
++static GList *deleting_data = NULL;
++static guint deleting_data_id = 0;
++
++static void (*emit_once_cb) (fdata *f, int events, gpointer sub);
++static void (*emit_cb) (fdata *f, int events);
++static int (*_event_converter) (int event);
++
++static gboolean fdata_delete (fdata* f);
++static gint fdata_sub_find (gpointer a, gpointer b);
++static void scan_children (node_t *f);
++static void scan_known_children (node_t* f);
++
++node_t*
++add_missing_cb (node_t* parent, gpointer user_data)
++{
++    g_assert (parent);
++    FD_W ("%s p:0x%p %s\n", __func__, parent, (gchar*)user_data);
++    return add_node (parent, (gchar*)user_data);
++}
++
++gboolean
++pre_del_cb (node_t* node, gpointer user_data)
++{
++    fdata* data;
++    
++    g_assert (node);
++    data = node_get_data (node);
++    FD_W ("%s node:0x%p %s\n", __func__, node, NODE_NAME(node));
++    if (data != NULL) {
++        if (!FN_IS_PASSIVE(data)) {
++            return FALSE;
++        }
++        fdata_delete (data);
++    }
++    return TRUE;
++}
++
++static guint
++_pow (guint x, guint y)
++{
++    guint z = 1;
++    g_assert (x >= 0 && y >= 0);
++    for (; y > 0; y--) {
++        z *= x;
++    }
++    return z;
++}
++
++static guint
++get_scalable_scan_time (fdata* data)
++{
++    guint sleep_time;
++    /* Caculate from num = 0 */
++    sleep_time = _pow (BASE_NUM, data->changed_event_num) * SCAN_CHANGINGS_TIME;
++    if (sleep_time < SCAN_CHANGINGS_MIN_TIME) {
++        sleep_time = SCAN_CHANGINGS_MIN_TIME;
++    } else if (sleep_time > SCAN_CHANGINGS_MAX_TIME) {
++        sleep_time = SCAN_CHANGINGS_MAX_TIME;
++        data->change_update_id = INIT_CHANGES_NUM;
++    }
++    FD_W ("SCALABE SCAN num:time [ %4u : %4u ] %s\n", data->changed_event_num, sleep_time, FN_NAME(data));
++    return sleep_time;
++}
++
++static gboolean
++g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
++{
++    if (val1->tv_sec < val2->tv_sec)
++        return TRUE;
++  
++    if (val1->tv_sec > val2->tv_sec)
++        return FALSE;
++  
++    /* val1->tv_sec == val2->tv_sec */
++    if (val1->tv_usec < val2->tv_usec)
++        return TRUE;
++  
++    return FALSE;
++}
++
++/**
++ * If all active children nodes are ported, then cancel monitor the parent node
++ *
++ * Unsafe, need lock.
++ */
++static void
++scan_known_children (node_t* f)
++{
++	GDir *dir;
++	GError *err = NULL;
++    fdata* pdata;
++    
++    FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
++    pdata = node_get_data (f);
++    /*
++     * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
++     */
++	dir = g_dir_open (NODE_NAME(f), 0, &err);
++	if (dir) {
++		const char *basename;
++        
++		while ((basename = g_dir_read_name (dir)))
++		{
++            node_t* childf = NULL;
++            fdata* data;
++			GList *idx;
++			/*
++             * If the node is existed, and isn't ported, then emit created
++             * event. Ignore others.
++             */
++            childf = children_find (f, basename);
++            if (childf &&
++              (data = node_get_data (childf)) != NULL &&
++              !FN_IS_PASSIVE (data)) {
++                if (!is_monitoring (data) &&
++                  port_add (&data->fobj, &data->len, data)) {
++                    fdata_emit_events (data, FN_EVENT_CREATED);
++                }
++            }
++        }
++		g_dir_close (dir);
++    } else {
++        FD_W (err->message);
++        g_error_free (err);
++    }
++}
++
++static void
++scan_children (node_t *f)
++{
++	GDir *dir;
++	GError *err = NULL;
++    fdata* pdata;
++    
++    FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
++    pdata = node_get_data (f);
++    /*
++     * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
++     */
++	dir = g_dir_open (NODE_NAME(f), 0, &err);
++	if (dir) {
++		const char *basename;
++        
++		while ((basename = g_dir_read_name (dir)))
++		{
++            node_t* childf = NULL;
++            fdata* data;
++			GList *idx;
++
++            childf = children_find (f, basename);
++            if (childf == NULL) {
++                gchar *filename;
++
++                filename = g_build_filename (NODE_NAME(f), basename, NULL);
++                childf = add_node (f, filename);
++                g_assert (childf);
++                data = fdata_new (childf, FALSE);
++                g_free (filename);
++            }
++            if ((data = node_get_data (childf)) == NULL) {
++                data = fdata_new (childf, FALSE);
++            }
++            /* Be sure data isn't ported and add to port successfully */
++            /* Don't need delete it, it will be deleted by the parent */
++            if (is_monitoring (data)) {
++                /* Ignored */
++            } else if (/* !is_ported (data) && */
++                port_add (&data->fobj, &data->len, data)) {
++                fdata_emit_events (data, FN_EVENT_CREATED);
++            }
++        }
++		g_dir_close (dir);
++    } else {
++        FD_W (err->message);
++        g_error_free (err);
++    }
++}
++
++static gboolean
++scan_deleting_data (gpointer data)
++{
++    fdata *f;
++    GList* i;
++    GList* deleted_list = NULL;
++    gboolean ret = TRUE;
++
++    if (G_TRYLOCK (fen_lock)) {
++        for (i = deleting_data; i; i = i->next) {
++            f = (fdata*)i->data;
++            if (fdata_delete (f)) {
++                deleted_list = g_list_prepend (deleted_list, i);
++            }
++        }
++
++        for (i = deleted_list; i; i = i->next) {
++            deleting_data = g_list_remove_link (deleting_data,
++              (GList *)i->data);
++            g_list_free_1 ((GList *)i->data);
++        }
++        g_list_free (deleted_list);
++
++        if (deleting_data == NULL) {
++            deleting_data_id = 0;
++            ret = FALSE;
++        }
++        G_UNLOCK (fen_lock);
++    }
++    return ret;
++}
++
++gboolean
++is_monitoring (fdata* data)
++{
++    return is_ported (data) || data->change_update_id > 0;
++}
++
++fdata*
++get_parent_data (fdata* data)
++{
++    if (FN_NODE(data) && !IS_TOPNODE(FN_NODE(data))) {
++        return node_get_data (FN_NODE(data)->parent);
++    }
++    return NULL;
++}
++
++node_t*
++get_parent_node (fdata* data)
++{
++    if (FN_NODE(data)) {
++        return (FN_NODE(data)->parent);
++    }
++    return NULL;
++}
++
++fdata *
++fdata_new (node_t* node, gboolean is_mondir)
++{
++	fdata *f = NULL;
++
++    g_assert (node);
++	if ((f = g_new0 (fdata, 1)) != NULL) {
++        FN_NODE(f) = node;
++		FN_NAME(f) = g_strdup (NODE_NAME(node));
++        f->is_dir = is_mondir;
++        f->eventq = g_queue_new ();
++        FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
++        node_set_data (node, f);
++	}
++	return f;
++}
++
++static gboolean
++fdata_delete (fdata *f)
++{
++    fnode_event_t *ev;
++
++    FD_W ("[ TRY %s ] 0x%p id[%4d:%4d] %s\n", __func__, f, f->eventq_id, f->change_update_id, FN_NAME(f));
++    g_assert (FN_IS_PASSIVE(f));
++
++    port_remove (f);
++    /* missing_remove (f); */
++
++    if (f->node != NULL) {
++        node_set_data (f->node, NULL);
++        f->node = NULL;
++    }
++
++    if (f->change_update_id > 0 || f->eventq_id > 0) {
++        if (FN_IS_LIVING(f)) {
++            f->is_cancelled = TRUE;
++            deleting_data = g_list_prepend (deleting_data, f);
++            if (deleting_data_id == 0) {
++                deleting_data_id = g_idle_add (scan_deleting_data, NULL);
++                g_assert (deleting_data_id > 0);
++            }
++        }
++        return FALSE;
++    }
++    FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
++
++    while ((ev = g_queue_pop_head (f->eventq)) != NULL) {
++        fnode_event_delete (ev);
++    }
++
++    g_queue_free (f->eventq);
++    g_free (FN_NAME(f));
++    g_free (f);
++    return TRUE;
++}
++
++void
++fdata_reset (fdata* data)
++{
++    fnode_event_t *ev;
++
++    g_assert (data);
++
++    while ((ev = g_queue_pop_head (data->eventq)) != NULL) {
++        fnode_event_delete (ev);
++    }
++}
++
++static gint
++fdata_sub_find (gpointer a, gpointer b)
++{
++    if (a != b) {
++        return 1;
++    } else {
++        return 0;
++    }
++}
++
++void
++fdata_sub_add (fdata *f, gpointer sub)
++{
++    FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
++    g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) == NULL);
++    f->subs = g_list_prepend (f->subs, sub);
++}
++
++void
++fdata_sub_remove (fdata *f, gpointer sub)
++{
++    GList *l;
++    FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
++    g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) != NULL);
++    l = g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find);
++    g_assert (l);
++    g_assert (sub == l->data);
++    f->subs = g_list_delete_link (f->subs, l);
++}
++
++/**
++ * Adjust self on failing to Port
++ */
++void
++fdata_adjust_deleted (fdata* f)
++{
++    node_t* parent;
++    fdata* pdata;
++    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
++
++    /*
++     * It's a top node. We move it to missing list.
++     */
++    parent = get_parent_node (f);
++    pdata = get_parent_data (f);
++    if (!FN_IS_PASSIVE(f) ||
++      children_num (FN_NODE(f)) > 0 ||
++      (pdata && !FN_IS_PASSIVE(pdata))) {
++        if (parent) {
++            if (pdata == NULL) {
++                pdata = fdata_new (parent, FALSE);
++            }
++            g_assert (pdata);
++            if (!port_add (&pdata->fobj, &pdata->len, pdata)) {
++                fdata_adjust_deleted (pdata);
++            }
++        } else {
++            /* f is root */
++            g_assert (IS_TOPNODE(FN_NODE(f)));
++            missing_add (f);
++        }
++    } else {
++#ifdef GIO_COMPILATION
++        pending_remove_node (FN_NODE(f), &op);
++#else
++        remove_node (FN_NODE(f), &op);
++#endif
++    }
++}
++
++static gboolean
++fdata_adjust_changed (fdata *f)
++{
++    fnode_event_t *ev;
++    struct stat buf;
++    node_t* parent;
++    fdata* pdata;
++
++    G_LOCK (fen_lock);
++    parent = get_parent_node (f);
++    pdata = get_parent_data (f);
++
++    if (!FN_IS_LIVING(f) ||
++      (children_num (FN_NODE(f)) == 0 &&
++        FN_IS_PASSIVE(f) &&
++        pdata && FN_IS_PASSIVE(pdata))) {
++        f->change_update_id = 0;
++        G_UNLOCK (fen_lock);
++        return FALSE;
++    }
++
++    FD_W ("[ %s ] %s\n", __func__, FN_NAME(f));
++    if (FN_STAT (FN_NAME(f), &buf) != 0) {
++        FD_W ("LSTAT [%-20s] %s\n", FN_NAME(f), g_strerror (errno));
++        goto L_delete;
++    }
++    f->is_dir = S_ISDIR (buf.st_mode) ? TRUE : FALSE;
++    if (f->len != buf.st_size) {
++        /* FD_W ("LEN [%lld:%lld] %s\n", f->len, buf.st_size, FN_NAME(f)); */
++        f->len = buf.st_size;
++        ev = fnode_event_new (FILE_MODIFIED, TRUE, f);
++        if (ev != NULL) {
++            ev->is_pending = TRUE;
++            fdata_add_event (f, ev);
++        }
++        /* Fdata is still changing, so scalable scan */
++        f->change_update_id = g_timeout_add (get_scalable_scan_time (f),
++          (GSourceFunc)fdata_adjust_changed,
++          (gpointer)f);
++        G_UNLOCK (fen_lock);
++        return FALSE;
++    } else {
++        f->changed_event_num = 0;
++        f->fobj.fo_atime = buf.st_atim;
++        f->fobj.fo_mtime = buf.st_mtim;
++        f->fobj.fo_ctime = buf.st_ctim;
++        if (FN_IS_DIR(f)) {
++            if (FN_IS_MONDIR(f)) {
++                scan_children (FN_NODE(f));
++            } else {
++                scan_known_children (FN_NODE(f));
++                if ((children_num (FN_NODE(f)) == 0 &&
++                      FN_IS_PASSIVE(f) &&
++                      pdata && FN_IS_PASSIVE(pdata))) {
++                    port_remove (f);
++                    goto L_exit;
++                }
++            }
++        }
++        if (!port_add_simple (&f->fobj, f)) {
++        L_delete:
++            ev = fnode_event_new (FILE_DELETE, FALSE, f);
++            if (ev != NULL) {
++                fdata_add_event (f, ev);
++            }
++        }
++    }
++L_exit:
++    f->change_update_id = 0;
++    G_UNLOCK (fen_lock);
++    return FALSE;
++}
++
++void
++fdata_emit_events_once (fdata *f, int event, gpointer sub)
++{
++    emit_once_cb (f, _event_converter (event), sub);
++}
++
++void
++fdata_emit_events (fdata *f, int event)
++{
++    emit_cb (f, _event_converter (event));
++}
++
++static gboolean
++process_events (gpointer udata)
++{
++    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
++    fdata* f;
++    fnode_event_t* ev;
++    int e;
++
++    /* FD_W ("IN <======== %s\n", __func__); */
++
++    f = (fdata*)udata;
++    FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
++    
++    G_LOCK (fen_lock);
++
++    if (!FN_IS_LIVING(f)) {
++        f->eventq_id = 0;
++        G_UNLOCK (fen_lock);
++        return FALSE;
++    }
++    
++    if ((ev = (fnode_event_t*)g_queue_pop_head (f->eventq)) != NULL) {
++        /* Send events to clients. */
++        e = ev->e;
++        if (!ev->is_pending) {
++#ifdef GIO_COMPILATION
++            if (ev->has_twin) {
++                fdata_emit_events (f, FILE_ATTRIB);
++            }
++#endif
++            fdata_emit_events (f, ev->e);
++        }
++        
++        fnode_event_delete (ev);
++        ev = NULL;
++
++        /* Adjust node state. */
++        /*
++         * Node the node has been created, so we can delete create event in
++         * optimizing. To reduce the statings, we add it to Port on discoving
++         * it then emit CREATED event. So we don't need to do anything here.
++         */
++        switch (e) {
++        case FILE_MODIFIED:
++        case MOUNTEDOVER:
++        case UNMOUNTED:
++            /* If the event is a changed event, then pending process it */
++            if (f->change_update_id == 0) {
++                f->change_update_id = g_timeout_add (get_scalable_scan_time(f),
++                  (GSourceFunc)fdata_adjust_changed,
++                  (gpointer)f);
++                g_assert (f->change_update_id > 0);
++            }
++            break;
++        case FILE_ATTRIB:
++            g_assert (f->change_update_id == 0);
++            if (!port_add (&f->fobj, &f->len, f)) {
++                ev = fnode_event_new (FILE_DELETE, FALSE, f);
++                if (ev != NULL) {
++                    fdata_add_event (f, ev);
++                }
++            }
++            break;
++        case FILE_DELETE: /* Ignored */
++            break;
++        default:
++            g_assert_not_reached ();
++            break;
++        }
++        /* Process one event a time */
++        G_UNLOCK (fen_lock); 
++        return TRUE;
++    }
++    f->eventq_id = 0;
++    G_UNLOCK (fen_lock); 
++    /* FD_W ("OUT ========> %s\n", __func__); */
++    return FALSE;
++}
++
++/**
++ * fdata_add_event:
++ *
++ */
++void
++fdata_add_event (fdata *f, fnode_event_t *ev)
++{
++    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
++    fnode_event_t *tail;
++
++    if (!FN_IS_LIVING(f)) {
++        fnode_event_delete (ev);
++        return;
++    }
++    
++    FD_W ("%s %d\n", __func__, ev->e);
++    g_get_current_time (&ev->t);
++    /*
++     * If created/deleted events of child node happened, then we use parent
++     * event queue to handle.
++     * If child node emits deleted event, it seems no changes for the parent
++     * node, but the attr is changed. So we may try to cancel processing the
++     * coming changed events of the parent node.
++     */
++    tail = (fnode_event_t*)g_queue_peek_tail (f->eventq);
++    switch (ev->e) {
++    case FILE_RENAME_FROM:
++    case FILE_RENAME_TO:
++    case FILE_ACCESS:
++        fnode_event_delete (ev);
++        g_assert_not_reached ();
++        return;
++    case FILE_DELETE:
++        /* clear changed event number */
++        f->changed_event_num = 0;
++        /*
++         * We will cancel all previous events.
++         */
++        if (tail) {
++            g_queue_pop_tail (f->eventq);
++            do {
++                fnode_event_delete (tail);
++            } while ((tail = (fnode_event_t*)g_queue_pop_tail (f->eventq)) != NULL);
++        }
++        /*
++         * Given a node "f" is deleted, process it ASAP.
++         */
++        fdata_emit_events (f, ev->e);
++        fnode_event_delete (ev);
++        fdata_adjust_deleted (f);
++        return;
++    case FILE_MODIFIED:
++    case UNMOUNTED:
++    case MOUNTEDOVER:
++        /* clear changed event number */
++        f->changed_event_num ++;
++    case FILE_ATTRIB:
++    default:
++        /*
++         * If in the time range, we will try optimizing
++         * (changed+) to (changed)
++         * (attrchanged changed) to ([changed, attrchanged])
++         * (event attrchanged) to ([event, attrchanged])
++         */
++        if (tail) {
++            do {
++                if (tail->e == ev->e) {
++                    if (g_timeval_lt (&ev->t, &tail->t)) {
++                        g_queue_peek_tail (f->eventq);
++                        /* Add the increment */
++                        g_time_val_add (&ev->t, PAIR_EVENTS_INC_TIMEVAL);
++                        /* skip the previous event */
++                        FD_W ("SKIPPED -- %s\n", _event_string (tail->e));
++                        fnode_event_delete (tail);
++                    } else {
++                        break;
++                    }
++                } else if (ev->e == FILE_MODIFIED && tail->e == FILE_ATTRIB) {
++                    ev->has_twin = TRUE;
++                    fnode_event_delete (tail);
++                } else if (ev->e == FILE_ATTRIB && f->change_update_id > 0) {
++                    tail->has_twin = TRUE;
++                    /* skip the current event */
++                    fnode_event_delete (ev);
++                    return;
++                } else {
++                    break;
++                }
++            } while ((tail = (fnode_event_t*)g_queue_peek_tail (f->eventq)) != NULL);
++        }
++    }
++
++    /* must add the threshold time */
++    g_time_val_add (&ev->t, PAIR_EVENTS_TIMEVAL);
++    
++    g_queue_push_tail (f->eventq, ev);
++
++    /* starting process_events */
++    if (f->eventq_id == 0) {
++        f->eventq_id = g_timeout_add (PROCESS_EVENTQ_TIME,
++          process_events,
++          (gpointer)f);
++        g_assert (f->eventq_id > 0);
++    }
++    FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
++}
++
++gboolean
++fdata_class_init (void (*user_emit_cb) (fdata*, int),
++  void (*user_emit_once_cb) (fdata*, int,  gpointer),
++  int (*user_event_converter) (int event))
++{
++    FD_W ("%s\n", __func__);
++    if (user_emit_cb == NULL) {
++        return FALSE;
++    }
++    if (user_emit_once_cb == NULL) {
++        return FALSE;
++    }
++    if (user_event_converter == NULL) {
++        return FALSE;
++    }
++    emit_cb = user_emit_cb;
++    emit_once_cb = user_emit_once_cb;
++    _event_converter = user_event_converter;
++    
++    if (!port_class_init (fdata_add_event)) {
++        FD_W ("port_class_init failed.");
++        return FALSE;
++    }
++    return TRUE;
++}
+Index: server/fen-data.h
+===================================================================
+--- server/fen-data.h	(revision 0)
++++ server/fen-data.h	(revision 0)
+@@ -0,0 +1,89 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include "fen-node.h"
++#include "fen-kernel.h"
++
++#ifndef _FEN_DATA_H_
++#define _FEN_DATA_H_
++
++#define FN_EVENT_CREATED	0
++#define FN_NAME(fp)	(((fdata*)(fp))->fobj.fo_name)
++#define FN_NODE(fp)	(((fdata*)(fp))->node)
++#define	FN_IS_DIR(fp)	(((fdata*)(fp))->is_dir)
++#define	FN_IS_PASSIVE(fp)	(((fdata*)(fp))->subs == NULL)
++#define	FN_IS_MONDIR(fp)	(((fdata*)(fp))->mon_dir_num > 0)
++#define	FN_IS_LIVING(fp)	(!((fdata*)(fp))->is_cancelled)
++
++typedef struct
++{
++	file_obj_t fobj;
++    off_t len;
++    gboolean is_cancelled;
++
++    node_t* node;
++	/* to identify if the path is dir */
++	gboolean is_dir;
++    guint mon_dir_num;
++
++	/* List of subscriptions monitoring this fdata/path */
++	GList *subs;
++
++    /* prcessed changed events num */
++    guint changed_event_num;
++    
++    /* process events source id */
++    GQueue* eventq;
++    guint eventq_id;
++    guint change_update_id;
++} fdata;
++
++/* fdata functions */
++fdata* fdata_new (node_t* node, gboolean is_mondir);
++void fdata_reset (fdata* data);
++void fdata_emit_events_once (fdata *f, int event, gpointer sub);
++void fdata_emit_events (fdata *f, int event);
++void fdata_add_event (fdata *f, fnode_event_t *ev);
++void fdata_adjust_deleted (fdata *f);
++fdata* get_parent_data (fdata* data);
++node_t* get_parent_node (fdata* data);
++gboolean is_monitoring (fdata* data);
++
++/* sub */
++void fdata_sub_add (fdata *f, gpointer sub);
++void fdata_sub_remove (fdata *f, gpointer sub);
++
++/* misc */
++node_t* add_missing_cb (node_t* parent, gpointer user_data);
++gboolean pre_del_cb (node_t* node, gpointer user_data);
++
++/* init */
++gboolean fdata_class_init (void (*user_emit_cb) (fdata*, int),
++  void (*user_emit_once_cb) (fdata*, int,  gpointer),
++  int (*user_event_converter) (int event));
++
++#endif /* _FEN_DATA_H_ */
+Index: server/fen-sub.c
+===================================================================
+--- server/fen-sub.c	(revision 0)
++++ server/fen-sub.c	(revision 0)
+@@ -0,0 +1,42 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include "fen-sub.h"
++
++fen_sub*
++fen_sub_new (gpointer udata, gboolean is_mondir)
++{
++    fen_sub *sub;
++    sub = g_new (fen_sub, 1);
++    sub->user_data = udata;
++    sub->is_mondir = is_mondir;
++    return sub;
++}
++
++void
++fen_sub_delete (fen_sub *sub)
++{
++    g_free (sub);
++}
+Index: server/fen-sub.h
+===================================================================
+--- server/fen-sub.h	(revision 0)
++++ server/fen-sub.h	(revision 0)
+@@ -0,0 +1,39 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include <glib.h>
++
++#ifndef _FEN_SUB_H_
++#define _FEN_SUB_H_
++
++typedef struct _fen_sub
++{
++    gpointer user_data;
++    gboolean is_mondir;
++} fen_sub;
++
++fen_sub*	fen_sub_new (gpointer udata, gboolean is_mondir);
++void		fen_sub_delete (fen_sub *sub);
++
++#endif _FEN_SUB_H_
+Index: server/fen-kernel.c
+===================================================================
+--- server/fen-kernel.c	(revision 0)
++++ server/fen-kernel.c	(revision 0)
+@@ -0,0 +1,535 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/* 
++ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
++ * Use is subject to license terms.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library 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
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Authors: Lin Ma <lin.ma at sun.com>
++ */
++
++#include "config.h"
++#include <rctl.h>
++#include <strings.h>
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <glib.h>
++#include "fen-kernel.h"
++#include "fen-dump.h"
++
++#ifdef GIO_COMPILATION
++#define FK_W if (fk_debug_enabled) g_warning
++static gboolean fk_debug_enabled = FALSE;
++#else
++#include "gam_error.h"
++#define FK_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
++#endif
++
++G_GNUC_INTERNAL G_LOCK_DEFINE (fen_lock);
++#define PE_ALLOC	64
++#define F_PORT(pfo)		(((_f *)(pfo))->port->port)
++#define F_NAME(pfo)		(((_f *)(pfo))->fobj->fo_name)
++#define FEN_ALL_EVENTS	(FILE_MODIFIED | FILE_ATTRIB | FILE_NOFOLLOW)
++#define FEN_IGNORE_EVENTS	(FILE_ACCESS)
++#define PROCESS_PORT_EVENTS_TIME	400	/* in milliseconds */
++
++static GHashTable *_obj_fen_hash = NULL;	/* <user_data, port> */
++static ulong max_port_events = 512;
++static GList *pn_vq;	/* the queue of ports which don't have the max objs */
++static GList *pn_fq;	/* the queue of ports which have the max objs */
++static GQueue *g_eventq = NULL;
++static void (*add_event_cb) (gpointer, fnode_event_t*);
++
++typedef struct pnode
++{
++	long ref;	/* how many fds are associated to this port */
++	int port;
++    guint port_source_id;
++} pnode_t;
++
++typedef struct {
++    pnode_t*	port;
++    file_obj_t*	fobj;
++
++    gboolean	is_active;
++    gpointer	user_data;
++} _f;
++
++static gboolean port_fetch_event_cb (void *arg);
++static pnode_t *pnode_new ();
++static void pnode_delete (pnode_t *pn);
++
++gboolean
++is_ported (gpointer f)
++{
++    _f* fo = g_hash_table_lookup (_obj_fen_hash, f);
++    
++    if (fo) {
++        return fo->is_active;
++    }
++    return FALSE;
++}
++
++static gchar*
++printevent (const char *pname, int event, const char *tag)
++{
++    static gchar	*event_string = NULL;
++    GString			*str;
++
++    if (event_string) {
++        g_free(event_string);
++    }
++
++    str = g_string_new ("");
++    g_string_printf (str, "[%s] [%-20s]", tag, pname);
++    if (event & FILE_ACCESS) {
++        str = g_string_append (str, " ACCESS");
++    }
++    if (event & FILE_MODIFIED) {
++        str = g_string_append (str, " MODIFIED");
++    }
++    if (event & FILE_ATTRIB) {
++        str = g_string_append (str, " ATTRIB");
++    }
++    if (event & FILE_DELETE) {
++        str = g_string_append (str, " DELETE");
++    }
++    if (event & FILE_RENAME_TO) {
++        str = g_string_append (str, " RENAME_TO");
++    }
++    if (event & FILE_RENAME_FROM) {
++        str = g_string_append (str, " RENAME_FROM");
++    }
++    if (event & UNMOUNTED) {
++        str = g_string_append (str, " UNMOUNTED");
++    }
++    if (event & MOUNTEDOVER) {
++        str = g_string_append (str, " MOUNTEDOVER");
++    }
++    event_string = str->str;
++    g_string_free (str, FALSE);
++    return event_string;
++}
++
++static void
++port_add_kevent (int e, gpointer f)
++{
++    fnode_event_t *ev, *tail;
++    GTimeVal t;
++    gboolean has_twin = FALSE;
++    
++    FK_W("%s\n", printevent(F_NAME(f), e, "org"));
++    /*
++     * Child FILE_DELETE | FILE_RENAME_FROM will trigger parent FILE_MODIFIED.
++     * FILE_MODIFIED will trigger FILE_ATTRIB.
++     */
++
++    if ((e & FILE_ATTRIB) && e != FILE_ATTRIB) {
++        e ^= FILE_ATTRIB;
++        has_twin = TRUE;
++    }
++    if (e == FILE_RENAME_FROM) {
++        e = FILE_DELETE;
++    }
++    if (e == FILE_RENAME_TO) {
++        e = FILE_MODIFIED;
++    }
++    
++    switch (e) {
++    case FILE_DELETE:
++    case FILE_RENAME_FROM:
++    case FILE_MODIFIED:
++    case FILE_ATTRIB:
++    case UNMOUNTED:
++    case MOUNTEDOVER:
++        break;
++    case FILE_RENAME_TO:
++    case FILE_ACCESS:
++    default:
++        g_assert_not_reached ();
++        return;
++    }
++
++    tail = (fnode_event_t*) g_queue_peek_tail (g_eventq);
++    if (tail) {
++        if (tail->user_data == f) {
++            if (tail->e == e) {
++                tail->has_twin = (has_twin | (tail->has_twin ^ has_twin));
++                /* skip the current */
++                return;
++            } else if (e == FILE_MODIFIED && !has_twin
++              && tail->e == FILE_ATTRIB) {
++                tail->e = FILE_MODIFIED;
++                tail->has_twin = TRUE;
++                return;
++            } else if (e == FILE_ATTRIB
++              && tail->e == FILE_MODIFIED && !tail->has_twin) {
++                tail->has_twin = TRUE;
++                return;
++            }
++        }
++    }
++    
++    if ((ev = fnode_event_new (e, has_twin, f)) != NULL) {
++        g_queue_push_tail (g_eventq, ev);
++    }
++}
++
++static void
++port_process_kevents ()
++{
++    fnode_event_t *ev;
++    
++    while ((ev = (fnode_event_t*)g_queue_pop_head (g_eventq)) != NULL) {
++        FK_W ("[%s] 0x%p %s\n", __func__, ev, _event_string (ev->e));
++        add_event_cb (ev->user_data, ev);
++    }
++}
++
++static gboolean
++port_fetch_event_cb (void *arg)
++{
++	pnode_t *pn = (pnode_t *)arg;
++    _f* fo;
++	uint_t nget = 0;
++	port_event_t pe[PE_ALLOC];
++    timespec_t timeout;
++    gpointer f;
++    gboolean ret = TRUE;
++    
++    /* FK_W ("IN <======== %s\n", __func__); */
++    G_LOCK (fen_lock);
++    
++    memset (&timeout, 0, sizeof (timespec_t));
++    do {
++        nget = 1;
++        if (port_getn (pn->port, pe, PE_ALLOC, &nget, &timeout) == 0) {
++            int i;
++            for (i = 0; i < nget; i++) {
++                fo = (_f*)pe[i].portev_user;
++                /* handle event */
++                switch (pe[i].portev_source) {
++                case PORT_SOURCE_FILE:
++                    /* If got FILE_EXCEPTION or add to port failed,
++                       delete the pnode */
++                    fo->is_active = FALSE;
++                    if (fo->user_data) {
++                        FK_W("%s\n",
++                          printevent(F_NAME(fo), pe[i].portev_events, "RAW"));
++                        port_add_kevent (pe[i].portev_events, fo->user_data);
++                    } else {
++                        /* fnode is deleted */
++                        goto L_delete;
++                    }
++                    if (pe[i].portev_events & FILE_EXCEPTION) {
++                        g_hash_table_remove (_obj_fen_hash, fo->user_data);
++                    L_delete:
++                        FK_W ("[ FREE_FO ] [0x%p]\n", fo);
++                        pnode_delete (fo->port);
++                        g_free (fo);
++                    }
++                    break;
++                default:
++                    /* case PORT_SOURCE_TIMER: */
++                    FK_W ("[kernel] unknown portev_source %d\n", pe[i].portev_source);
++                }
++            }
++        } else {
++            FK_W ("[kernel] port_getn %s\n", g_strerror (errno));
++            nget = 0;
++        }
++    } while (nget == PE_ALLOC);
++
++	/* Processing g_eventq */
++    port_process_kevents ();
++    
++    if (pn->ref == 0) {
++        pn->port_source_id = 0;
++        ret = FALSE;
++    }
++    G_UNLOCK (fen_lock);
++    /* FK_W ("OUT ========> %s\n", __func__); */
++	return ret;
++}
++
++/*
++ * ref - 1 if remove a watching file succeeded.
++ */
++static void
++pnode_delete (pnode_t *pn)
++{
++    g_assert (pn->ref <= max_port_events);
++    
++	if (pn->ref == max_port_events) {
++        FK_W ("PORT : move to visible queue - [pn] 0x%p [ref] %d\n", pn, pn->ref);
++		pn_fq = g_list_remove (pn_fq, pn);
++		pn_vq = g_list_prepend (pn_vq, pn);
++	}
++	if ((-- pn->ref) == 0) {
++        /* Should dispatch the source */
++	}
++	FK_W ("%s [pn] 0x%p [ref] %d\n", __func__, pn, pn->ref);
++}
++
++/*
++ * malloc pnode_t and port_create, start thread at pnode_ref.
++ * if pnode_new succeeded, the pnode_t will never
++ * be freed. So pnode_t can be freed only in pnode_new.
++ * Note pnode_monitor_remove_all can also free pnode_t, but currently no one
++ * invork it.
++ */
++static pnode_t *
++pnode_new ()
++{
++	pnode_t *pn = NULL;
++
++	if (pn_vq) {
++		pn = (pnode_t*)pn_vq->data;
++        g_assert (pn->ref < max_port_events);
++	} else {
++		pn = g_new0 (pnode_t, 1);
++		if (pn != NULL) {
++            if ((pn->port = port_create ()) >= 0) {
++                g_assert (g_list_find (pn_vq, pn) == NULL);
++                pn_vq = g_list_prepend (pn_vq, pn);
++            } else {
++                FK_W ("PORT_CREATE %s\n", g_strerror (errno));
++                g_free (pn);
++                pn = NULL;
++			}
++		}
++	}
++	if (pn) {
++		FK_W ("%s [pn] 0x%p [ref] %d\n", __func__, pn, pn->ref);
++        pn->ref++;
++        if (pn->ref == max_port_events) {
++            FK_W ("PORT : move to full queue - [pn] 0x%p [ref] %d\n", pn, pn->ref);
++            pn_vq = g_list_remove (pn_vq, pn);
++            pn_fq = g_list_prepend (pn_fq, pn);
++            g_assert (g_list_find (pn_vq, pn) == NULL);
++        }
++        /* attach the source */
++        if (pn->port_source_id == 0) {
++            pn->port_source_id = g_timeout_add (PROCESS_PORT_EVENTS_TIME,
++              port_fetch_event_cb,
++              (void *)pn);
++            g_assert (pn->port_source_id > 0);
++        }
++	}
++
++	return pn;
++}
++
++/**
++ * port_add_internal
++ *
++ * < private >
++ * Unsafe, need lock fen_lock.
++ */
++static gboolean
++port_add_internal (file_obj_t* fobj, off_t* len,
++  gpointer f, gboolean need_stat)
++{
++    int ret;
++    struct stat buf;
++    _f* fo = NULL;
++
++    g_assert (f && fobj);
++    FK_W ("%s [0x%p] %s\n", __func__, f, fobj->fo_name);
++
++    if ((fo = g_hash_table_lookup (_obj_fen_hash, f)) == NULL) {
++        fo = g_new0 (_f, 1);
++        fo->fobj = fobj;
++        fo->user_data = f;
++        g_assert (fo);
++        FK_W ("[ NEW_FO ] [0x%p] %s\n", fo, F_NAME(fo));
++        g_hash_table_insert (_obj_fen_hash, f, fo);
++    }
++
++    if (fo->is_active) {
++        return TRUE;
++    }
++
++    if (fo->port == NULL) {
++        fo->port = pnode_new ();
++    }
++    
++    if (need_stat) {
++        if (FN_STAT (F_NAME(fo), &buf) != 0) {
++            FK_W ("LSTAT [%-20s] %s\n", F_NAME(fo), g_strerror (errno));
++            goto L_exit;
++        }
++        g_assert (len);
++        fo->fobj->fo_atime = buf.st_atim;
++        fo->fobj->fo_mtime = buf.st_mtim;
++        fo->fobj->fo_ctime = buf.st_ctim;
++        *len = buf.st_size;
++    }
++    
++    if (port_associate (F_PORT(fo),
++          PORT_SOURCE_FILE,
++          (uintptr_t)fo->fobj,
++          FEN_ALL_EVENTS,
++          (void *)fo) == 0) {
++        fo->is_active = TRUE;
++        FK_W ("%s %s\n", "PORT_ASSOCIATE", F_NAME(fo));
++        return TRUE;
++    } else {
++        FK_W ("PORT_ASSOCIATE [%-20s] %s\n", F_NAME(fo), g_strerror (errno));
++    L_exit:
++        FK_W ("[ FREE_FO ] [0x%p]\n", fo);
++        g_hash_table_remove (_obj_fen_hash, f);
++        pnode_delete (fo->port);
++        g_free (fo);
++    }
++    return FALSE;
++}
++
++gboolean
++port_add (file_obj_t* fobj, off_t* len, gpointer f)
++{
++    return port_add_internal (fobj, len, f, TRUE);
++}
++
++gboolean
++port_add_simple (file_obj_t* fobj, gpointer f)
++{
++    return port_add_internal (fobj, NULL, f, FALSE);
++}
++
++/**
++ * port_remove
++ *
++ * < private >
++ * Unsafe, need lock fen_lock.
++ */
++void
++port_remove (gpointer f)
++{
++    _f* fo = NULL;
++
++    FK_W ("%s\n", __func__);
++    if ((fo = g_hash_table_lookup (_obj_fen_hash, f)) != NULL) {
++        /* Marked */
++        fo->user_data = NULL;
++        g_hash_table_remove (_obj_fen_hash, f);
++        
++        if (port_dissociate (F_PORT(fo),
++              PORT_SOURCE_FILE,
++              (uintptr_t)fo->fobj) == 0) {
++            /*
++             * Note, we can run foode_delete if dissociating is failed,
++             * because there may be some pending events (mostly like
++             * FILE_DELETE) in the port_get. If we delete the foode
++             * the fnode may be deleted, then port_get will run on an invalid
++             * address.
++             */
++            FK_W ("[ FREE_FO ] [0x%p]\n", fo);
++            pnode_delete (fo->port);
++            g_free (fo);
++        } else {
++            FK_W ("PORT_DISSOCIATE [%-20s] %s\n", F_NAME(fo), g_strerror (errno));
++        }
++    }
++}
++
++const gchar *
++_event_string (int event)
++{
++    switch (event) {
++    case FILE_DELETE:
++        return "FILE_DELETE";
++    case FILE_RENAME_FROM:
++        return "FILE_RENAME_FROM";

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.


More information about the devel mailing list