[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