[csw-devel] SF.net SVN: gar:[16232] csw/mgar/pkg/gamin/trunk
dmichelsen at users.sourceforge.net
dmichelsen at users.sourceforge.net
Tue Nov 22 15:31:53 CET 2011
Revision: 16232
http://gar.svn.sourceforge.net/gar/?rev=16232&view=rev
Author: dmichelsen
Date: 2011-11-22 14:31:53 +0000 (Tue, 22 Nov 2011)
Log Message:
-----------
gamin: Complete rewrite during port into GAR
Added Paths:
-----------
csw/mgar/pkg/gamin/trunk/Makefile
csw/mgar/pkg/gamin/trunk/checksums
csw/mgar/pkg/gamin/trunk/files/
csw/mgar/pkg/gamin/trunk/files/0001-Patch-from-bugid-107675.patch
csw/mgar/pkg/gamin/trunk/files/0003-Enable-64-bit-builds.patch
Property Changed:
----------------
csw/mgar/pkg/gamin/trunk/
Property changes on: csw/mgar/pkg/gamin/trunk
___________________________________________________________________
Added: svn:ignore
+ work
Added: csw/mgar/pkg/gamin/trunk/Makefile
===================================================================
--- csw/mgar/pkg/gamin/trunk/Makefile (rev 0)
+++ csw/mgar/pkg/gamin/trunk/Makefile 2011-11-22 14:31:53 UTC (rev 16232)
@@ -0,0 +1,84 @@
+NAME = gamin
+VERSION = 0.1.10
+CATEGORIES = server
+
+DESCRIPTION = GNOME replacement for FAM which watches for file alterations
+define BLURB
+endef
+
+MASTER_SITES += http://people.gnome.org/~veillard/gamin/sources/
+DISTFILES = $(DISTNAME).tar.gz
+
+# Solaris Nevada FEN support from
+# https://bugzilla.gnome.org/show_bug.cgi?id=491319
+# The patch has been reformatted
+# PATCHFILES += 0001-Patch-from-bugid-107675.patch
+
+# As taken from
+# http://src.opensolaris.org/source/xref/jds/spec-files/trunk/base-specs/gamin.spec
+
+MASTER_SITES += http://src.opensolaris.org/source/raw/jds/spec-files/branches/opensolaris-2010-03/patches/
+PATCHDIRLEVEL = 0
+PATCHFILES += gamin-01-all.diff
+PATCHFILES += gamin-02-gamin.diff
+
+PATCHFILES += 0003-Enable-64-bit-builds.patch
+
+VENDOR_URL = http://people.gnome.org/~veillard/gamin/
+
+PACKAGES += CSWlibfam0
+SPKG_DESC_CSWlibfam0 = FAM compatibility library from GAMIN, libfam.so.0
+PKGFILES_CSWlibfam0 += $(call pkgfiles_lib,libfam.so.0)
+OBSOLETED_BY_CSWlibfam0 += CSWfam
+
+PACKAGES += CSWlibgamin1-0
+SPKG_DESC_CSWlibgamin1-0 = GNOME replacement for FAM library, libgamin-1.so.0
+PKGFILES_CSWlibgamin1-0 += $(call pkgfiles_lib,libgamin-1.so.0)
+
+PACKAGES += CSWlibgamin-dev
+SPKG_DESC_CSWlibgamin-dev = Development files for libgamin-1.so.0 and legacy compat libfam.so.0
+PKGFILES_CSWlibgamin-dev += $(PKGFILES_DEVEL)
+RUNTIME_DEP_PKGS_CSWlibgamin-dev += CSWlibgamin1-0
+# That is a legacy-only library, no need to pull it in unless for legacy CSWfam
+CHECKPKG_OVERRIDES_CSWlibgamin-dev += missing-dependency|CSWlibfam0
+
+PACKAGES += CSWpy-gamin
+SPKG_DESC_CSWpy-gamin = Python bindings for GAMIN file alteration monitor
+PKGFILES_CSWpy-gamin += $(libdir)/site-packages/.*
+RUNTIME_DEP_PKGS_CSWpy-gamin += CSWpython
+RUNTIME_DEP_PKGS_CSWpy-gamin += CSWlibgamin1-0
+
+PACKAGES += CSWgamin
+SPKG_DESC_CSWgamin = GAMIN file alteration monitor daemon (compatible to FAM)
+# PKGFILES is catchall
+RUNTIME_DEP_PKGS_CSWgamin += CSWlibintl8
+RUNTIME_DEP_PKGS_CSWgamin += CSWglib2
+OBSOLETED_BY_CSWgamin += CSWfam
+
+# For socket, recvmsg, connect
+EXTRA_LINKER_FLAGS = -lsocket -lnsl
+
+BUILD64 = 1
+
+# This is not passed to configure, although it is defined (but wrong) in configure.in
+EXTRA_CONFIGURE_EXPORTS += ENV_CFLAGS
+CONFIGURE_ENV_ENV_CFLAGS = $(CFLAGS)
+
+# No 64 bit Python yet
+CONFIGURE_ARGS-64 += --without-python
+
+CONFIGURE_ARGS += $(DIRPATHS)
+CONFIGURE_ARGS += $(CONFIGURE_ARGS-$(MEMORYMODEL))
+
+PYCOMPILE = 1
+EXTRA_MERGE_EXCLUDE_FILES += .*\.pyo .*\.pyc
+EXTRA_PAX_ARGS += -s ,$(libdir)/python2.6/site-packages,$(libdir)/site-packages,
+
+include gar/category.mk
+
+pre-configure-modulated:
+ cd $(WORKSRC) && libtoolize --copy --force
+ cd $(WORKSRC) && aclocal
+ cd $(WORKSRC) && autoconf --force
+ cd $(WORKSRC) && automake -a -c -f
+ @$(MAKECOOKIE)
Added: csw/mgar/pkg/gamin/trunk/checksums
===================================================================
--- csw/mgar/pkg/gamin/trunk/checksums (rev 0)
+++ csw/mgar/pkg/gamin/trunk/checksums 2011-11-22 14:31:53 UTC (rev 16232)
@@ -0,0 +1,3 @@
+b4ec549e57da470c04edd5ec2876a028 gamin-0.1.10.tar.gz
+7470c29bca30a8e5a8e7db3066cc8ec3 gamin-01-all.diff
+a4904d53e3ba167705966e6334667fab gamin-02-gamin.diff
Added: csw/mgar/pkg/gamin/trunk/files/0001-Patch-from-bugid-107675.patch
===================================================================
--- csw/mgar/pkg/gamin/trunk/files/0001-Patch-from-bugid-107675.patch (rev 0)
+++ csw/mgar/pkg/gamin/trunk/files/0001-Patch-from-bugid-107675.patch 2011-11-22 14:31:53 UTC (rev 16232)
@@ -0,0 +1,3424 @@
+From b817cb4d04a99894af10e38737399802b1677046 Mon Sep 17 00:00:00 2001
+From: Dagobert Michelsen <dam at opencsw.org>
+Date: Tue, 22 Nov 2011 12:44:16 +0100
+Subject: [PATCH] Patch from bugid #107675
+
+---
+ configure.in | 61 +++++
+ libgamin/fam.h | 24 ++
+ libgamin/gam_api.c | 46 +++-
+ server/Makefile.am | 20 ++-
+ server/fen-data.c | 720 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ server/fen-data.h | 88 ++++++
+ server/fen-dump.c | 99 +++++++
+ server/fen-dump.h | 28 ++
+ server/fen-helper.c | 334 +++++++++++++++++++++++
+ server/fen-helper.h | 35 +++
+ server/fen-kernel.c | 527 ++++++++++++++++++++++++++++++++++++
+ server/fen-kernel.h | 53 ++++
+ server/fen-missing.c | 114 ++++++++
+ server/fen-missing.h | 37 +++
+ server/fen-node.c | 460 ++++++++++++++++++++++++++++++++
+ server/fen-node.h | 72 +++++
+ server/fen-sub.c | 41 +++
+ server/fen-sub.h | 38 +++
+ server/gam_channel.c | 26 ++-
+ server/gam_fen.c | 119 +++++++++
+ server/gam_fen.h | 20 ++
+ server/gam_fs.c | 29 ++-
+ server/gam_fs.h | 1 +
+ server/gam_server.c | 13 +
+ server/gam_server.h | 3 +-
+ tests/testing.c | 8 +-
+ 26 files changed, 3004 insertions(+), 12 deletions(-)
+ create mode 100644 server/fen-data.c
+ create mode 100644 server/fen-data.h
+ create mode 100644 server/fen-dump.c
+ create mode 100644 server/fen-dump.h
+ create mode 100644 server/fen-helper.c
+ create mode 100644 server/fen-helper.h
+ create mode 100644 server/fen-kernel.c
+ create mode 100644 server/fen-kernel.h
+ create mode 100644 server/fen-missing.c
+ create mode 100644 server/fen-missing.h
+ create mode 100644 server/fen-node.c
+ create mode 100644 server/fen-node.h
+ create mode 100644 server/fen-sub.c
+ create mode 100644 server/fen-sub.h
+ create mode 100644 server/gam_fen.c
+ create mode 100644 server/gam_fen.h
+
+diff --git a/configure.in b/configure.in
+index 9fd852e..a2604f4 100644
+--- a/configure.in
++++ b/configure.in
+@@ -37,6 +37,12 @@ AC_HEADER_STDC
+ AC_PROG_INSTALL
+ AC_PROG_MAKE_SET
+
++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)
+@@ -248,6 +254,43 @@ if test x$kqueue = xtrue; then
+ 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)])
+@@ -354,6 +397,14 @@ if test x$dbus_have_struct_cmsgcred = xyes; then
+ 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)
+@@ -501,6 +552,16 @@ AC_SUBST(PYTHON_SUBDIR)
+ 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)
+diff --git a/libgamin/fam.h b/libgamin/fam.h
+index b7490da..3e1d562 100644
+--- a/libgamin/fam.h
++++ b/libgamin/fam.h
+@@ -190,6 +190,30 @@ extern int FAMPending (FAMConnection* fc);
+ extern int FAMErrno;
+
+ /**
++ * 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
+diff --git a/libgamin/gam_api.c b/libgamin/gam_api.c
+index cf0fd5b..86d6dcd 100644
+--- a/libgamin/gam_api.c
++++ b/libgamin/gam_api.c
+@@ -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 @@ gamin_check_cred(GAMDataPtr conn, int fd)
+ } cmsg;
+ #endif
+
++#if defined(HAVE_GETPEERUCRED)
++ ucred_t *creds;
++#endif
++
+ s_uid = getuid();
+
+ #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+@@ -726,11 +736,25 @@ retry:
+ 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 @@ FAMPending(FAMConnection * fc)
+ 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 @@ FAMPending(FAMConnection * fc)
+ * 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;
+@@ -1529,4 +1557,20 @@ FAMDebug(FAMConnection *fc, const char *filename, FAMRequest * fr,
+ }
+ return(ret);
+ }
++
++/**
++ * 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
+diff --git a/server/Makefile.am b/server/Makefile.am
+index 30fbe92..f98a335 100644
+--- a/server/Makefile.am
++++ b/server/Makefile.am
+@@ -10,7 +10,7 @@ INCLUDES = \
+ -DG_DISABLE_DEPRECATED
+
+ if GAMIN_DEBUG
+-INCLUDES += -DGAM_DEBUG_ENABLED
++INCLUDES += -DGAM_DEBUG_ENABLED -g
+ endif
+
+
+@@ -69,6 +69,24 @@ if ENABLE_KQUEUE
+ gam_server_SOURCES += gam_kqueue.c gam_kqueue.h
+ endif
+
++if ENABLE_FEN
++gam_server_SOURCES += gam_fen.c gam_fen.h \
++ fen-dump.c \
++ fen-dump.h \
++ fen-kernel.c \
++ fen-kernel.h \
++ fen-missing.c \
++ fen-missing.h \
++ fen-helper.c \
++ fen-helper.h \
++ fen-data.c \
++ fen-data.h \
++ fen-node.c \
++ fen-node.h \
++ fen-sub.c \
++ fen-sub.h
++endif
++
+ if ENABLE_HURD_MACH_NOTIFY
+ gam_server_SOURCES += gam_hurd_mach_notify.c gam_hurd_mach_notify.h
+
+diff --git a/server/fen-data.c b/server/fen-data.c
+new file mode 100644
+index 0000000..9a44b67
+--- /dev/null
++++ b/server/fen-data.c
+@@ -0,0 +1,720 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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
++
++#define FD_W if (fd_debug_enabled) g_warning
++#ifdef GIO_COMPILATION
++static gboolean fd_debug_enabled = FALSE;
++#else
++static gboolean fd_debug_enabled = TRUE;
++#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;
++}
+diff --git a/server/fen-data.h b/server/fen-data.h
+new file mode 100644
+index 0000000..842358b
+--- /dev/null
++++ b/server/fen-data.h
+@@ -0,0 +1,88 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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_ */
+diff --git a/server/fen-dump.c b/server/fen-dump.c
+new file mode 100644
+index 0000000..cd4de28
+--- /dev/null
++++ b/server/fen-dump.c
+@@ -0,0 +1,99 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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);
++ }
++}
++
+diff --git a/server/fen-dump.h b/server/fen-dump.h
+new file mode 100644
+index 0000000..a4b0e81
+--- /dev/null
++++ b/server/fen-dump.h
+@@ -0,0 +1,28 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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_ */
+diff --git a/server/fen-helper.c b/server/fen-helper.c
+new file mode 100644
+index 0000000..6d304ac
+--- /dev/null
++++ b/server/fen-helper.c
+@@ -0,0 +1,334 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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
++
++#define FH_W if (fh_debug_enabled) g_warning
++#ifdef GIO_COMPILATION
++static gboolean fh_debug_enabled = FALSE;
++#else
++static gboolean fh_debug_enabled = TRUE;
++#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
++}
+diff --git a/server/fen-helper.h b/server/fen-helper.h
+new file mode 100644
+index 0000000..75fa0df
+--- /dev/null
++++ b/server/fen-helper.h
+@@ -0,0 +1,35 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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_ */
+diff --git a/server/fen-kernel.c b/server/fen-kernel.c
+new file mode 100644
+index 0000000..d9531da
+--- /dev/null
++++ b/server/fen-kernel.c
+@@ -0,0 +1,527 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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"
++
++#define FK_W if (fk_debug_enabled) g_warning
++#ifdef GIO_COMPILATION
++static gboolean fk_debug_enabled = FALSE;
++#else
++static gboolean fk_debug_enabled = TRUE;
++#endif
++
++G_GNUC_INTERNAL G_LOCK_DEFINE (fen_lock);
++#define PE_ALLOC 64
++#define F_PORT(pp) (((_f *)(fo))->port->port)
++#define F_NAME(pp) (((_f *)(fo))->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 void
++printevent (const char *pname, int event, const char *tag)
++{
++ GString* str;
++ 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");
++ }
++
++ FK_W ("%s\n", str->str);
++ g_string_free (str, TRUE);
++}
++
++static void
++port_add_kevent (int e, gpointer f)
++{
++ fnode_event_t *ev, *tail;
++ GTimeVal t;
++ gboolean has_twin = FALSE;
++
++ /* 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) {
++ 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";
++ case FILE_MODIFIED:
++ return "FILE_MODIFIED";
++ case FILE_RENAME_TO:
++ return "FILE_RENAME_TO";
++ case MOUNTEDOVER:
++ return "MOUNTEDOVER";
++ case FILE_ATTRIB:
++ return "FILE_ATTRIB";
++ case UNMOUNTED:
++ return "UNMOUNTED";
++ case FILE_ACCESS:
++ return "FILE_ACCESS";
++ default:
++ return "EVENT_UNKNOWN";
++ }
++}
++
++/**
++ * Get Solaris resouce values.
++ *
++ */
++
++extern gboolean
++port_class_init (void (*user_add_event) (gpointer, fnode_event_t*))
++{
++ rctlblk_t *rblk;
++ FK_W ("%s\n", __func__);
++ if ((rblk = malloc (rctlblk_size ())) == NULL) {
++ FK_W ("[kernel] rblk malloc %s\n", g_strerror (errno));
++ return FALSE;
++ }
++ if (getrctl ("process.max-port-events", NULL, rblk, RCTL_FIRST) == -1) {
++ FK_W ("[kernel] getrctl %s\n", g_strerror (errno));
++ free (rblk);
++ return FALSE;
++ } else {
++ max_port_events = rctlblk_get_value(rblk);
++ FK_W ("[kernel] max_port_events = %u\n", max_port_events);
++ free (rblk);
++ }
++ if ((_obj_fen_hash = g_hash_table_new(g_direct_hash,
++ g_direct_equal)) == NULL) {
++ FK_W ("[kernel] fobj hash initializing faild\n");
++ return FALSE;
++ }
++ if ((g_eventq = g_queue_new ()) == NULL) {
++ FK_W ("[kernel] FEN global event queue initializing faild\n");
++ }
++ if (user_add_event == NULL) {
++ return FALSE;
++ }
++ add_event_cb = user_add_event;
++ return TRUE;
++}
++
++fnode_event_t*
++fnode_event_new (int event, gboolean has_twin, gpointer user_data)
++{
++ fnode_event_t *ev;
++
++ if ((ev = g_new (fnode_event_t, 1)) != NULL) {
++ g_assert (ev);
++ ev->e = event;
++ ev->user_data = user_data;
++ ev->has_twin = has_twin;
++ /* Default isn't a pending event. */
++ ev->is_pending = FALSE;
++ }
++ return ev;
++}
++
++void
++fnode_event_delete (fnode_event_t* ev)
++{
++ g_free (ev);
++}
+diff --git a/server/fen-kernel.h b/server/fen-kernel.h
+new file mode 100644
+index 0000000..cf3ae42
+--- /dev/null
++++ b/server/fen-kernel.h
+@@ -0,0 +1,53 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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 <port.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#ifndef _FEN_KERNEL_H_
++#define _FEN_KERNEL_H_
++
++#define FN_STAT lstat
++
++typedef struct fnode_event
++{
++ int e;
++ gboolean has_twin;
++ gboolean is_pending;
++ gpointer user_data;
++ GTimeVal t;
++} fnode_event_t;
++
++gboolean port_add (file_obj_t* fobj, off_t* len, gpointer f);
++gboolean port_add_simple (file_obj_t* fobj, gpointer f);
++void port_remove (gpointer f);
++gboolean is_ported (gpointer f);
++
++fnode_event_t* fnode_event_new (int event, gboolean has_twin, gpointer user_data);
++void fnode_event_delete (fnode_event_t* ev);
++const gchar * _event_string (int event);
++
++extern gboolean port_class_init ();
++
++#endif /* _FEN_KERNEL_H_ */
+diff --git a/server/fen-missing.c b/server/fen-missing.c
+new file mode 100644
+index 0000000..84662c2
+--- /dev/null
++++ b/server/fen-missing.c
+@@ -0,0 +1,114 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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 */
++#define FM_W if (fm_debug_enabled) g_warning
++
++/* global data structure for scan missing files */
++gboolean fm_debug_enabled = TRUE;
++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);
++}
+diff --git a/server/fen-missing.h b/server/fen-missing.h
+new file mode 100644
+index 0000000..89e4389
+--- /dev/null
++++ b/server/fen-missing.h
+@@ -0,0 +1,37 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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__ */
++
+diff --git a/server/fen-node.c b/server/fen-node.c
+new file mode 100644
+index 0000000..7285c72
+--- /dev/null
++++ b/server/fen-node.c
+@@ -0,0 +1,460 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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;
++};
++
++#define FN_W if (fn_debug_enabled) g_warning
++static gboolean fn_debug_enabled = FALSE;
++
++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;
++}
+diff --git a/server/fen-node.h b/server/fen-node.h
+new file mode 100644
+index 0000000..7c16762
+--- /dev/null
++++ b/server/fen-node.h
+@@ -0,0 +1,72 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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_ */
+diff --git a/server/fen-sub.c b/server/fen-sub.c
+new file mode 100644
+index 0000000..373cba5
+--- /dev/null
++++ b/server/fen-sub.c
+@@ -0,0 +1,41 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Copyright (C) 2008 Sun Microsystem.
++ *
++ * 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);
++}
+diff --git a/server/fen-sub.h b/server/fen-sub.h
+new file mode 100644
+index 0000000..1f4bb5e
+--- /dev/null
++++ b/server/fen-sub.h
+@@ -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 Microsystem.
++ *
++ * 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_
+diff --git a/server/gam_channel.c b/server/gam_channel.c
+index 56c3ea7..6db1692 100644
+--- a/server/gam_channel.c
++++ b/server/gam_channel.c
+@@ -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 @@ gam_client_conn_check_cred(GIOChannel * source, int fd,
+ } cmsg;
+ #endif
+
++#if defined(HAVE_GETPEERUCRED)
++ ucred_t *creds;
++#endif
++
+ s_uid = getuid();
+
+ #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+@@ -167,11 +177,25 @@ gam_client_conn_check_cred(GIOChannel * source, int fd,
+ 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;
+diff --git a/server/gam_fen.c b/server/gam_fen.c
+new file mode 100644
+index 0000000..303f191
+--- /dev/null
++++ b/server/gam_fen.c
+@@ -0,0 +1,119 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++/*
++ * Design:
++ * A Solaris port has a resource limit of events (port_max_events) which
++ * limits the number of objects (fds) that can be actively associated objects
++ * whith the port. The default is (65536), but can be changed.
++ *
++ * project.max-port-ids identify the max number of ports
++ * process.max-port-events identify the max objs of a port
++ * process.max-file-descriptor identify the max fds of a process
++ *
++ * For a user server process, process.max-file-descriptor seems a bottleneck.
++ * I will use a port list for monitor fds to avoid process.max-file-descriptor
++ * is greater than process.max-port-events.
++ */
++#include "config.h"
++#include "gam_error.h"
++#include "gam_fen.h"
++#include "gam_event.h"
++#include "gam_server.h"
++#include "gam_protocol.h"
++#include <glib.h>
++#include "fen-helper.h"
++
++/**
++ * Initializes the FEN system. This must be called before
++ * any other functions in this module.
++ *
++ * @returns TRUE if initialization succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_init (void)
++{
++ if (!fen_init ())
++ return FALSE;
++
++ gam_server_install_kernel_hooks (GAMIN_K_FEN,
++ gam_fen_add_subscription,
++ gam_fen_remove_subscription,
++ gam_fen_remove_all_for,
++ NULL, NULL);
++ return TRUE;
++}
++
++/**
++ * Adds a subscription to be monitored.
++ *
++ * @param sub a #GamSubscription to be polled
++ * @returns TRUE if adding the subscription succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_add_subscription (GamSubscription *sub)
++{
++ g_debug ("[ %s ] sub[0x%p]\n", __func__, sub);
++
++ gam_listener_add_subscription (gam_subscription_get_listener (sub), sub);
++ fen_add (gam_subscription_get_path(sub),
++ sub,
++ gam_subscription_is_dir (sub));
++ return TRUE;
++}
++
++/**
++ * Removes a subscription which was being monitored.
++ *
++ * @param sub a #GamSubscription to remove
++ * @returns TRUE if removing the subscription succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_remove_subscription (GamSubscription *sub)
++{
++ g_debug ("[ %s ] sub[0x%p]\n", __func__, sub);
++
++ fen_remove (gam_subscription_get_path(sub),
++ sub,
++ gam_subscription_is_dir (sub));
++ /* free subscription */
++ gam_subscription_cancel(sub);
++ gam_subscription_free(sub);
++ return TRUE;
++}
++
++/**
++ * Stop monitoring all subscriptions for a given listener.
++ *
++ * @param listener a #GamListener
++ * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
++ */
++
++gboolean
++gam_fen_remove_all_for (GamListener *listener)
++{
++ GList *subs;
++ GList *idx;
++ gboolean success = TRUE;
++
++ subs = gam_listener_get_subscriptions (listener);
++
++ if (subs == NULL)
++ return FALSE;
++
++ for (idx = subs; idx != NULL; idx = idx->next) {
++ GamSubscription *sub = (GamSubscription *)idx->data;
++ g_assert (sub);
++ if (!gam_fen_remove_subscription (sub))
++ success = FALSE;
++ }
++
++ if (subs) {
++ g_list_free (subs);
++ return TRUE;
++ } else {
++ return FALSE;
++ }
++}
+diff --git a/server/gam_fen.h b/server/gam_fen.h
+new file mode 100644
+index 0000000..07bba39
+--- /dev/null
++++ b/server/gam_fen.h
+@@ -0,0 +1,20 @@
++/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* vim:set expandtab ts=4 shiftwidth=4: */
++
++#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__ */
++
+diff --git a/server/gam_fs.c b/server/gam_fs.c
+index dd414b5..052e4d9 100644
+--- a/server/gam_fs.c
++++ b/server/gam_fs.c
+@@ -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_scan_mtab (void)
+ 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 @@ gam_fs_scan_mtab (void)
+ 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_init (void)
+ 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 */
+diff --git a/server/gam_fs.h b/server/gam_fs.h
+index bc2d538..94e70fd 100644
+--- a/server/gam_fs.h
++++ b/server/gam_fs.h
+@@ -8,6 +8,7 @@ typedef enum {
+ #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
+diff --git a/server/gam_server.c b/server/gam_server.c
+index f92a691..e5da29f 100644
+--- a/server/gam_server.c
++++ b/server/gam_server.c
+@@ -45,6 +45,9 @@
+ #ifdef ENABLE_HURD_MACH_NOTIFY
+ #include "gam_hurd_mach_notify.h"
+ #endif
++#ifdef ENABLE_FEN
++#include "gam_fen.h"
++#endif
+ #include "gam_excludes.h"
+ #include "gam_fs.h"
+ #include "gam_conf.h"
+@@ -162,6 +165,12 @@ gam_init_subscriptions(void)
+ return(TRUE);
+ }
+ #endif
++#ifdef ENABLE_FEN
++ if (gam_fen_init()) {
++ GAM_DEBUG(DEBUG_INFO, "Using fen as backend\n");
++ return(TRUE);
++ }
++#endif
+ }
+
+ if (gam_poll_basic_init()) {
+@@ -627,6 +636,10 @@ main(int argc, const char *argv[])
+ signal(SIGQUIT, gam_exit);
+ signal(SIGTERM, gam_exit);
+ signal(SIGPIPE, SIG_IGN);
++#ifdef ENABLE_FEN
++ signal(SIGUSR1, SIG_IGN);
++ signal(SIGUSR2, SIG_IGN);
++#endif
+
+ if (!gam_init_subscriptions()) {
+ GAM_DEBUG(DEBUG_INFO, "Could not initialize the subscription system.\n");
+diff --git a/server/gam_server.h b/server/gam_server.h
+index bc99e09..313dd84 100644
+--- a/server/gam_server.h
++++ b/server/gam_server.h
+@@ -16,7 +16,8 @@ typedef enum {
+ 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 {
+diff --git a/tests/testing.c b/tests/testing.c
+index 9926c0a..4c08740 100644
@@ 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